From 430000e58f295ff9d341379ca0776536eeeee4d4 Mon Sep 17 00:00:00 2001 From: Diogo Mota <38500808+xdimota7x@users.noreply.github.com> Date: Tue, 28 Apr 2026 14:23:47 +0100 Subject: [PATCH 01/94] chore: remove md2 (#4898) * chore: remove md2 support * docs: remove md2 references from docs * chore: update example to use md3 only, removing md2 references * chore: remove Typography/v2 and version references from types * chore: remove subtitle and subtitleStyle from AppbarContent * chore: remove getColor from BottomNavigation --- docs/docs/guides/01-getting-started.md | 4 - docs/docs/guides/02-theming.mdx | 110 +----- docs/docs/guides/04-fonts.md | 95 +---- .../08-theming-with-react-navigation.md | 64 +-- docs/docs/guides/10-migration-guide-to-5.0.md | 47 +-- docs/static/llms.txt | 4 +- example/src/DrawerItems.tsx | 67 ++-- example/src/ExampleList.tsx | 6 +- .../src/Examples/ActivityIndicatorExample.tsx | 12 +- .../AnimatedFABExample/AnimatedFABExample.tsx | 40 +- .../Examples/AnimatedFABExample/CustomFAB.tsx | 5 +- .../AnimatedFABExample/CustomFABControls.tsx | 14 +- example/src/Examples/AppbarExample.tsx | 147 +++---- example/src/Examples/AvatarExample.tsx | 12 +- example/src/Examples/BadgeExample.tsx | 11 +- example/src/Examples/BannerExample.tsx | 33 +- .../src/Examples/BottomNavigationExample.tsx | 18 +- example/src/Examples/ButtonExample.tsx | 214 +++++----- example/src/Examples/CardExample.tsx | 61 ++- example/src/Examples/CheckboxExample.tsx | 27 +- example/src/Examples/CheckboxItemExample.tsx | 17 +- example/src/Examples/ChipExample.tsx | 159 ++++---- example/src/Examples/DialogExample.tsx | 28 +- .../Examples/Dialogs/DialogTextComponent.tsx | 26 +- .../Dialogs/DialogWithCustomColors.tsx | 26 +- .../DialogWithDismissableBackButton.tsx | 4 +- .../Dialogs/DialogWithLoadingIndicator.tsx | 6 +- .../Examples/Dialogs/UndismissableDialog.tsx | 4 +- example/src/Examples/FABExample.tsx | 88 ++--- example/src/Examples/IconButtonExample.tsx | 31 +- example/src/Examples/ListSectionExample.tsx | 4 +- example/src/Examples/MenuExample.tsx | 27 +- example/src/Examples/ProgressBarExample.tsx | 8 +- example/src/Examples/RadioButtonExample.tsx | 17 +- .../src/Examples/RadioButtonGroupExample.tsx | 11 +- .../src/Examples/RadioButtonItemExample.tsx | 19 +- example/src/Examples/SearchbarExample.tsx | 248 ++++++------ example/src/Examples/SnackbarExample.tsx | 22 +- example/src/Examples/SurfaceExample.tsx | 16 +- example/src/Examples/SwitchExample.tsx | 37 +- example/src/Examples/TextExample.tsx | 122 +++--- example/src/Examples/TextInputExample.tsx | 29 +- example/src/PreferencesContext.tsx | 5 +- example/src/hooks/useExampleTheme.ts | 4 +- example/src/index.native.tsx | 23 +- example/src/index.tsx | 26 +- .../__fixtures__/rewrite-imports/code.js | 1 - .../__fixtures__/rewrite-imports/output.js | 1 - src/components/ActivityIndicator.tsx | 4 +- src/components/Appbar/Appbar.tsx | 50 +-- src/components/Appbar/AppbarAction.tsx | 12 +- src/components/Appbar/AppbarContent.tsx | 59 +-- src/components/Appbar/AppbarHeader.tsx | 12 +- src/components/Appbar/utils.ts | 39 +- src/components/Badge.tsx | 17 +- src/components/Banner.tsx | 17 +- .../BottomNavigation/BottomNavigation.tsx | 13 +- .../BottomNavigation/BottomNavigationBar.tsx | 210 ++-------- src/components/BottomNavigation/utils.ts | 45 +-- src/components/Button/Button.tsx | 51 +-- src/components/Button/utils.tsx | 119 ++---- src/components/Card/Card.tsx | 25 +- src/components/Card/CardActions.tsx | 13 +- src/components/Card/CardTitle.tsx | 14 +- src/components/Card/utils.tsx | 62 +-- src/components/Checkbox/CheckboxItem.tsx | 16 +- src/components/Checkbox/utils.ts | 42 +- src/components/Chip/Chip.tsx | 63 ++- src/components/Chip/helpers.tsx | 178 +++------ src/components/DataTable/DataTableHeader.tsx | 10 +- .../DataTable/DataTablePagination.tsx | 9 +- src/components/DataTable/DataTableRow.tsx | 10 +- src/components/DataTable/DataTableTitle.tsx | 2 +- src/components/Dialog/Dialog.tsx | 32 +- src/components/Dialog/DialogActions.tsx | 17 +- src/components/Dialog/DialogIcon.tsx | 8 +- src/components/Dialog/DialogScrollArea.tsx | 17 +- src/components/Dialog/DialogTitle.tsx | 19 +- src/components/Divider.tsx | 20 +- src/components/Drawer/DrawerCollapsedItem.tsx | 7 +- src/components/Drawer/DrawerItem.tsx | 30 +- src/components/Drawer/DrawerSection.tsx | 18 +- src/components/FAB/AnimatedFAB.tsx | 39 +- src/components/FAB/FAB.tsx | 16 +- src/components/FAB/FABGroup.tsx | 28 +- src/components/FAB/utils.ts | 151 ++----- src/components/HelperText/utils.ts | 18 +- src/components/Icon.tsx | 3 +- src/components/IconButton/IconButton.tsx | 11 +- src/components/IconButton/utils.ts | 115 +++--- src/components/List/ListAccordion.tsx | 58 +-- src/components/List/ListIcon.tsx | 12 +- src/components/List/ListImage.tsx | 15 +- src/components/List/ListItem.tsx | 38 +- src/components/List/ListSubheader.tsx | 7 +- src/components/List/utils.ts | 55 +-- src/components/Menu/Menu.tsx | 10 +- src/components/Menu/MenuItem.tsx | 39 +- src/components/Menu/utils.ts | 55 +-- src/components/ProgressBar.tsx | 6 +- .../RadioButton/RadioButtonItem.tsx | 16 +- src/components/Searchbar.tsx | 66 ++-- .../SegmentedButtons/SegmentedButtonItem.tsx | 13 +- src/components/SegmentedButtons/utils.ts | 64 +-- src/components/Snackbar.tsx | 22 +- src/components/Surface.tsx | 39 +- src/components/Switch/utils.ts | 11 +- src/components/TextInput/Addons/Outline.tsx | 6 +- src/components/TextInput/Addons/Underline.tsx | 14 +- .../Adornment/TextInputAdornment.tsx | 9 +- .../TextInput/Adornment/TextInputAffix.tsx | 2 +- .../TextInput/Adornment/TextInputIcon.tsx | 5 +- src/components/TextInput/Adornment/utils.ts | 19 +- src/components/TextInput/Label/InputLabel.tsx | 3 +- src/components/TextInput/TextInputFlat.tsx | 9 +- .../TextInput/TextInputOutlined.tsx | 14 +- src/components/TextInput/constants.tsx | 9 - src/components/TextInput/helpers.tsx | 146 ++----- src/components/TextInput/types.tsx | 1 - src/components/ToggleButton/ToggleButton.tsx | 10 +- src/components/ToggleButton/utils.ts | 14 +- src/components/Tooltip/Tooltip.tsx | 4 +- src/components/TouchableRipple/utils.ts | 18 +- src/components/Typography/AnimatedText.tsx | 6 +- src/components/Typography/Text.tsx | 11 +- src/components/Typography/v2/Caption.tsx | 50 --- src/components/Typography/v2/Headline.tsx | 53 --- src/components/Typography/v2/Paragraph.tsx | 50 --- src/components/Typography/v2/StyledText.tsx | 56 --- src/components/Typography/v2/Subheading.tsx | 51 --- src/components/Typography/v2/Text.tsx | 62 --- src/components/Typography/v2/Title.tsx | 50 --- src/components/Typography/v2/index.ts | 5 - .../__tests__/Appbar/Appbar.test.tsx | 130 +----- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 1 - src/components/__tests__/Banner.test.tsx | 2 +- .../__tests__/BottomNavigation.test.tsx | 73 ++-- src/components/__tests__/Button.test.tsx | 215 +--------- src/components/__tests__/Card/Card.test.tsx | 71 ---- .../__snapshots__/CheckboxItem.test.tsx.snap | 4 - .../__tests__/Checkbox/utils.test.tsx | 128 ------ src/components/__tests__/Chip.test.tsx | 372 ------------------ src/components/__tests__/FAB.test.tsx | 77 ---- src/components/__tests__/FABGroup.test.tsx | 43 -- src/components/__tests__/IconButton.test.tsx | 33 -- .../__tests__/ListAccordion.test.tsx | 66 ---- src/components/__tests__/ListUtils.test.tsx | 43 +- src/components/__tests__/Menu.test.tsx | 2 +- src/components/__tests__/MenuItem.test.tsx | 81 ---- .../RadioButtonItem.test.tsx.snap | 4 - .../__tests__/SegmentedButton.test.tsx | 83 +--- src/components/__tests__/Switch.test.tsx | 35 +- src/components/__tests__/TextInput.test.tsx | 255 +----------- .../__tests__/ToggleButton.test.tsx | 12 - .../__tests__/Typography/Caption.test.tsx | 28 -- .../__snapshots__/Caption.test.tsx.snap | 74 ---- .../__snapshots__/Banner.test.tsx.snap | 148 ++++--- .../__snapshots__/Button.test.tsx.snap | 13 - .../__snapshots__/DataTable.test.tsx.snap | 1 - .../__snapshots__/DrawerItem.test.tsx.snap | 24 +- .../__snapshots__/ListSection.test.tsx.snap | 6 - .../__snapshots__/Menu.test.tsx.snap | 11 - .../__snapshots__/MenuItem.test.tsx.snap | 14 - .../__snapshots__/Snackbar.test.tsx.snap | 1 - src/core/PaperProvider.tsx | 25 +- src/core/__tests__/PaperProvider.test.tsx | 31 +- src/core/theming.tsx | 34 +- src/index.tsx | 15 - .../views/MaterialBottomTabView.tsx | 1 - src/styles/__tests__/fonts.test.js | 71 +--- src/styles/fonts.tsx | 88 +---- src/styles/overlay.tsx | 4 +- src/styles/shadow.tsx | 65 +-- src/styles/themes/index.ts | 2 - src/styles/themes/v2/DarkTheme.tsx | 30 -- src/styles/themes/v2/LightTheme.tsx | 30 -- src/styles/themes/v3/DarkTheme.tsx | 2 - src/styles/themes/v3/LightTheme.tsx | 2 - src/types.tsx | 26 +- 179 files changed, 1533 insertions(+), 5787 deletions(-) delete mode 100644 src/components/Typography/v2/Caption.tsx delete mode 100644 src/components/Typography/v2/Headline.tsx delete mode 100644 src/components/Typography/v2/Paragraph.tsx delete mode 100644 src/components/Typography/v2/StyledText.tsx delete mode 100644 src/components/Typography/v2/Subheading.tsx delete mode 100644 src/components/Typography/v2/Text.tsx delete mode 100644 src/components/Typography/v2/Title.tsx delete mode 100644 src/components/Typography/v2/index.ts delete mode 100644 src/components/__tests__/Typography/Caption.test.tsx delete mode 100644 src/components/__tests__/Typography/__snapshots__/Caption.test.tsx.snap delete mode 100644 src/styles/themes/v2/DarkTheme.tsx delete mode 100644 src/styles/themes/v2/LightTheme.tsx diff --git a/docs/docs/guides/01-getting-started.md b/docs/docs/guides/01-getting-started.md index 68071c4d25..19fc3c2dea 100644 --- a/docs/docs/guides/01-getting-started.md +++ b/docs/docs/guides/01-getting-started.md @@ -165,7 +165,3 @@ export default function Main() { ); } ``` - -:::note -For MD2 check the following [Material Design 2 default theme](https://github.com/callstack/react-native-paper/blob/main/src/styles/themes/v2/LightTheme.tsx). -::: diff --git a/docs/docs/guides/02-theming.mdx b/docs/docs/guides/02-theming.mdx index 76869182ae..beeb8bfaff 100644 --- a/docs/docs/guides/02-theming.mdx +++ b/docs/docs/guides/02-theming.mdx @@ -65,9 +65,7 @@ You can change the theme prop dynamically and all the components will automatica A theme usually contains the following properties: - `dark` (`boolean`): whether this is a dark theme or light theme. -- `version`: specify which design system components should follow in the app - - 3 - new Material You (MD3) - - 2 - previous Material Design (MD2) +- `version`: Material You (MD3); kept for compatibility and normalized to `3` by `PaperProvider` - `mode` (`'adaptive' | 'exact'`): color mode for dark theme (See [Dark Theme](#dark-theme)). - `roundness` (`number`): roundness of common elements, such as buttons. - `colors` (`object`): various colors used throughout different elements. @@ -471,111 +469,13 @@ export default function HomeScreen() { } ``` -## Material Design 2 +## Material Design 3 -Using Material Design 2 is fully supported in React Native Paper v5.x. +React Native Paper ships with Material Design 3 (Material You) only. Customize the default experience by extending `MD3LightTheme` or `MD3DarkTheme` (see [Extending the theme](#extending-the-theme) and [Advanced theme overrides](#advanced-theme-overrides)). -### Simple setup +### Migrating from Material Design 2 -In order to use the Material Design 2 theme you can just pass `{ version: 2 }` to the PaperProvider theme prop: - -```js -import * as React from 'react'; -import { PaperProvider } from 'react-native-paper'; -import App from './src/App'; - -export default function Main() { - return ( - - - - ); -} -``` - -Specifying `{ version: 2 }` tells React Native Paper to use the built in Material Design 2 theme, so you don't have to fully extend it on your own. - -### Advanced setup - -As with any theme, you can also specify your custom properties within the Material Design 2 theme: - -```js -import * as React from 'react'; -import { MD2LightTheme, PaperProvider } from 'react-native-paper'; -import App from './src/App'; - -export default function Main() { - const theme = { - ...MD2LightTheme, - - // Specify a custom property - custom: 'property', - - // Specify a custom nested property - colors: { - ...MD2LightTheme.colors, - primary: '#fefefe', - }, - }; - - return ( - - - - ); -} -``` - -### Typescript - -Due to the amount of changes in the theme's schema shape it falls into the [Advanced theme overrides](#advanced-theme-overrides) category. The steps are identical as with any advanced theme, just make sure to extend the built-in `MD2LightTheme` or `MD2DarkTheme` instead of `MD3LightTheme` or `MD3DarkTheme`. - -The final example for Material Design 2 would look like this: - -```ts -import * as React from 'react'; -import { MD2LightTheme, PaperProvider, useTheme } from 'react-native-paper'; -import App from './src/App'; - -const theme = { - // Extend Material Design 2 theme - - ...MD2LightTheme, // or MD2DarkTheme - - // Specify a custom property - myOwnProperty: true, - - // Specify a custom nested property - colors: { - ...MD2LightTheme.colors, - myOwnColor: '#BADA55', - }, -}; - -export type AppTheme = typeof theme; - -export const useAppTheme = () => useTheme(); - -export default function Main() { - return ( - - - - ); -} - -// App.tsx - -export default function App() { - const { theme } = useAppTheme(); - - return ; -} -``` - -### Migrating to Material You - -If you are migrating from Material Design 2 (4.x and lower) to Material You (5.x), please refer to our [Migration Guide](https://callstack.github.io/react-native-paper/docs/guides/migration-guide-to-5.0) +If you are upgrading from Material Design 2 (4.x and lower), follow the [Migration Guide](https://callstack.github.io/react-native-paper/docs/guides/migration-guide-to-5.0). ## Applying a theme to a paper component diff --git a/docs/docs/guides/04-fonts.md b/docs/docs/guides/04-fonts.md index 60d3c6e457..5e8e435c0a 100644 --- a/docs/docs/guides/04-fonts.md +++ b/docs/docs/guides/04-fonts.md @@ -47,99 +47,8 @@ Now, you are able to use `fontFamily` from font files. ## Configuring fonts in ThemeProvider -### Material Design 2 - -#### Using `configureFonts` helper - -To create a custom font, prepare a `fontConfig` object where fonts are divided by platforms. After that, you have to: - -* pass the `fontConfig` into `configureFonts` params object property called `config` -* set the params object property `isV3` to `false`. - -The `fontConfig` object accepts `ios`, `android`, `macos`, `windows`, `web`, and `native`. Use these to override fonts on particular platforms. - -:::info -At a minimum, you need to explicitly pass fonts for `android`, `ios`, and `web`. -::: - -```js -import * as React from 'react'; -import { configureFonts, MD2LightTheme, PaperProvider } from 'react-native-paper'; -import App from './src/App'; - -const fontConfig = { - web: { - regular: { - fontFamily: 'sans-serif', - fontWeight: 'normal', - }, - medium: { - fontFamily: 'sans-serif-medium', - fontWeight: 'normal', - }, - light: { - fontFamily: 'sans-serif-light', - fontWeight: 'normal', - }, - thin: { - fontFamily: 'sans-serif-thin', - fontWeight: 'normal', - }, - }, - ios: { - regular: { - fontFamily: 'sans-serif', - fontWeight: 'normal', - }, - medium: { - fontFamily: 'sans-serif-medium', - fontWeight: 'normal', - }, - light: { - fontFamily: 'sans-serif-light', - fontWeight: 'normal', - }, - thin: { - fontFamily: 'sans-serif-thin', - fontWeight: 'normal', - }, - }, - android: { - regular: { - fontFamily: 'sans-serif', - fontWeight: 'normal', - }, - medium: { - fontFamily: 'sans-serif-medium', - fontWeight: 'normal', - }, - light: { - fontFamily: 'sans-serif-light', - fontWeight: 'normal', - }, - thin: { - fontFamily: 'sans-serif-thin', - fontWeight: 'normal', - }, - } -}; - -const theme = { - ...MD2LightTheme, - fonts: configureFonts({config: fontConfig, isV3: false}), -}; - -export default function Main() { - return ( - - - - ); -} -``` - -:::tip -If you're using TypeScript use `as const` when defining `fontConfig`. +:::note +Older Material Design 2 platform-split font configuration (`configureFonts` with per-platform `ios` / `android` / `web` keys) is no longer supported. Use the Material Design 3 typescale and `configureFonts` as documented below. ::: ### Material Design 3 diff --git a/docs/docs/guides/08-theming-with-react-navigation.md b/docs/docs/guides/08-theming-with-react-navigation.md index 6003953c02..272b0c08cd 100644 --- a/docs/docs/guides/08-theming-with-react-navigation.md +++ b/docs/docs/guides/08-theming-with-react-navigation.md @@ -11,25 +11,9 @@ But how to make them work together? ## Themes adaptation -### Material Design 2 - -Fortunately, in Material Design 2, both React Navigation and React Native Paper offer very similar API when it comes to theming and theme color structure. It's possible to import them in light and dark variants from both. - -```js -import { - DarkTheme as NavigationDarkTheme, - DefaultTheme as NavigationDefaultTheme, -} from '@react-navigation/native'; - -import { - MD2LightTheme, - MD2DarkTheme, -} from 'react-native-paper'; -``` - ### Material Design 3 -From v5, React Native Paper theme colors structure follows the Material Design 3 (known as Material You) colors system, which differs significantly from both the previous Paper's theme and React Navigation theme. +React Native Paper follows the Material Design 3 (Material You) color system, which differs from React Navigation’s default theme shape. However, to simplify adapting React Navigation theme colors, to use the ones from React Native Paper, it's worth using a utility called `adaptNavigationTheme` – it accepts navigation-compliant themes in both modes and returns their equivalents adjusted to Material Design 3. @@ -67,8 +51,7 @@ import { createStackNavigator } from '@react-navigation/stack'; import { TouchableOpacity } from 'react-native'; import { Card, - Title, - Paragraph, + Text, List, PaperProvider, } from 'react-native-paper'; @@ -86,8 +69,8 @@ const HomeScreen = ({ navigation }) => ( > - {title} - {content} + {title} + {content} @@ -127,24 +110,6 @@ To make things easier we can use [deepmerge](https://www.npmjs.com/package/deepm npm install deepmerge ``` -### Material Design 2 - -```js -import { - NavigationContainer, - DarkTheme as NavigationDarkTheme, - DefaultTheme as NavigationDefaultTheme, -} from '@react-navigation/native'; -import { - MD2DarkTheme, - MD2LightTheme, -} from 'react-native-paper'; -import merge from 'deepmerge'; - -const CombinedDefaultTheme = merge(MD2LightTheme, NavigationDefaultTheme); -const CombinedDarkTheme = merge(MD2DarkTheme, NavigationDarkTheme); -``` - ### Material Design 3 ```js @@ -171,27 +136,6 @@ const CombinedDarkTheme = merge(MD3DarkTheme, DarkTheme); Alternatively, we could merge those themes using vanilla JavaScript: -### Material Design 2 - -```js -const CombinedDefaultTheme = { - ...MD2LightTheme, - ...NavigationDefaultTheme, - colors: { - ...MD2LightTheme.colors, - ...NavigationDefaultTheme.colors, - }, -}; -const CombinedDarkTheme = { - ...MD2DarkTheme, - ...NavigationDarkTheme, - colors: { - ...MD2DarkTheme.colors, - ...NavigationDarkTheme.colors, - }, -}; -``` - ### Material Design 3 ```js diff --git a/docs/docs/guides/10-migration-guide-to-5.0.md b/docs/docs/guides/10-migration-guide-to-5.0.md index 6c358d387f..178900d2e3 100644 --- a/docs/docs/guides/10-migration-guide-to-5.0.md +++ b/docs/docs/guides/10-migration-guide-to-5.0.md @@ -4,7 +4,7 @@ title: Introducing v5 with Material You React Native Paper v5 is all about adopting the new Material Design 3 aka Material You. It was released in October 2021 after intense work and effort to make Material You follow a more expressive approach to design. -Paper now supports both Material Design 2 and 3 through the configuration described in [Versioning](#versioning) and is compatible with a handful of API changes. +Current releases use Material Design 3 only; the historical `version: 2` / MD2 theme APIs described below were removed after v5. Use this guide when upgrading older apps to MD3. # Migration guide to Material You 5.0 @@ -36,19 +36,11 @@ npx pod-install ### Versioning -Introducing Material You (MD3) into `react-native-paper` doesn't mean dropping previous Material Design (MD2)! On the contrary, both of them will be supported, however, not simultaneously. To specify which design system components should follow in the app, there is a newly created property in [the theme](https://callstack.github.io/react-native-paper/docs/guides/theming#theme-properties) named `version` that accepts one of two values: +In v5, Material You (MD3) became the default theme. Earlier releases also exposed `version: 2` for Material Design 2; that option and MD2 theme exports have since been removed—use MD3 themes (`MD3LightTheme` / `MD3DarkTheme`) only. -* 3 – (default) new Material You (MD3), -* 2 - previous Material Design (MD2). +The theme object no longer includes `version` or `isV3`. Material Design 3 is the only supported mode; use `MD3LightTheme` / `MD3DarkTheme` (or a custom object matching the `MD3Theme` shape) without setting a version flag. -```js -theme: { - /* ... */ - version: 3 | 2 -} -``` - -Read more about using Material Design 2 in our [Material Design 2 theming guide](https://callstack.github.io/react-native-paper/docs/guides/theming#material-design-2) +Read more about theming in the [theming guide](https://callstack.github.io/react-native-paper/docs/guides/theming). ### Colors @@ -161,40 +153,21 @@ Take a look at the suggested replacement diff: + Caption ``` +:::caution +The standalone components `Caption`, `Headline`, `Paragraph`, `Subheading`, and `Title` are no longer exported from `react-native-paper`. Use `` as in the diff above. +::: 👉 You can find more about typography on the [Material You website](https://m3.material.io/styles/typography/overview) ### Configure fonts -The existing utility called `configureFonts` was adjusted to help users configure their theme fonts in both version, that's why that function, as of today, is going to accept the object with the follwing properties as an argument: +The `configureFonts` helper configures the Material Design 3 typescale. Pass overrides via the `config` object (see [Fonts](https://callstack.github.io/react-native-paper/docs/guides/fonts.html)). ```ts -configureFonts(params) +configureFonts({ config: { bodyLarge: { fontFamily: 'System' } } }) ``` -Parameters: - -| NAME | TYPE | REQUIRED | -| ----------- | ----------- | ----------- | -| params | object | No | - -Valid `params` keys are: - - * `config` ([MD2FontsConfig](https://github.com/callstack/react-native-paper/blob/main/src/styles/fonts.tsx#L63) | [MD3FontsConfig](https://github.com/callstack/react-native-paper/blob/main/src/styles/fonts.tsx#L67)) - fonts config object appropriate to the MD version - * `isV3` (boolean) - whether adjusting theme fonts for v3. Default it true. - -To use your current font config from v2 and migrate to v3 there are two requirements: -* the font config previously passed directly into function has to be passed into the params object property called `config` -* the params object property `isV3` has to be set to `false` - -```diff -- configureFonts(fontConfig) -+ configureFonts({config: fontConfig, isV3: false}) -``` - -:::tip -If you want to check how to use `configureFonts` on MD3, check the [Fonts](https://callstack.github.io/react-native-paper/docs/guides/fonts.html) guide. -::: +Older direct calls like `configureFonts(fontConfig)` and MD2 platform-split configs should be replaced with MD3 variant keys (`displayLarge`, `bodyMedium`, and so on). ## Components diff --git a/docs/static/llms.txt b/docs/static/llms.txt index 46da789d41..4029ace3ea 100644 --- a/docs/static/llms.txt +++ b/docs/static/llms.txt @@ -7,7 +7,7 @@ - Install: npm install react-native-paper react-native-safe-area-context - Wrap your app with PaperProvider -- Default theme is Material You (MD3); set theme.version: 2 for MD2 +- Default theme is Material You (MD3) - Requires React Native 0.72+ and React 18+ ## Guides @@ -63,7 +63,7 @@ - Theme object accepts colors, dark, roundness, fonts, animation, and version properties - Use useTheme() hook or ThemeProvider for nested theme overrides -- MD3 (Material You) is the default; set version: 2 for MD2 compatibility +- MD3 (Material You) is the only supported design system; extend MD3LightTheme / MD3DarkTheme - See theming guide: https://callstack.github.io/react-native-paper/docs/guides/theming ## Provider Pattern diff --git a/example/src/DrawerItems.tsx b/example/src/DrawerItems.tsx index 2d3ca6c202..c4a37a43f3 100644 --- a/example/src/DrawerItems.tsx +++ b/example/src/DrawerItems.tsx @@ -9,7 +9,6 @@ import { Button, Dialog, Drawer, - MD2Colors, MD3Colors, Switch, Text, @@ -96,7 +95,7 @@ function DrawerItems() { const _setDrawerItem = (index: number) => setDrawerItemIndex(index); - const { isV3, colors } = useExampleTheme(); + const { colors } = useExampleTheme(); const isIOS = Platform.OS === 'ios'; const expoGoExecution = Constants.executionEnvironment === ExecutionEnvironment.StoreClient; @@ -107,7 +106,6 @@ function DrawerItems() { toggleShouldUseDeviceColors, toggleTheme, toggleRtl: toggleRTL, - toggleThemeVersion, toggleCollapsed, toggleCustomFont, toggleRippleEffect, @@ -137,14 +135,10 @@ function DrawerItems() { }; const coloredLabelTheme = { - colors: isV3 - ? { - secondaryContainer: MD3Colors.tertiary80, - onSecondaryContainer: MD3Colors.tertiary20, - } - : { - primary: MD2Colors.tealA200, - }, + colors: { + secondaryContainer: MD3Colors.tertiary80, + onSecondaryContainer: MD3Colors.tertiary20, + }, }; return ( @@ -157,7 +151,7 @@ function DrawerItems() { }, ]} > - {isV3 && collapsed && ( + {collapsed && ( {DrawerCollapsedItemsData.map((props, index) => ( - {deviceColorsSupported && isV3 ? ( + {deviceColorsSupported ? ( - + Use device colors * @@ -198,7 +192,7 @@ function DrawerItems() { ) : null} - + Dark Theme @@ -208,7 +202,7 @@ function DrawerItems() { {!isWeb && ( - + RTL @@ -217,39 +211,26 @@ function DrawerItems() { )} - - - MD 2 + + + Collapsed drawer * - + - {isV3 && ( - - - Collapsed drawer * - - - - - - )} - - {isV3 && ( - - - Custom font * - - - + + + Custom font * + + - - )} + + - + {isIOS ? 'Highlight' : 'Ripple'} effect * @@ -259,9 +240,9 @@ function DrawerItems() { - {isV3 && !collapsed && ( + {!collapsed && ( - * - available only for MD3 + * - optional example toggles )} diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index 016f085caa..af6ed7534c 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -132,16 +132,12 @@ const data = Object.keys(mainExamples).map( export default function ExampleList({ navigation }: Props) { const keyExtractor = (item: { id: string }) => item.id; - const { colors, isV3 } = useExampleTheme(); + const { colors } = useExampleTheme(); const safeArea = useSafeAreaInsets(); const renderItem = ({ item }: { item: Item }) => { const { data, id } = item; - if (!isV3 && data.title === mainExamples.themingWithReactNavigation.title) { - return null; - } - return ( { const [animating, setAnimating] = React.useState(true); - const { isV3 } = useExampleTheme(); return ( @@ -49,7 +41,7 @@ const ActivityIndicatorExample = () => { diff --git a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx index 4bffa05322..f9b2303fad 100644 --- a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx +++ b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx @@ -3,13 +3,7 @@ import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'; import { Animated, FlatList, Platform, StyleSheet, View } from 'react-native'; import Icon from '@expo/vector-icons/MaterialCommunityIcons'; -import { - Avatar, - MD2Colors, - MD3Colors, - Paragraph, - Text, -} from 'react-native-paper'; +import { Avatar, MD3Colors, Text } from 'react-native-paper'; import CustomFAB from './CustomFAB'; import CustomFABControls, { @@ -32,7 +26,7 @@ type Item = { }; const AnimatedFABExample = () => { - const { colors, isV3 } = useExampleTheme(); + const { colors } = useExampleTheme(); const isIOS = Platform.OS === 'ios'; @@ -47,63 +41,55 @@ const AnimatedFABExample = () => { const renderItem = React.useCallback( ({ item }: { item: Item }) => { - const TextComponent = isV3 ? Text : Paragraph; - return ( - {item.sender} - - + {item.date} - + - {item.header} - - + {item.message} - + setVisible(!visible)} @@ -114,7 +100,7 @@ const AnimatedFABExample = () => { ); }, - [visible, isV3] + [visible] ); const onScroll = ({ diff --git a/example/src/Examples/AnimatedFABExample/CustomFAB.tsx b/example/src/Examples/AnimatedFABExample/CustomFAB.tsx index 3495b00a86..9130debf33 100644 --- a/example/src/Examples/AnimatedFABExample/CustomFAB.tsx +++ b/example/src/Examples/AnimatedFABExample/CustomFAB.tsx @@ -3,8 +3,6 @@ import { Animated, Platform, StyleSheet, ViewStyle } from 'react-native'; import { AnimatedFAB } from 'react-native-paper'; -import { useExampleTheme } from '../../hooks/useExampleTheme'; - type CustomFABProps = { animatedValue: Animated.Value; visible: boolean; @@ -25,7 +23,6 @@ const CustomFAB = ({ iconMode, }: CustomFABProps) => { const [isExtended, setIsExtended] = React.useState(true); - const { isV3 } = useExampleTheme(); const isIOS = Platform.OS === 'ios'; @@ -44,7 +41,7 @@ const CustomFAB = ({ icon={'plus'} label={label} extended={isExtended} - uppercase={!isV3} + uppercase={false} onPress={() => console.log('Pressed')} visible={visible} animateFrom={animateFrom} diff --git a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx index a612a4234f..950022e9e4 100644 --- a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx +++ b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx @@ -6,7 +6,7 @@ import type { AnimatedFABAnimateFrom, AnimatedFABIconMode, } from 'react-native-paper'; -import { Paragraph, RadioButton, Text } from 'react-native-paper'; +import { RadioButton, Text } from 'react-native-paper'; import { useExampleTheme } from '../../hooks/useExampleTheme'; @@ -40,19 +40,15 @@ const CustomControl = ({ value, onChange, }: CustomControlProps) => { - const { isV3 } = useExampleTheme(); - const _renderItem = React.useCallback( ({ item }: ListRenderItemInfo<(typeof options)[number]>) => { - const TextComponent = isV3 ? Text : Paragraph; - return ( onChange(item)} style={styles.controlItem} > - {item} + {item} ); }, - [value, onChange, isV3] + [value, onChange] ); const _keyExtractor = React.useCallback( (item: (typeof options)[number]) => item, [] ); - const TextComponent = isV3 ? Text : Paragraph; - return ( - {name} + {name} { const theme = useExampleTheme(); const { bottom, left, right } = useSafeAreaInsets(); - const height = theme.isV3 ? 80 : 56; + const height = 80; const isCenterAlignedMode = appbarMode === 'center-aligned'; @@ -59,11 +58,7 @@ const AppbarExample = ({ navigation }: Props) => { {showLeftIcon && ( navigation.goBack()} /> )} - setShowSnackbar(true)} - /> + setShowSnackbar(true)} /> {isCenterAlignedMode ? false : showCalendarIcon && ( @@ -92,21 +87,14 @@ const AppbarExample = ({ navigation }: Props) => { showElevated, ]); - const TextComponent = theme.isV3 ? Text : Paragraph; - const renderFAB = () => { return ( {}} - style={[ - styles.fab, - theme.isV3 - ? { top: (height - MEDIUM_FAB_HEIGHT) / 2 } - : { bottom: height / 2 + bottom }, - ]} + style={[styles.fab, { top: (height - MEDIUM_FAB_HEIGHT) / 2 }]} /> ); }; @@ -114,49 +102,41 @@ const AppbarExample = ({ navigation }: Props) => { const renderDefaultOptions = () => ( <> - Left icon + Left icon - {!theme.isV3 && ( - - Subtitle - - - )} - Search icon + Subtitle + + + + Search icon - More icon + More icon - {theme.isV3 && ( - - Calendar icon - - - )} - Custom Color + Calendar icon + + + + Custom Color - {!theme.isV3 && ( - - Exact Dark Theme - - - )} - {theme.isV3 && ( - - Elevated - - - )} + + Exact Dark Theme + + + + Elevated + + ); @@ -166,40 +146,34 @@ const AppbarExample = ({ navigation }: Props) => { style={{ marginBottom: height + bottom }} contentContainerStyle={styles.contentContainer} > - {theme.isV3 ? ( - - {renderDefaultOptions()} - - ) : ( - renderDefaultOptions() - )} - {theme.isV3 && ( - - - setAppbarMode(value as AppbarModes) - } - > - - Small (default) - - - - Medium - - - - Large - - - - Center-aligned - - - - - )} + + {renderDefaultOptions()} + + + + setAppbarMode(value as AppbarModes) + } + > + + Small (default) + + + + Medium + + + + Large + + + + Center-aligned + + + + { { height: height + bottom, }, - theme.isV3 && { + { backgroundColor: theme.colors.elevation.level2, }, ]} @@ -218,9 +192,8 @@ const AppbarExample = ({ navigation }: Props) => { {}} /> {}} /> {}} /> - {theme.isV3 && renderFAB()} + {renderFAB()} - {!theme.isV3 && renderFAB()} setShowSnackbar(false)} @@ -258,6 +231,6 @@ const styles = StyleSheet.create({ right: 16, }, customColor: { - backgroundColor: yellowA200, + backgroundColor: MD3Colors.secondary80, }, }); diff --git a/example/src/Examples/AvatarExample.tsx b/example/src/Examples/AvatarExample.tsx index b77da80b09..229c2f4a0b 100644 --- a/example/src/Examples/AvatarExample.tsx +++ b/example/src/Examples/AvatarExample.tsx @@ -1,13 +1,11 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; -import { Avatar, List, MD2Colors, MD3Colors } from 'react-native-paper'; +import { Avatar, List, MD3Colors } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const AvatarExample = () => { - const { isV3 } = useExampleTheme(); return ( @@ -16,11 +14,11 @@ const AvatarExample = () => { style={[ styles.avatar, { - backgroundColor: isV3 ? MD3Colors.error70 : MD2Colors.yellow500, + backgroundColor: MD3Colors.error70, }, ]} label="XD" - color={isV3 ? MD3Colors.primary0 : MD2Colors.black} + color={MD3Colors.primary0} /> @@ -32,11 +30,11 @@ const AvatarExample = () => { style={[ styles.avatar, { - backgroundColor: isV3 ? MD3Colors.error70 : MD2Colors.yellow500, + backgroundColor: MD3Colors.error70, }, ]} icon="folder" - color={isV3 ? MD3Colors.primary0 : MD2Colors.black} + color={MD3Colors.primary0} /> diff --git a/example/src/Examples/BadgeExample.tsx b/example/src/Examples/BadgeExample.tsx index 17f0e1b160..3269dc70a8 100644 --- a/example/src/Examples/BadgeExample.tsx +++ b/example/src/Examples/BadgeExample.tsx @@ -5,18 +5,15 @@ import { Badge, IconButton, List, - MD2Colors, MD3Colors, Text, Switch, } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const BadgeExample = () => { const [visible, setVisible] = React.useState(true); - const { isV3 } = useExampleTheme(); return ( @@ -44,9 +41,7 @@ const BadgeExample = () => { style={[ styles.badge, { - backgroundColor: isV3 - ? MD3Colors.primary80 - : MD2Colors.blue500, + backgroundColor: MD3Colors.primary80, }, ]} > @@ -59,11 +54,11 @@ const BadgeExample = () => { - + - + diff --git a/example/src/Examples/BannerExample.tsx b/example/src/Examples/BannerExample.tsx index 0e856e9699..7ec874a283 100644 --- a/example/src/Examples/BannerExample.tsx +++ b/example/src/Examples/BannerExample.tsx @@ -8,7 +8,7 @@ import { View, } from 'react-native'; -import { Banner, FAB, MD2Colors, MD3Colors } from 'react-native-paper'; +import { Banner, FAB, MD3Colors } from 'react-native-paper'; import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; @@ -29,25 +29,18 @@ const BannerExample = () => { setHeight(layoutHeight); }; - const customTheme = !defaultTheme.isV3 - ? { - ...defaultTheme, - colors: { - text: MD2Colors.white, - surface: MD2Colors.blue200, - primary: MD2Colors.purple900, - }, - } - : { - ...defaultTheme, - colors: { - onSurface: MD3Colors.tertiary100, - elevation: { - level1: MD3Colors.tertiary50, - }, - primary: MD3Colors.tertiary10, - }, - }; + const customTheme = { + ...defaultTheme, + colors: { + ...defaultTheme.colors, + onSurface: MD3Colors.tertiary100, + elevation: { + ...defaultTheme.colors.elevation, + level1: MD3Colors.tertiary50, + }, + primary: MD3Colors.tertiary10, + }, + }; return ( <> diff --git a/example/src/Examples/BottomNavigationExample.tsx b/example/src/Examples/BottomNavigationExample.tsx index 6ed5d7add5..419aca0f9d 100644 --- a/example/src/Examples/BottomNavigationExample.tsx +++ b/example/src/Examples/BottomNavigationExample.tsx @@ -17,7 +17,6 @@ import { } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; type Route = { route: { key: string } }; @@ -49,7 +48,6 @@ const PhotoGallery = ({ route }: Route) => { }; const BottomNavigationExample = ({ navigation }: Props) => { - const { isV3 } = useExampleTheme(); const insets = useSafeAreaInsets(); const [index, setIndex] = React.useState(0); const [menuVisible, setMenuVisible] = React.useState(false); @@ -63,34 +61,25 @@ const BottomNavigationExample = ({ navigation }: Props) => { key: 'album', title: 'Album', focusedIcon: 'image-album', - ...(!isV3 && { color: '#6200ee' }), }, { key: 'library', title: 'Library', focusedIcon: 'inbox', + unfocusedIcon: 'inbox-outline', badge: true, - ...(isV3 - ? { unfocusedIcon: 'inbox-outline' } - : { - color: '#2962ff', - }), }, { key: 'favorites', title: 'Favorites', focusedIcon: 'heart', - ...(isV3 - ? { unfocusedIcon: 'heart-outline' } - : { - color: '#00796b', - }), + unfocusedIcon: 'heart-outline', }, { key: 'purchased', title: 'Purchased', focusedIcon: 'shopping', - ...(isV3 ? { unfocusedIcon: 'shopping-outline' } : { color: '#c51162' }), + unfocusedIcon: 'shopping-outline', }, ]); @@ -112,7 +101,6 @@ const BottomNavigationExample = ({ navigation }: Props) => { setMenuVisible(true)} - {...(!isV3 && { color: 'white' })} /> } > diff --git a/example/src/Examples/ButtonExample.tsx b/example/src/Examples/ButtonExample.tsx index 7ec58f90f4..c6ecc56d09 100644 --- a/example/src/Examples/ButtonExample.tsx +++ b/example/src/Examples/ButtonExample.tsx @@ -9,11 +9,11 @@ import ScreenWrapper from '../ScreenWrapper'; const ButtonExample = () => { const theme = useExampleTheme(); - const color = theme.isV3 ? theme.colors.inversePrimary : theme.colors.accent; + const color = theme.colors.inversePrimary; return ( - + - {theme.isV3 && ( - - - - - - - - - - - )} - + + + + + + + + + + + - + - {theme.isV3 && ( - - - - - - - - - - - )} + + + + + + + + + + diff --git a/example/src/Examples/CardExample.tsx b/example/src/Examples/CardExample.tsx index 5b12fc08bb..08829bdf85 100644 --- a/example/src/Examples/CardExample.tsx +++ b/example/src/Examples/CardExample.tsx @@ -7,7 +7,6 @@ import { Card, Chip, IconButton, - Paragraph, Text, } from 'react-native-paper'; @@ -19,21 +18,17 @@ import ScreenWrapper from '../ScreenWrapper'; type Mode = 'elevated' | 'outlined' | 'contained'; const CardExample = () => { - const { colors, isV3 } = useExampleTheme(); + const { colors } = useExampleTheme(); const [selectedMode, setSelectedMode] = React.useState('elevated' as Mode); const [isSelected, setIsSelected] = React.useState(false); const preferences = React.useContext(PreferencesContext); - const modes = isV3 - ? ['elevated', 'outlined', 'contained'] - : ['elevated', 'outlined']; - - const TextComponent = isV3 ? Text : Paragraph; + const modes: Mode[] = ['elevated', 'outlined', 'contained']; return ( - {(modes as Mode[]).map((mode) => ( + {modes.map((mode) => ( { /> - + The Abandoned Ship is a wrecked ship located on Route 108 in Hoenn, originally being a ship named the S.S. Cactus. The second part of the ship can only be accessed by using Dive and contains the Scanner. - + + + + + + + + + This is a card using title and subtitle with specified variants. + - {isV3 && ( - - - - - - This is a card using title and subtitle with specified variants. - - - - )} @@ -96,13 +89,13 @@ const CardExample = () => { )} /> - + Dotted around the Hoenn region, you will find loamy soil, many of which are housing berries. Once you have picked the berries, then you have the ability to use that loamy soil to grow your own berries. These can be any berry and will require attention to get the best crop. - + @@ -160,9 +153,9 @@ const CardExample = () => { - + This is a pressable chameleon. If you press me, I will alert. - + { left={(props) => } /> - + This is a long press only city. If you long press me, I will alert. - + { left={(props) => } /> - + This is pressable card. If you press me, I will switch the theme. - + diff --git a/example/src/Examples/CheckboxExample.tsx b/example/src/Examples/CheckboxExample.tsx index c8ce8c23a7..06088a646a 100644 --- a/example/src/Examples/CheckboxExample.tsx +++ b/example/src/Examples/CheckboxExample.tsx @@ -1,16 +1,8 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; -import { - Checkbox, - MD2Colors, - MD3Colors, - Paragraph, - Text, - TouchableRipple, -} from 'react-native-paper'; +import { Checkbox, MD3Colors, Text, TouchableRipple } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const CheckboxExample = () => { @@ -18,14 +10,11 @@ const CheckboxExample = () => { const [checkedCustom, setCheckedCustom] = React.useState(true); const [indeterminate, setIndeterminate] = React.useState(true); - const { isV3 } = useExampleTheme(); - const TextComponent = isV3 ? Text : Paragraph; - return ( setCheckedNormal(!checkedNormal)}> - Normal + Normal @@ -34,10 +23,10 @@ const CheckboxExample = () => { setCheckedCustom(!checkedCustom)}> - Custom + Custom @@ -46,7 +35,7 @@ const CheckboxExample = () => { setIndeterminate(!indeterminate)}> - Indeterminate + Indeterminate @@ -54,15 +43,15 @@ const CheckboxExample = () => { - Checked (Disabled) + Checked (Disabled) - Unchecked (Disabled) + Unchecked (Disabled) - Indeterminate (Disabled) + Indeterminate (Disabled) diff --git a/example/src/Examples/CheckboxItemExample.tsx b/example/src/Examples/CheckboxItemExample.tsx index 514272788c..acafd709b7 100644 --- a/example/src/Examples/CheckboxItemExample.tsx +++ b/example/src/Examples/CheckboxItemExample.tsx @@ -3,7 +3,6 @@ import { StyleSheet } from 'react-native'; import { Checkbox } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const CheckboxExample = () => { @@ -15,8 +14,6 @@ const CheckboxExample = () => { const [checkedDisabled, setCheckedDisabled] = React.useState(true); const [checkedLabelVariant, setCheckedLabelVariant] = React.useState(true); - const { isV3 } = useExampleTheme(); - return ( { onPress={() => setCheckedDisabled(!checkedDisabled)} disabled /> - {isV3 && ( - setCheckedLabelVariant(!checkedLabelVariant)} - /> - )} + setCheckedLabelVariant(!checkedLabelVariant)} + /> ); }; diff --git a/example/src/Examples/ChipExample.tsx b/example/src/Examples/ChipExample.tsx index 20f0393153..211ebfde49 100644 --- a/example/src/Examples/ChipExample.tsx +++ b/example/src/Examples/ChipExample.tsx @@ -2,16 +2,8 @@ import * as React from 'react'; import { Image, StyleSheet, View } from 'react-native'; import color from 'color'; -import { - Chip, - List, - MD2Colors, - MD3Colors, - Snackbar, - Text, -} from 'react-native-paper'; +import { Chip, List, MD3Colors, Snackbar, Text } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const ChipExample = () => { @@ -19,8 +11,7 @@ const ChipExample = () => { visible: false, text: '', }); - const { isV3 } = useExampleTheme(); - const customColor = isV3 ? MD3Colors.error50 : MD2Colors.purple900; + const customColor = MD3Colors.error50; return ( <> @@ -30,24 +21,20 @@ const ChipExample = () => { {}} style={styles.chip}> Simple - {isV3 && ( - <> - {}} - style={styles.chip} - > - With selected overlay - - {}} style={styles.chip}> - Elevated - - {}}> - Compact chip - - - )} + {}} + style={styles.chip} + > + With selected overlay + + {}} style={styles.chip}> + Elevated + + {}}> + Compact chip + {}} onClose={() => @@ -137,35 +124,31 @@ const ChipExample = () => { {}} style={styles.chip}> Simple - {isV3 && ( - <> - {}} - style={styles.chip} - > - With selected overlay - - {}} - style={styles.chip} - > - Elevated - - {}} - style={styles.chip} - > - Compact chip - - - )} + {}} + style={styles.chip} + > + With selected overlay + + {}} + style={styles.chip} + > + Elevated + + {}} + style={styles.chip} + > + Compact chip + {}} @@ -251,38 +234,34 @@ const ChipExample = () => { - {isV3 && ( - <> - {}} - compact - avatar={ - - } - style={[styles.chip, styles.customBorderRadius]} - > - Compact with custom border radius - - {}} - compact - avatar={ - - } - style={[styles.chip, styles.customBorderRadius]} - > - Compact with custom border radius - - - )} + {}} + compact + avatar={ + + } + style={[styles.chip, styles.customBorderRadius]} + > + Compact with custom border radius + + {}} + compact + avatar={ + + } + style={[styles.chip, styles.customBorderRadius]} + > + Compact with custom border radius + {}} diff --git a/example/src/Examples/DialogExample.tsx b/example/src/Examples/DialogExample.tsx index 4a9de665d7..d926ae6616 100644 --- a/example/src/Examples/DialogExample.tsx +++ b/example/src/Examples/DialogExample.tsx @@ -12,7 +12,6 @@ import { DialogWithRadioBtns, UndismissableDialog, } from './Dialogs'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; type ButtonVisibility = { @@ -21,7 +20,6 @@ type ButtonVisibility = { const DialogExample = () => { const [visible, setVisible] = React.useState({}); - const { isV3 } = useExampleTheme(); const _toggleDialog = (name: string) => () => setVisible({ ...visible, [name]: !visible[name] }); @@ -65,15 +63,13 @@ const DialogExample = () => { > Custom colors - {isV3 && ( - - )} + {Platform.OS === 'android' && ( diff --git a/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx b/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx index 32512dc91d..261b7ff9e6 100644 --- a/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx +++ b/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { Button, Portal, Dialog, MD2Colors } from 'react-native-paper'; +import { Button, Portal, Dialog, MD3Colors } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -26,7 +26,7 @@ const DialogWithDismissableBackButton = ({ - diff --git a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx index 5ef9e7e7f2..5860737e3e 100644 --- a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx +++ b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx @@ -1,10 +1,9 @@ import * as React from 'react'; import { ActivityIndicator, Platform, StyleSheet, View } from 'react-native'; -import { Dialog, MD2Colors, MD3Colors, Portal } from 'react-native-paper'; +import { Dialog, MD3Colors, Portal } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; -import { useExampleTheme } from '../../hooks/useExampleTheme'; const isIOS = Platform.OS === 'ios'; @@ -15,7 +14,6 @@ const DialogWithLoadingIndicator = ({ visible: boolean; close: () => void; }) => { - const { isV3 } = useExampleTheme(); return ( @@ -23,7 +21,7 @@ const DialogWithLoadingIndicator = ({ diff --git a/example/src/Examples/Dialogs/UndismissableDialog.tsx b/example/src/Examples/Dialogs/UndismissableDialog.tsx index 2af40f4e97..ded4896fe2 100644 --- a/example/src/Examples/Dialogs/UndismissableDialog.tsx +++ b/example/src/Examples/Dialogs/UndismissableDialog.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { Button, Portal, Dialog, MD2Colors } from 'react-native-paper'; +import { Button, Portal, Dialog, MD3Colors } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -18,7 +18,7 @@ const UndismissableDialog = ({ This is an undismissable dialog!! - diff --git a/example/src/Examples/FABExample.tsx b/example/src/Examples/FABExample.tsx index 0cff916980..e7db1118a7 100644 --- a/example/src/Examples/FABExample.tsx +++ b/example/src/Examples/FABExample.tsx @@ -4,7 +4,6 @@ import { Alert, StyleSheet, View } from 'react-native'; import { FAB, Portal, Text } from 'react-native-paper'; import { isWeb } from '../../utils'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; type FABVariant = 'primary' | 'secondary' | 'tertiary' | 'surface'; @@ -16,7 +15,6 @@ const FABExample = () => { const [toggleStackOnLongPress, setToggleStackOnLongPress] = React.useState(false); const [open, setOpen] = React.useState(false); - const { isV3 } = useExampleTheme(); const variants = ['primary', 'secondary', 'tertiary', 'surface']; const sizes = ['small', 'medium', 'large']; @@ -32,69 +30,49 @@ const FABExample = () => { onPress={() => setVisible(!visible)} /> - {isV3 && ( - <> - - {variants.map((variant) => ( - - {}} - visible={visible} - variant={variant as FABVariant} - /> - {visible && {variant}} - - ))} - - - {sizes.map((size) => ( - - {}} - visible={visible} - size={size as FABSize} - /> - {visible && {size}} - - ))} - - - {modes.map((mode) => ( - - {}} - visible={visible} - mode={mode as FABMode} - /> - {visible && {mode}} - - ))} + + {variants.map((variant) => ( + + {}} + visible={visible} + variant={variant as FABVariant} + /> + {visible && {variant}} - - )} - - {!isV3 && ( - <> + ))} + + + {sizes.map((size) => ( + {}} visible={visible} + size={size as FABSize} /> + {visible && {size}} + + ))} + + + {modes.map((mode) => ( + {}} visible={visible} + mode={mode as FABMode} /> - - )} + {visible && {mode}} + + ))} + + { icon: 'bell', label: 'Remind', onPress: () => {}, - size: isV3 ? 'small' : 'medium', + size: 'small', }, { icon: toggleStackOnLongPress diff --git a/example/src/Examples/IconButtonExample.tsx b/example/src/Examples/IconButtonExample.tsx index 6342b1e144..a2ac7e72db 100644 --- a/example/src/Examples/IconButtonExample.tsx +++ b/example/src/Examples/IconButtonExample.tsx @@ -1,36 +1,11 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; -import { IconButton, List, MD2Colors, MD3Colors } from 'react-native-paper'; +import { IconButton, List, MD3Colors } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const ButtonExample = () => { - const { isV3 } = useExampleTheme(); - if (!isV3) { - return ( - - {}} /> - {}} - /> - {}} loading /> - {}} /> - {}} - style={{ backgroundColor: MD2Colors.lightGreen200 }} - /> - {}} /> - - ); - } - return ( @@ -200,10 +175,6 @@ const ButtonExample = () => { ButtonExample.title = 'Icon Button'; const styles = StyleSheet.create({ - v2Container: { - flexDirection: 'row', - padding: 8, - }, v3Container: { flexDirection: 'column', }, diff --git a/example/src/Examples/ListSectionExample.tsx b/example/src/Examples/ListSectionExample.tsx index dd15ab29e2..79f4d7f624 100644 --- a/example/src/Examples/ListSectionExample.tsx +++ b/example/src/Examples/ListSectionExample.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { StyleSheet, Image, View } from 'react-native'; -import { Caption, List, Text, Chip, Divider } from 'react-native-paper'; +import { List, Text, Chip, Divider } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; const ListSectionExample = () => { @@ -97,7 +97,7 @@ const ListSectionExample = () => { > List Item - Yesterday + Yesterday )} description={({ diff --git a/example/src/Examples/MenuExample.tsx b/example/src/Examples/MenuExample.tsx index c1164d35b6..b43fb04778 100644 --- a/example/src/Examples/MenuExample.tsx +++ b/example/src/Examples/MenuExample.tsx @@ -16,7 +16,6 @@ import { TouchableRipple, } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; type ContextualMenuCoord = { x: number; y: number }; @@ -35,8 +34,6 @@ const MenuExample = ({ navigation }: Props) => { const [visible, setVisible] = React.useState({}); const [contextualMenuCoord, setContextualMenuCoor] = React.useState({ x: 0, y: 0 }); - const { isV3 } = useExampleTheme(); - const _toggleMenu = (name: string) => () => setVisible({ ...visible, [name]: !visible[name] }); @@ -66,16 +63,12 @@ const MenuExample = ({ navigation }: Props) => { visible={_getVisible('menu1')} onDismiss={_toggleMenu('menu1')} anchor={ - + } > {}} title="Undo" /> {}} title="Redo" /> - + {}} title="Cut" disabled /> {}} title="Copy" disabled /> {}} title="Paste" /> @@ -99,7 +92,7 @@ const MenuExample = ({ navigation }: Props) => { {}} title="Undo" /> {}} title="Redo" /> - + { onPress={() => {}} title="Paste" /> - {isV3 && ( - {}} - title="Share" - /> - )} + {}} + title="Share" + /> { > {}} title="Item 1" /> {}} title="Item 2" /> - + {}} title="Item 3" disabled /> diff --git a/example/src/Examples/ProgressBarExample.tsx b/example/src/Examples/ProgressBarExample.tsx index 565147f4c6..5ab797bb1d 100644 --- a/example/src/Examples/ProgressBarExample.tsx +++ b/example/src/Examples/ProgressBarExample.tsx @@ -4,7 +4,6 @@ import { View, StyleSheet, Animated } from 'react-native'; import { Button, ProgressBar, - MD2Colors, MD3Colors, ProgressBarProps, Text, @@ -29,7 +28,6 @@ const ProgressBarExample = () => { const [visible, setVisible] = React.useState(true); const [progress, setProgress] = React.useState(0.3); const theme = useExampleTheme(); - const { isV3 } = theme; const { current: progressBarValue } = React.useRef(new Animated.Value(0)); const runCustomAnimation = () => { @@ -64,7 +62,7 @@ const ProgressBarExample = () => { @@ -75,9 +73,9 @@ const ProgressBarExample = () => { diff --git a/example/src/Examples/RadioButtonExample.tsx b/example/src/Examples/RadioButtonExample.tsx index 88bf9ebfb7..7e3b3cf754 100644 --- a/example/src/Examples/RadioButtonExample.tsx +++ b/example/src/Examples/RadioButtonExample.tsx @@ -2,29 +2,24 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import { - MD2Colors, MD3Colors, - Paragraph, RadioButton, Text, TouchableRipple, } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; type State = 'normal' | 'normal-ios' | 'normal-item' | 'custom'; const RadioButtonExample = () => { const [checked, setChecked] = React.useState('normal'); - const { isV3 } = useExampleTheme(); - const TextComponent = isV3 ? Text : Paragraph; return ( setChecked('normal')}> - Normal - Material Design + Normal - Material Design { setChecked('normal-ios')}> - Normal 2 - IOS + Normal 2 - IOS { setChecked('custom')}> - Custom + Custom @@ -63,11 +58,11 @@ const RadioButtonExample = () => { onPress={() => setChecked('normal-item')} /> - Checked (Disabled) + Checked (Disabled) - Unchecked (Disabled) + Unchecked (Disabled) { const [value, setValue] = React.useState('first'); const [value2, setValue2] = React.useState('first'); - const { colors, isV3 } = useExampleTheme(); - const TextComponent = isV3 ? Text : Paragraph; + const { colors } = useExampleTheme(); return ( @@ -21,15 +20,15 @@ const RadioButtonGroupExample = () => { onValueChange={(value: string) => setValue(value)} > - First + First - Second + Second - Third + Third diff --git a/example/src/Examples/RadioButtonItemExample.tsx b/example/src/Examples/RadioButtonItemExample.tsx index 38eff755a5..e640a1dd0a 100644 --- a/example/src/Examples/RadioButtonItemExample.tsx +++ b/example/src/Examples/RadioButtonItemExample.tsx @@ -3,7 +3,6 @@ import { StyleSheet } from 'react-native'; import { RadioButton } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const RadioButtonItemExample = () => { @@ -15,8 +14,6 @@ const RadioButtonItemExample = () => { const [checkedDisabled, setCheckedDisabled] = React.useState(true); const [checkedLabelVariant, setCheckedLabelVariant] = React.useState(true); - const { isV3 } = useExampleTheme(); - return ( { value="iOS" disabled /> - {isV3 && ( - setCheckedLabelVariant(!checkedLabelVariant)} - value="default" - /> - )} + setCheckedLabelVariant(!checkedLabelVariant)} + value="default" + /> ); }; diff --git a/example/src/Examples/SearchbarExample.tsx b/example/src/Examples/SearchbarExample.tsx index 51a8af3ed8..c5b5eaca15 100644 --- a/example/src/Examples/SearchbarExample.tsx +++ b/example/src/Examples/SearchbarExample.tsx @@ -34,12 +34,12 @@ const SearchExample = ({ navigation }: Props) => { clickableLoading: '', }); - const { isV3, colors } = useExampleTheme(); + const { colors } = useExampleTheme(); return ( <> - {!isV3 && ( + @@ -47,136 +47,122 @@ const SearchExample = ({ navigation }: Props) => { } value={searchQueries.searchBarMode} style={styles.searchbar} + mode="bar" /> - )} - {isV3 && ( - - - setSearchQuery({ ...searchQueries, searchBarMode: query }) - } - value={searchQueries.searchBarMode} - style={styles.searchbar} - mode="bar" - /> - - setSearchQuery({ ...searchQueries, traileringIcon: query }) - } - value={searchQueries.traileringIcon} - traileringIcon={'microphone'} - traileringIconColor={ - isVisible ? MD3Colors.error40 : colors.onSurfaceVariant - } - traileringIconAccessibilityLabel={'microphone button'} - onTraileringIconPress={() => setIsVisible(true)} - style={styles.searchbar} - mode="bar" - /> - - setSearchQuery({ - ...searchQueries, - traileringIconWithRightItem: query, - }) - } - value={searchQueries.traileringIconWithRightItem} - traileringIcon={'microphone'} - traileringIconColor={ - isVisible ? MD3Colors.error40 : colors.onSurfaceVariant - } - traileringIconAccessibilityLabel={'microphone button'} - onTraileringIconPress={() => setIsVisible(true)} - right={(props) => ( - - )} - style={styles.searchbar} - /> - - setSearchQuery({ - ...searchQueries, - rightItem: query, - }) - } - value={searchQueries.rightItem} - right={(props) => ( - - )} - style={styles.searchbar} - /> - - setSearchQuery({ - ...searchQueries, - loadingBarMode: query, - }) - } - value={searchQueries.loadingBarMode} - style={styles.searchbar} - mode="bar" - loading - traileringIcon={'microphone'} - /> - - )} - {isV3 && ( - - - setSearchQuery({ - ...searchQueries, - searchViewMode: query, - }) - } - value={searchQueries.searchViewMode} - style={styles.searchbar} - mode="view" - /> - - setSearchQuery({ - ...searchQueries, - searchWithoutBottomLine: query, - }) - } - value={searchQueries.searchWithoutBottomLine} - style={styles.searchbar} - mode="view" - showDivider={false} - /> - - setSearchQuery({ - ...searchQueries, - loadingViewMode: query, - }) - } - value={searchQueries.loadingViewMode} - style={styles.searchbar} - mode="view" - loading - /> - - )} + + setSearchQuery({ ...searchQueries, traileringIcon: query }) + } + value={searchQueries.traileringIcon} + traileringIcon={'microphone'} + traileringIconColor={ + isVisible ? MD3Colors.error40 : colors.onSurfaceVariant + } + traileringIconAccessibilityLabel={'microphone button'} + onTraileringIconPress={() => setIsVisible(true)} + style={styles.searchbar} + mode="bar" + /> + + setSearchQuery({ + ...searchQueries, + traileringIconWithRightItem: query, + }) + } + value={searchQueries.traileringIconWithRightItem} + traileringIcon={'microphone'} + traileringIconColor={ + isVisible ? MD3Colors.error40 : colors.onSurfaceVariant + } + traileringIconAccessibilityLabel={'microphone button'} + onTraileringIconPress={() => setIsVisible(true)} + right={(props) => ( + + )} + style={styles.searchbar} + /> + + setSearchQuery({ + ...searchQueries, + rightItem: query, + }) + } + value={searchQueries.rightItem} + right={(props) => ( + + )} + style={styles.searchbar} + /> + + setSearchQuery({ + ...searchQueries, + loadingBarMode: query, + }) + } + value={searchQueries.loadingBarMode} + style={styles.searchbar} + mode="bar" + loading + traileringIcon={'microphone'} + /> + + + + setSearchQuery({ + ...searchQueries, + searchViewMode: query, + }) + } + value={searchQueries.searchViewMode} + style={styles.searchbar} + mode="view" + /> + + setSearchQuery({ + ...searchQueries, + searchWithoutBottomLine: query, + }) + } + value={searchQueries.searchWithoutBottomLine} + style={styles.searchbar} + mode="view" + showDivider={false} + /> + + setSearchQuery({ + ...searchQueries, + loadingViewMode: query, + }) + } + value={searchQueries.loadingViewMode} + style={styles.searchbar} + mode="view" + loading + /> + { const preferences = React.useContext(PreferencesContext); - const theme = useExampleTheme(); const [options, setOptions] = React.useState({ showSnackbar: false, @@ -51,17 +49,15 @@ const SnackbarExample = () => { } /> - {theme.isV3 && ( - - Close icon button - - setOptions({ ...options, showCloseIcon: !showCloseIcon }) - } - /> - - )} + + Close icon button + + setOptions({ ...options, showCloseIcon: !showCloseIcon }) + } + /> + Longer message { - const { isV3 } = useExampleTheme(); - const v2Elevation = [1, 2, 4, 8, 12]; - const elevationValues = isV3 ? Array.from({ length: 6 }) : v2Elevation; + const elevationValues = Array.from({ length: 6 }); const renderSurface = (index: number, mode: 'flat' | 'elevated') => ( - {isV3 - ? `Elevation ${index === 1 ? '(default)' : ''} ${index}` - : `${elevationValues[index]}`} + {`Elevation ${index === 1 ? '(default)' : ''} ${index}`} ); diff --git a/example/src/Examples/SwitchExample.tsx b/example/src/Examples/SwitchExample.tsx index a1bff15dc4..51baa12b01 100644 --- a/example/src/Examples/SwitchExample.tsx +++ b/example/src/Examples/SwitchExample.tsx @@ -1,24 +1,14 @@ import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; -import { - MD2Colors, - MD3Colors, - Paragraph, - Switch, - Text, - TouchableRipple, -} from 'react-native-paper'; +import { MD3Colors, Switch, Text, TouchableRipple } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const SwitchExample = () => { const [valueNormal, setNormalValue] = React.useState(true); const [valueCustom, setCustomValue] = React.useState(true); - const { isV3 } = useExampleTheme(); - const switchValueNormalLabel = `switch ${ valueNormal === true ? 'on' : 'off' }`; @@ -26,13 +16,11 @@ const SwitchExample = () => { valueCustom === true ? 'on' : 'off' }`; - const TextComponent = isV3 ? Text : Paragraph; - return Platform.OS === 'android' ? ( setNormalValue(!valueNormal)}> - Normal {switchValueNormalLabel} + Normal {switchValueNormalLabel} @@ -40,47 +28,44 @@ const SwitchExample = () => { setCustomValue(!valueCustom)}> - Custom {switchValueCustomlLabel} + Custom {switchValueCustomlLabel} - + - Switch on (disabled) + Switch on (disabled) - Switch off (disabled) + Switch off (disabled) ) : ( - Normal {switchValueNormalLabel} + Normal {switchValueNormalLabel} setNormalValue(!valueNormal)} /> - Custom {switchValueCustomlLabel} + Custom {switchValueCustomlLabel} setCustomValue(!valueCustom)} - color={isV3 ? MD3Colors.tertiary50 : MD2Colors.blue500} + color={MD3Colors.tertiary50} /> - Switch on (disabled) + Switch on (disabled) - Switch off (disabled) + Switch off (disabled) diff --git a/example/src/Examples/TextExample.tsx b/example/src/Examples/TextExample.tsx index 4bd1572359..1cb96edfaf 100644 --- a/example/src/Examples/TextExample.tsx +++ b/example/src/Examples/TextExample.tsx @@ -2,25 +2,17 @@ import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; import { - Caption, configureFonts, - Headline, MD3LightTheme, - Paragraph, PaperProvider, - Subheading, customText, - Title, } from 'react-native-paper'; -import { useExampleTheme } from '../hooks/useExampleTheme'; import ScreenWrapper from '../ScreenWrapper'; const Text = customText<'customVariant'>(); const TextExample = () => { - const { isV3 } = useExampleTheme(); - const fontConfig = { customVariant: { fontFamily: Platform.select({ @@ -44,75 +36,61 @@ const TextExample = () => { return ( - {!isV3 && ( - <> - Caption - Paragraph - Subheading - Title - Headline - - )} - - {isV3 && ( - <> - - Display Large - - - Display Medium - - - Display small - + + Display Large + + + Display Medium + + + Display small + - - Headline Large - - - Headline Medium - - - Headline Small - + + Headline Large + + + Headline Medium + + + Headline Small + - - Title Large - - - Title Medium - - - Title Small - + + Title Large + + + Title Medium + + + Title Small + - - Label Large - - - Label Medium - - - Label Small - + + Label Large + + + Label Medium + + + Label Small + - - Body Large - - - Body Medium - - - Body Small - + + Body Large + + + Body Medium + + + Body Small + - - - Custom Variant - - - - )} + + + Custom Variant + + ); diff --git a/example/src/Examples/TextInputExample.tsx b/example/src/Examples/TextInputExample.tsx index 7102abd0b6..caecdc49b8 100644 --- a/example/src/Examples/TextInputExample.tsx +++ b/example/src/Examples/TextInputExample.tsx @@ -13,7 +13,6 @@ import { configureFonts, HelperText, List, - MD2Colors, MD3Colors, TextInput, } from 'react-native-paper'; @@ -138,11 +137,7 @@ const TextInputExample = () => { const newColors = { ...state.iconsColor, - [name]: !color - ? theme.isV3 - ? theme.colors.primary - : theme.colors?.accent - : undefined, + [name]: !color ? theme.colors.primary : undefined, }; dispatch({ @@ -572,9 +567,7 @@ const TextInputExample = () => { * @@ -604,12 +597,8 @@ const TextInputExample = () => { onChangeText={(flatUnderlineColors) => inputActionHandler('flatUnderlineColors', flatUnderlineColors) } - underlineColor={ - theme.isV3 ? MD3Colors.primary70 : MD2Colors.pink400 - } - activeUnderlineColor={ - theme.isV3 ? MD3Colors.tertiary50 : MD2Colors.amber900 - } + underlineColor={MD3Colors.primary70} + activeUnderlineColor={MD3Colors.tertiary50} /> { onChangeText={(outlinedColors) => inputActionHandler('outlinedColors', outlinedColors) } - outlineColor={ - theme.isV3 ? MD3Colors.primary70 : MD2Colors.pink400 - } - activeOutlineColor={ - theme.isV3 ? MD3Colors.tertiary50 : MD2Colors.amber900 - } + outlineColor={MD3Colors.primary70} + activeOutlineColor={MD3Colors.tertiary50} /> { placeholder="Custom colors" /> - {fontsLoaded && theme.isV3 ? ( + {fontsLoaded ? ( void; toggleRtl: () => void; - toggleThemeVersion: () => void; toggleCollapsed: () => void; toggleCustomFont: () => void; toggleRippleEffect: () => void; toggleShouldUseDeviceColors?: () => void; - theme: MD2Theme | MD3Theme; + theme: MD3Theme; rtl: boolean; collapsed: boolean; customFontLoaded: boolean; diff --git a/example/src/hooks/useExampleTheme.ts b/example/src/hooks/useExampleTheme.ts index 9196f044b0..059ab4d05c 100644 --- a/example/src/hooks/useExampleTheme.ts +++ b/example/src/hooks/useExampleTheme.ts @@ -1,3 +1,3 @@ -import { MD2Theme, MD3Theme, useTheme } from 'react-native-paper'; +import { useTheme, type MD3Theme } from 'react-native-paper'; -export const useExampleTheme = () => useTheme(); +export const useExampleTheme = () => useTheme(); diff --git a/example/src/index.native.tsx b/example/src/index.native.tsx index 0878a1f13b..c6bba2b1bd 100644 --- a/example/src/index.native.tsx +++ b/example/src/index.native.tsx @@ -9,13 +9,7 @@ import { useFonts } from 'expo-font'; import { useKeepAwake } from 'expo-keep-awake'; import { StatusBar } from 'expo-status-bar'; import * as Updates from 'expo-updates'; -import { - PaperProvider, - MD2DarkTheme, - MD2LightTheme, - MD3DarkTheme, - MD3LightTheme, -} from 'react-native-paper'; +import { PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper'; import { SafeAreaInsetsContext } from 'react-native-safe-area-context'; import DrawerItems from './DrawerItems'; @@ -49,7 +43,6 @@ export default function PaperExample() { const [shouldUseDeviceColors, setShouldUseDeviceColors] = React.useState(true); const [isDarkMode, setIsDarkMode] = React.useState(false); - const [themeVersion, setThemeVersion] = React.useState<2 | 3>(3); const [rtl, setRtl] = React.useState( I18nManager.getConstants().isRTL ); @@ -59,10 +52,6 @@ export default function PaperExample() { const { theme: mdTheme } = useMaterial3Theme(); const theme = React.useMemo(() => { - if (themeVersion === 2) { - return isDarkMode ? MD2DarkTheme : MD2LightTheme; - } - if (!deviceColorsSupported || !shouldUseDeviceColors) { return isDarkMode ? MD3DarkTheme : MD3LightTheme; } @@ -70,7 +59,7 @@ export default function PaperExample() { return isDarkMode ? { ...MD3DarkTheme, colors: mdTheme.dark } : { ...MD3LightTheme, colors: mdTheme.light }; - }, [isDarkMode, mdTheme, shouldUseDeviceColors, themeVersion]); + }, [isDarkMode, mdTheme, shouldUseDeviceColors]); React.useEffect(() => { const restoreState = async () => { @@ -144,12 +133,6 @@ export default function PaperExample() { toggleCollapsed: () => setCollapsed(!collapsed), toggleCustomFont: () => setCustomFont(!customFontLoaded), toggleRippleEffect: () => setRippleEffectEnabled(!rippleEffectEnabled), - toggleThemeVersion: () => { - setCustomFont(false); - setCollapsed(false); - setThemeVersion((oldThemeVersion) => (oldThemeVersion === 2 ? 3 : 2)); - setRippleEffectEnabled(true); - }, customFontLoaded, rippleEffectEnabled, collapsed, @@ -213,7 +196,7 @@ export default function PaperExample() { ); }} - + diff --git a/example/src/index.tsx b/example/src/index.tsx index 76607763f1..4395d2d55e 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -5,13 +5,7 @@ import { createDrawerNavigator } from '@react-navigation/drawer'; import { InitialState, NavigationContainer } from '@react-navigation/native'; import { useFonts } from 'expo-font'; import { useKeepAwake } from 'expo-keep-awake'; -import { - PaperProvider, - MD3DarkTheme, - MD3LightTheme, - MD2DarkTheme, - MD2LightTheme, -} from 'react-native-paper'; +import { PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper'; import { SafeAreaInsetsContext } from 'react-native-safe-area-context'; import DrawerItems from './DrawerItems'; @@ -43,18 +37,14 @@ export default function PaperExample() { >(); const [isDarkMode, setIsDarkMode] = React.useState(false); - const [themeVersion, setThemeVersion] = React.useState<2 | 3>(3); const [collapsed, setCollapsed] = React.useState(false); const [customFontLoaded, setCustomFont] = React.useState(false); const [rippleEffectEnabled, setRippleEffectEnabled] = React.useState(true); - const theme = React.useMemo(() => { - if (themeVersion === 2) { - return isDarkMode ? MD2DarkTheme : MD2LightTheme; - } - - return isDarkMode ? MD3DarkTheme : MD3LightTheme; - }, [isDarkMode, themeVersion]); + const theme = React.useMemo( + () => (isDarkMode ? MD3DarkTheme : MD3LightTheme), + [isDarkMode] + ); React.useEffect(() => { const restoreState = async () => { @@ -112,12 +102,6 @@ export default function PaperExample() { const preferences = React.useMemo( () => ({ toggleTheme: () => setIsDarkMode((oldValue) => !oldValue), - toggleThemeVersion: () => { - setCustomFont(false); - setCollapsed(false); - setThemeVersion((oldThemeVersion) => (oldThemeVersion === 2 ? 3 : 2)); - setRippleEffectEnabled(true); - }, toggleCollapsed: () => setCollapsed(!collapsed), toggleCustomFont: () => setCustomFont(!customFontLoaded), toggleRippleEffect: () => setRippleEffectEnabled(!rippleEffectEnabled), diff --git a/src/babel/__fixtures__/rewrite-imports/code.js b/src/babel/__fixtures__/rewrite-imports/code.js index cc5e6d0b5c..6509ef2f67 100644 --- a/src/babel/__fixtures__/rewrite-imports/code.js +++ b/src/babel/__fixtures__/rewrite-imports/code.js @@ -5,7 +5,6 @@ import { Button, FAB, Appbar, - MD2Colors, MD3Colors, NonExistent, NonExistentSecond as Stuff, diff --git a/src/babel/__fixtures__/rewrite-imports/output.js b/src/babel/__fixtures__/rewrite-imports/output.js index ecaff8d814..7204b0ca33 100644 --- a/src/babel/__fixtures__/rewrite-imports/output.js +++ b/src/babel/__fixtures__/rewrite-imports/output.js @@ -4,7 +4,6 @@ import BottomNavigation from "react-native-paper/lib/module/components/BottomNav import Button from "react-native-paper/lib/module/components/Button/Button"; import FAB from "react-native-paper/lib/module/components/FAB"; import Appbar from "react-native-paper/lib/module/components/Appbar"; -import * as MD2Colors from "react-native-paper/lib/module/styles/themes/v2/colors"; import { MD3Colors } from "react-native-paper/lib/module/styles/themes/v3/tokens"; import { NonExistent, NonExistentSecond as Stuff } from "react-native-paper/lib/module/index.js"; import { ThemeProvider } from "react-native-paper/lib/module/core/theming"; diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 893c49f5ee..054a54bcad 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -45,10 +45,10 @@ const DURATION = 2400; * ## Usage * ```js * import * as React from 'react'; - * import { ActivityIndicator, MD2Colors } from 'react-native-paper'; + * import { ActivityIndicator, MD3Colors } from 'react-native-paper'; * * const MyComponent = () => ( - * + * * ); * * export default MyComponent; diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index d59c626769..4934ef7bf6 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Animated, - Platform, StyleProp, StyleSheet, View, @@ -9,12 +8,9 @@ import { ColorValue, } from 'react-native'; -import color from 'color'; - import AppbarContent from './AppbarContent'; import { AppbarModes, - DEFAULT_APPBAR_HEIGHT, getAppbarBackgroundColor, modeAppbarHeight, renderAppbarContent, @@ -165,11 +161,10 @@ const Appbar = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { isV3 } = theme; const flattenedStyle = StyleSheet.flatten(style); const { backgroundColor: customBackground, - elevation = isV3 ? (elevated ? 2 : 0) : 4, + elevation = elevated ? 2 : 0, ...restStyle } = (flattenedStyle || {}) as Exclude & { elevation?: number; @@ -184,28 +179,17 @@ const Appbar = ({ ); const isMode = (modeToCompare: AppbarModes) => { - return isV3 && mode === modeToCompare; + return mode === modeToCompare; }; - let isDark = false; - - if (typeof dark === 'boolean') { - isDark = dark; - } else if (!isV3) { - isDark = - backgroundColor === 'transparent' - ? false - : typeof backgroundColor === 'string' - ? !color(backgroundColor).isLight() - : true; - } + const isDark = typeof dark === 'boolean' ? dark : false; - const isV3CenterAlignedMode = isV3 && isMode('center-aligned'); + const isCenterAlignedMode = isMode('center-aligned'); let shouldCenterContent = false; let shouldAddLeftSpacing = false; let shouldAddRightSpacing = false; - if ((!isV3 && Platform.OS === 'ios') || isV3CenterAlignedMode) { + if (isCenterAlignedMode) { let hasAppbarContent = false; let leftItemsCount = 0; let rightItemsCount = 0; @@ -225,14 +209,12 @@ const Appbar = ({ }); shouldCenterContent = - hasAppbarContent && - leftItemsCount < 2 && - rightItemsCount < (isV3 ? 3 : 2); + hasAppbarContent && leftItemsCount < 2 && rightItemsCount < 3; shouldAddLeftSpacing = shouldCenterContent && leftItemsCount === 0; shouldAddRightSpacing = shouldCenterContent && rightItemsCount === 0; } - const spacingStyle = isV3 ? styles.v3Spacing : styles.spacing; + const spacingStyle = styles.v3Spacing; const insets = { paddingBottom: safeAreaInsets?.bottom, @@ -247,27 +229,25 @@ const Appbar = ({ { backgroundColor }, styles.appbar, { - height: isV3 ? modeAppbarHeight[mode] : DEFAULT_APPBAR_HEIGHT, + height: modeAppbarHeight[mode], }, insets, restStyle, - !theme.isV3 && { elevation }, ]} elevation={elevation as MD3Elevation} container {...rest} > {shouldAddLeftSpacing ? : null} - {(!isV3 || isMode('small') || isMode('center-aligned')) && ( + {(isMode('small') || isMode('center-aligned')) && ( <> {/* Render only the back action at first place */} {renderAppbarContent({ children, isDark, theme, - isV3, renderOnly: ['Appbar.BackAction'], - shouldCenterContent: isV3CenterAlignedMode || shouldCenterContent, + shouldCenterContent: isCenterAlignedMode || shouldCenterContent, })} {/* Render the rest of the content except the back action */} {renderAppbarContent({ @@ -278,9 +258,8 @@ const Appbar = ({ ], isDark, theme, - isV3, renderExcept: ['Appbar.BackAction'], - shouldCenterContent: isV3CenterAlignedMode || shouldCenterContent, + shouldCenterContent: isCenterAlignedMode || shouldCenterContent, })} )} @@ -297,14 +276,12 @@ const Appbar = ({ {renderAppbarContent({ children, isDark, - isV3, renderOnly: ['Appbar.BackAction'], mode, })} {renderAppbarContent({ children: filterAppbarActions(children, true), isDark, - isV3, renderOnly: ['Appbar.Action'], mode, })} @@ -313,7 +290,6 @@ const Appbar = ({ {renderAppbarContent({ children: filterAppbarActions(children), isDark, - isV3, renderExcept: [ 'Appbar', 'Appbar.BackAction', @@ -327,7 +303,6 @@ const Appbar = ({ {renderAppbarContent({ children, isDark, - isV3, renderOnly: ['Appbar.Content'], mode, })} @@ -344,9 +319,6 @@ const styles = StyleSheet.create({ alignItems: 'center', paddingHorizontal: 4, }, - spacing: { - width: 48, - }, v3Spacing: { width: 52, }, diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index ab5a72a110..14982cbecb 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -7,11 +7,10 @@ import type { ViewStyle, } from 'react-native'; -import color from 'color'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; -import { black } from '../../styles/themes/v2/colors'; +import type { MD3Theme } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; @@ -98,14 +97,13 @@ const AppbarAction = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); + const { colors } = theme as MD3Theme; const actionIconColor = iconColor ? iconColor - : theme.isV3 - ? isLeading - ? theme.colors.onSurface - : theme.colors.onSurfaceVariant - : color(black).alpha(0.54).rgb().string(); + : isLeading + ? colors.onSurface + : colors.onSurfaceVariant; return ( & { * Reference for the title. */ titleRef?: React.RefObject; - /** - * @deprecated Deprecated in v5.x - * Text for the subtitle. - */ - subtitle?: React.ReactNode; - /** - * @deprecated Deprecated in v5.x - * Style for the subtitle. - */ - subtitleStyle?: StyleProp; /** * Function to execute on press. */ @@ -84,7 +76,7 @@ export type Props = $RemoveChildren & { } & (TitleString | TitleElement); /** - * A component used to display a title and optional subtitle in an appbar. + * A component used to display a title in an appbar. * * ## Usage * ```js @@ -102,8 +94,6 @@ export type Props = $RemoveChildren & { */ const AppbarContent = ({ color: titleColor, - subtitle, - subtitleStyle, onPress, disabled, style, @@ -117,15 +107,9 @@ const AppbarContent = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { isV3, colors } = theme; - - const titleTextColor = titleColor - ? titleColor - : isV3 - ? colors.onSurface - : white; + const { colors, fonts } = theme as MD3Theme; - const subtitleColor = color(titleTextColor).alpha(0.7).rgb().string(); + const titleTextColor = titleColor ? titleColor : colors.onSurface; const modeContainerStyles = { small: styles.v3DefaultContainer, @@ -138,7 +122,7 @@ const AppbarContent = ({ const contentWrapperProps = { pointerEvents: 'box-none' as ViewProps['pointerEvents'], - style: [styles.container, isV3 && modeContainerStyles[mode], style], + style: [styles.container, modeContainerStyles[mode], style], testID, ...rest, }; @@ -147,18 +131,13 @@ const AppbarContent = ({ <> {typeof title === 'string' ? ( - {subtitle} - - ) : null} ); @@ -233,12 +204,6 @@ const styles = StyleSheet.create({ justifyContent: 'flex-end', paddingBottom: 28, }, - title: { - fontSize: Platform.OS === 'ios' ? 17 : 20, - }, - subtitle: { - fontSize: Platform.OS === 'ios' ? 11 : 14, - }, }); const touchableRole: AccessibilityRole = 'button'; diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index e0eb6386d7..9efe81bba1 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -13,7 +13,6 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Appbar } from './Appbar'; import { - DEFAULT_APPBAR_HEIGHT, getAppbarBackgroundColor, modeAppbarHeight, getAppbarBorders, @@ -104,14 +103,13 @@ const AppbarHeader = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { isV3 } = theme; const flattenedStyle = StyleSheet.flatten(style); const { - height = isV3 ? modeAppbarHeight[mode] : DEFAULT_APPBAR_HEIGHT, - elevation = isV3 ? (elevated ? 2 : 0) : 4, + height = modeAppbarHeight[mode], + elevation = elevated ? 2 : 0, backgroundColor: customBackground, - zIndex = isV3 && elevated ? 1 : 0, + zIndex = elevated ? 1 : 0, ...restStyle } = (flattenedStyle || {}) as Exclude & { height?: number; @@ -150,10 +148,8 @@ const AppbarHeader = ({ testID={testID} style={[{ height, backgroundColor }, styles.appbar, restStyle]} dark={dark} - {...(isV3 && { - mode, - })} {...rest} + mode={mode} theme={theme} /> diff --git a/src/components/Appbar/utils.ts b/src/components/Appbar/utils.ts index a6c68af2a5..fbf4fe438e 100644 --- a/src/components/Appbar/utils.ts +++ b/src/components/Appbar/utils.ts @@ -2,9 +2,8 @@ import React from 'react'; import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import { StyleSheet, Animated } from 'react-native'; -import overlay from '../../styles/overlay'; -import { black, white } from '../../styles/themes/v2/colors'; -import type { InternalTheme, ThemeProp } from '../../types'; +import { white } from '../../styles/themes/v2/colors'; +import type { InternalTheme, MD3Theme, ThemeProp } from '../../types'; export type AppbarModes = 'small' | 'medium' | 'large' | 'center-aligned'; @@ -24,26 +23,17 @@ const borderStyleProperties = [ export const getAppbarBackgroundColor = ( theme: InternalTheme, - elevation: number, + _elevation: number, customBackground?: ColorValue, elevated?: boolean ) => { - const { isV3, dark: isDarkTheme, mode, colors } = theme; - const isAdaptiveMode = mode === 'adaptive'; + const { colors } = theme as MD3Theme; if (customBackground) { return customBackground; } - if (!isV3) { - if (isDarkTheme && isAdaptiveMode) { - return overlay(elevation, colors?.surface); - } - - return colors.primary; - } - if (elevated) { - return theme.colors.elevation.level2; + return colors.elevation.level2; } return colors.surface; @@ -52,7 +42,6 @@ export const getAppbarBackgroundColor = ( export const getAppbarColor = ({ color, isDark, - isV3, }: BaseProps & { color: string }) => { if (typeof color !== 'undefined') { return color; @@ -62,11 +51,7 @@ export const getAppbarColor = ({ return white; } - if (isV3) { - return undefined; - } - - return black; + return undefined; }; export const getAppbarBorders = ( @@ -89,13 +74,11 @@ export const getAppbarBorders = ( type BaseProps = { isDark: boolean; - isV3: boolean; }; type RenderAppbarContentProps = BaseProps & { children: React.ReactNode; shouldCenterContent?: boolean; - isV3: boolean; renderOnly?: (string | boolean)[]; renderExcept?: string[]; mode?: AppbarModes; @@ -133,7 +116,6 @@ export const renderAppbarContent = ({ children, isDark, shouldCenterContent = false, - isV3, renderOnly, renderExcept, mode = 'small', @@ -172,16 +154,14 @@ export const renderAppbarContent = ({ theme?: ThemeProp; } = { theme, - color: getAppbarColor({ color: child.props.color, isDark, isV3 }), + color: getAppbarColor({ color: child.props.color, isDark }), }; // @ts-expect-error: TypeScript complains about the type of type but it doesn't matter if (child.type.displayName === 'Appbar.Content') { props.mode = mode; props.style = [ - isV3 - ? i === 0 && !shouldCenterContent && styles.v3Spacing - : i !== 0 && styles.v2Spacing, + i === 0 && !shouldCenterContent && styles.v3Spacing, shouldCenterContent && styles.centerAlignedContent, child.props.style, ]; @@ -195,9 +175,6 @@ const styles = StyleSheet.create({ centerAlignedContent: { alignItems: 'center', }, - v2Spacing: { - marginLeft: 8, - }, v3Spacing: { marginLeft: 12, }, diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 54c70770f2..d8bf38015b 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -8,9 +8,7 @@ import { } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import { black, white } from '../styles/themes/v2/colors'; import type { ThemeProp } from '../types'; -import getContrastingColor from '../utils/getContrastingColor'; const defaultSize = 20; @@ -85,20 +83,14 @@ const Badge = ({ }).start(); }, [visible, opacity, scale]); - const { - backgroundColor = theme.isV3 - ? theme.colors.error - : theme.colors?.notification, - ...restStyle - } = (StyleSheet.flatten(style) || {}) as TextStyle; + const { backgroundColor = theme.colors.error, ...restStyle } = + (StyleSheet.flatten(style) || {}) as TextStyle; - const textColor = theme.isV3 - ? theme.colors.onError - : getContrastingColor(backgroundColor, white, black); + const textColor = theme.colors.onError; const borderRadius = size / 2; - const paddingHorizontal = theme.isV3 ? 3 : 4; + const paddingHorizontal = 3; return ( { const theme = useInternalTheme(themeOverrides); + const { colors } = theme as MD3Theme; const { current: position } = React.useRef( new Animated.Value(visible ? 1 : 0) ); @@ -192,10 +193,10 @@ const Banner = ({ return ( @@ -222,12 +223,11 @@ const Banner = ({ ) : null} @@ -292,9 +292,6 @@ const styles = StyleSheet.create({ button: { margin: 4, }, - elevation: { - elevation: 1, - }, transparent: { opacity: 0, }, diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index c4e9b71a60..a15cdd0110 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -179,10 +179,6 @@ export type Props = { * Get badge for the tab, uses `route.badge` by default. */ getBadge?: (props: { route: Route }) => boolean | number | string | undefined; - /** - * Get color for the tab, uses `route.color` by default. - */ - getColor?: (props: { route: Route }) => string | undefined; /** * Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component. */ @@ -327,7 +323,6 @@ const BottomNavigation = ({ renderTouchable, getLabelText, getBadge, - getColor, getAccessibilityLabel, getTestID, activeColor, @@ -353,9 +348,8 @@ const BottomNavigation = ({ }: Props) => { const theme = useInternalTheme(themeOverrides); const { scale } = theme.animation; - const compact = compactProp ?? !theme.isV3; - let shifting = - shiftingProp ?? (theme.isV3 ? false : navigationState.routes.length > 3); + const compact = compactProp ?? false; + let shifting = shiftingProp ?? false; if (shifting && navigationState.routes.length < 2) { shifting = false; @@ -404,7 +398,7 @@ const BottomNavigation = ({ ...navigationState.routes.map((_, i) => Animated.timing(tabsPositionAnims[i], { toValue: i === index ? 0 : i >= index ? 1 : -1, - duration: theme.isV3 || shifting ? 150 * scale : 0, + duration: 150 * scale, useNativeDriver: true, easing: sceneAnimationEasing, }) @@ -579,7 +573,6 @@ const BottomNavigation = ({ renderTouchable={renderTouchable} getLabelText={getLabelText} getBadge={getBadge} - getColor={getColor} getAccessibilityLabel={getAccessibilityLabel} getTestID={getTestID} activeColor={activeColor} diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index 17e4899750..0791a8fa3b 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -11,7 +11,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { @@ -20,9 +19,7 @@ import { getLabelColor, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import overlay from '../../styles/overlay'; -import { black, white } from '../../styles/themes/v2/colors'; -import type { ThemeProp } from '../../types'; +import type { MD3Theme, ThemeProp } from '../../types'; import useAnimatedValue from '../../utils/useAnimatedValue'; import useAnimatedValueArray from '../../utils/useAnimatedValueArray'; import useIsKeyboardShown from '../../utils/useIsKeyboardShown'; @@ -149,10 +146,6 @@ export type Props = { * Get badge for the tab, uses `route.badge` by default. */ getBadge?: (props: { route: Route }) => boolean | number | string | undefined; - /** - * Get color for the tab, uses `route.color` by default. - */ - getColor?: (props: { route: Route }) => string | undefined; /** * Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component. */ @@ -212,7 +205,6 @@ export type Props = { testID?: string; }; -const MIN_RIPPLE_SCALE = 0.001; // Minimum scale is not 0 due to bug with animation const MIN_TAB_WIDTH = 96; const MAX_TAB_WIDTH = 168; const BAR_HEIGHT = 56; @@ -321,7 +313,6 @@ const BottomNavigationBar = ({ ), getLabelText = ({ route }: { route: Route }) => route.title, getBadge = ({ route }: { route: Route }) => route.badge, - getColor = ({ route }: { route: Route }) => route.color, getAccessibilityLabel = ({ route }: { route: Route }) => route.accessibilityLabel, getTestID = ({ route }: { route: Route }) => route.testID, @@ -342,11 +333,11 @@ const BottomNavigationBar = ({ theme: themeOverrides, }: Props) => { const theme = useInternalTheme(themeOverrides); + const { colors } = theme as MD3Theme; const { bottom, left, right } = useSafeAreaInsets(); const { scale } = theme.animation; - const compact = compactProp ?? !theme.isV3; - let shifting = - shiftingProp ?? (theme.isV3 ? false : navigationState.routes.length > 3); + const compact = compactProp ?? false; + let shifting = shiftingProp ?? false; if (shifting && navigationState.routes.length < 2) { shifting = false; @@ -371,18 +362,7 @@ const BottomNavigationBar = ({ ); /** - * Index of the currently active tab. Used for setting the background color. - * We don't use the color as an animated value directly, because `setValue` seems to be buggy with colors?. - */ - const indexAnim = useAnimatedValue(navigationState.index); - - /** - * Animation for the background color ripple, used to determine it's scale and opacity. - */ - const rippleAnim = useAnimatedValue(MIN_RIPPLE_SCALE); - - /** - * Layout of the navigation bar. The width is used to determine the size and position of the ripple. + * Layout of the navigation bar. */ const [layout, onLayout] = useLayout(); @@ -412,42 +392,21 @@ const BottomNavigationBar = ({ const animateToIndex = React.useCallback( (index: number) => { - // Reset the ripple to avoid glitch if it's currently animating - rippleAnim.setValue(MIN_RIPPLE_SCALE); - - Animated.parallel([ - Animated.timing(rippleAnim, { - toValue: 1, - duration: theme.isV3 || shifting ? 400 * scale : 0, - useNativeDriver: true, - }), - ...navigationState.routes.map((_, i) => + Animated.parallel( + navigationState.routes.map((_, i) => Animated.timing(tabsAnims[i], { toValue: i === index ? 1 : 0, - duration: theme.isV3 || shifting ? 150 * scale : 0, + duration: 150 * scale, useNativeDriver: true, easing: animationEasing, }) - ), - ]).start(() => { + ) + ).start(() => { // Workaround a bug in native animations where this is reset after first animation tabsAnims.map((tab, i) => tab.setValue(i === index ? 1 : 0)); - - // Update the index to change bar's background color and then hide the ripple - indexAnim.setValue(index); - rippleAnim.setValue(MIN_RIPPLE_SCALE); }); }, - [ - rippleAnim, - theme.isV3, - shifting, - scale, - navigationState.routes, - tabsAnims, - animationEasing, - indexAnim, - ] + [scale, navigationState.routes, tabsAnims, animationEasing] ); React.useEffect(() => { @@ -479,62 +438,28 @@ const BottomNavigationBar = ({ }; const { routes } = navigationState; - const { colors, dark: isDarkTheme, mode, isV3 } = theme; - const { backgroundColor: customBackground, elevation = 4 } = - (StyleSheet.flatten(style) || {}) as { - elevation?: number; - backgroundColor?: ColorValue; - }; + const { backgroundColor: customBackground } = (StyleSheet.flatten(style) || + {}) as { + elevation?: number; + backgroundColor?: ColorValue; + }; - const approxBackgroundColor = customBackground - ? customBackground - : isDarkTheme && mode === 'adaptive' - ? overlay(elevation, colors?.surface) - : colors?.primary; - - const v2BackgroundColorInterpolation = shifting - ? indexAnim.interpolate({ - inputRange: routes.map((_, i) => i), - // FIXME: does outputRange support ColorValue or just strings? - // @ts-expect-error - outputRange: routes.map( - (route) => getColor({ route }) || approxBackgroundColor - ), - }) - : approxBackgroundColor; - - const backgroundColor = isV3 - ? customBackground || theme.colors.elevation.level2 - : shifting - ? v2BackgroundColorInterpolation - : approxBackgroundColor; - - const isDark = - typeof approxBackgroundColor === 'string' - ? !color(approxBackgroundColor).isLight() - : true; - - const textColor = isDark ? white : black; + const backgroundColor = customBackground || colors.elevation.level2; const activeTintColor = getActiveTintColor({ activeColor, - defaultColor: textColor, theme, }); const inactiveTintColor = getInactiveTintColor({ inactiveColor, - defaultColor: textColor, theme, }); - const touchColor = color(activeTintColor).alpha(0.12).rgb().string(); const maxTabWidth = routes.length > 3 ? MIN_TAB_WIDTH : MAX_TAB_WIDTH; const maxTabBarWidth = maxTabWidth * routes.length; - const rippleSize = layout.width / 4; - const insets = { left: safeAreaInsets?.left ?? left, right: safeAreaInsets?.right ?? right, @@ -543,10 +468,9 @@ const BottomNavigationBar = ({ return ( ({ accessibilityRole={'tablist'} testID={`${testID}-content-wrapper`} > - {shifting && !isV3 ? ( - - ) : null} {routes.map((route, index) => { const focused = navigationState.index === index; const active = tabsAnims[index]; - // Scale the label up - const scale = - labeled && shifting - ? active.interpolate({ - inputRange: [0, 1], - outputRange: [0.5, 1], - }) - : 1; - // Move down the icon to account for no-label in shifting and smaller label in non-shifting. const translateY = labeled ? shifting @@ -684,7 +562,6 @@ const BottomNavigationBar = ({ tintColor: activeTintColor, hasColor: Boolean(activeColor), focused, - defaultColor: textColor, theme, }); @@ -692,55 +569,53 @@ const BottomNavigationBar = ({ tintColor: inactiveTintColor, hasColor: Boolean(inactiveColor), focused, - defaultColor: textColor, theme, }); const badgeStyle = { - top: !isV3 ? -2 : typeof badge === 'boolean' ? 4 : 2, + top: typeof badge === 'boolean' ? 4 : 2, right: - (badge != null && typeof badge !== 'boolean' + badge != null && typeof badge !== 'boolean' ? String(badge).length * -2 - : 0) - (!isV3 ? 2 : 0), + : 0, }; - const isLegacyOrV3Shifting = !isV3 || (isV3 && shifting && labeled); + const isLegacyOrV3Shifting = shifting && labeled; - const font = isV3 ? theme.fonts.labelMedium : {}; + const font = (theme as MD3Theme).fonts.labelMedium; return renderTouchable({ key: route.key, route, borderless: true, centered: true, - rippleColor: isV3 ? 'transparent' : touchColor, + rippleColor: 'transparent', onPress: () => onTabPress(eventForIndex(index)), onLongPress: () => onTabLongPress?.(eventForIndex(index)), testID: getTestID({ route }), accessibilityLabel: getAccessibilityLabel({ route }), accessibilityRole: Platform.OS === 'ios' ? 'button' : 'tab', accessibilityState: { selected: focused }, - style: [styles.item, isV3 && styles.v3Item], + style: [styles.item, styles.v3Item], children: ( - {isV3 && focused && ( + {focused && ( ({ scaleX: outlineScale, }, ], - backgroundColor: theme.colors.secondaryContainer, + backgroundColor: colors.secondaryContainer, }, activeIndicatorStyle, ]} @@ -759,7 +634,7 @@ const BottomNavigationBar = ({ ({ ({ ) : ( ({ {typeof badge === 'boolean' ? ( - + ) : ( {badge} @@ -821,12 +696,7 @@ const BottomNavigationBar = ({ {labeled ? ( - + ({ )} - ) : ( - !isV3 && - )} + ) : null} ), }); @@ -939,9 +807,6 @@ const styles = StyleSheet.create({ v3Item: { paddingVertical: 0, }, - ripple: { - position: 'absolute', - }, iconContainer: { height: 24, width: 24, @@ -1002,7 +867,4 @@ const styles = StyleSheet.create({ borderRadius: OUTLINE_WIDTH / 4, alignSelf: 'center', }, - elevation: { - elevation: 4, - }, }); diff --git a/src/components/BottomNavigation/utils.ts b/src/components/BottomNavigation/utils.ts index 93a4d95091..20208d4b7d 100644 --- a/src/components/BottomNavigation/utils.ts +++ b/src/components/BottomNavigation/utils.ts @@ -1,70 +1,51 @@ -import color from 'color'; -import type { InternalTheme } from 'src/types'; - -import type { black, white } from '../../styles/themes/v2/colors'; - -type BaseProps = { - defaultColor: typeof black | typeof white; - theme: InternalTheme; -}; +import type { InternalTheme, MD3Theme } from '../../types'; export const getActiveTintColor = ({ activeColor, - defaultColor, theme, -}: BaseProps & { +}: { activeColor: string | undefined; + theme: InternalTheme; }) => { if (typeof activeColor === 'string') { return activeColor; } - if (theme.isV3) { - return theme.colors.onSecondaryContainer; - } - - return defaultColor; + return (theme as MD3Theme).colors.onSecondaryContainer; }; export const getInactiveTintColor = ({ inactiveColor, - defaultColor, theme, -}: BaseProps & { +}: { inactiveColor: string | undefined; + theme: InternalTheme; }) => { if (typeof inactiveColor === 'string') { return inactiveColor; } - if (theme.isV3) { - return theme.colors.onSurfaceVariant; - } - - return color(defaultColor).alpha(0.5).rgb().string(); + return (theme as MD3Theme).colors.onSurfaceVariant; }; export const getLabelColor = ({ tintColor, hasColor, focused, - defaultColor, theme, -}: BaseProps & { +}: { tintColor: string; hasColor: boolean; focused: boolean; + theme: InternalTheme; }) => { + const { colors } = theme as MD3Theme; if (hasColor) { return tintColor; } - if (theme.isV3) { - if (focused) { - return theme.colors.onSurface; - } - return theme.colors.onSurfaceVariant; + if (focused) { + return colors.onSurface; } - - return defaultColor; + return colors.onSurfaceVariant; }; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 2e4d2cf519..1d1d0d3002 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -21,7 +21,7 @@ import { getButtonTouchableRippleStyle, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $Omit, ThemeProp } from '../../types'; +import type { $Omit, MD3Theme, ThemeProp } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; import hasTouchHandler from '../../utils/hasTouchHandler'; import { splitStyles } from '../../utils/splitStyles'; @@ -218,8 +218,8 @@ const Button = ( }, [mode] ); - const { roundness, isV3, animation } = theme; - const uppercase = uppercaseProp ?? !theme.isV3; + const { roundness, animation } = theme; + const uppercase = uppercaseProp ?? false; const isWeb = Platform.OS === 'web'; const hasPassedTouchHandler = hasTouchHandler({ @@ -229,10 +229,9 @@ const Button = ( onLongPress, }); - const isElevationEntitled = - !disabled && (isV3 ? isMode('elevated') : isMode('contained')); - const initialElevation = isV3 ? 1 : 2; - const activeElevation = isV3 ? 2 : 8; + const isElevationEntitled = !disabled && isMode('elevated'); + const initialElevation = 1; + const activeElevation = 2; const { current: elevation } = React.useRef( new Animated.Value(isElevationEntitled ? initialElevation : 0) @@ -250,7 +249,7 @@ const Button = ( const handlePressIn = (e: GestureResponderEvent) => { onPressIn?.(e); - if (isV3 ? isMode('elevated') : isMode('contained')) { + if (isMode('elevated')) { const { scale } = animation; Animated.timing(elevation, { toValue: activeElevation, @@ -263,7 +262,7 @@ const Button = ( const handlePressOut = (e: GestureResponderEvent) => { onPressOut?.(e); - if (isV3 ? isMode('elevated') : isMode('contained')) { + if (isMode('elevated')) { const { scale } = animation; Animated.timing(elevation, { toValue: initialElevation, @@ -280,8 +279,8 @@ const Button = ( (style) => style.startsWith('border') && style.endsWith('Radius') ); - const borderRadius = (isV3 ? 5 : 1) * roundness; - const iconSize = isV3 ? 18 : 16; + const borderRadius = 5 * roundness; + const iconSize = 18; const { backgroundColor, borderColor, textColor, borderWidth } = getButtonColors({ @@ -311,7 +310,7 @@ const Button = ( const { color: customLabelColor, fontSize: customLabelSize } = StyleSheet.flatten(labelStyle) || {}; - const font = isV3 ? theme.fonts.labelLarge : theme.fonts.medium; + const font = (theme as MD3Theme).fonts.labelLarge; const textStyle = { color: textColor, @@ -322,16 +321,14 @@ const Button = ( StyleSheet.flatten(contentStyle)?.flexDirection === 'row-reverse' ? [ styles.iconReverse, - isV3 && styles[`md3IconReverse${compact ? 'Compact' : ''}`], - isV3 && - isMode('text') && + styles[`md3IconReverse${compact ? 'Compact' : ''}`], + isMode('text') && styles[`md3IconReverseTextMode${compact ? 'Compact' : ''}`], ] : [ styles.icon, - isV3 && styles[`md3Icon${compact ? 'Compact' : ''}`], - isV3 && - isMode('text') && + styles[`md3Icon${compact ? 'Compact' : ''}`], + isMode('text') && styles[`md3IconTextMode${compact ? 'Compact' : ''}`], ]; @@ -346,10 +343,9 @@ const Button = ( compact && styles.compact, buttonStyle, style, - !isV3 && !disabled && { elevation }, ] as Animated.WithAnimatedValue> } - {...(isV3 && { elevation: elevation })} + elevation={elevation} container > { + const { colors } = theme as MD3Theme; if (customButtonColor && !disabled) { return customButtonColor; } - if (theme.isV3) { - if (disabled) { - if (isMode('outlined') || isMode('text')) { - return 'transparent'; - } - - return theme.colors.surfaceDisabled; - } - - if (isMode('elevated')) { - return theme.colors.elevation.level1; + if (disabled) { + if (isMode('outlined') || isMode('text')) { + return 'transparent'; } - if (isMode('contained')) { - return theme.colors.primary; - } + return colors.surfaceDisabled; + } - if (isMode('contained-tonal')) { - return theme.colors.secondaryContainer; - } + if (isMode('elevated')) { + return colors.elevation.level1; } if (isMode('contained')) { - if (disabled) { - return color(theme.dark ? white : black) - .alpha(0.12) - .rgb() - .string(); - } + return colors.primary; + } - return theme.colors.primary; + if (isMode('contained-tonal')) { + return colors.secondaryContainer; } return 'transparent'; @@ -101,85 +89,56 @@ const getButtonTextColor = ({ backgroundColor: string; dark?: boolean; }) => { + const { colors } = theme as MD3Theme; if (customTextColor && !disabled) { return customTextColor; } - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - if (typeof dark === 'boolean') { - if ( - isMode('contained') || - isMode('contained-tonal') || - isMode('elevated') - ) { - return isDark({ dark, backgroundColor }) ? white : black; - } - } - - if (isMode('outlined') || isMode('text') || isMode('elevated')) { - return theme.colors.primary; - } - - if (isMode('contained')) { - return theme.colors.onPrimary; - } + if (disabled) { + return colors.onSurfaceDisabled; + } - if (isMode('contained-tonal')) { - return theme.colors.onSecondaryContainer; + if (typeof dark === 'boolean') { + if ( + isMode('contained') || + isMode('contained-tonal') || + isMode('elevated') + ) { + return isDark({ dark, backgroundColor }) ? white : black; } } - if (disabled) { - return color(theme.dark ? white : black) - .alpha(0.32) - .rgb() - .string(); + if (isMode('outlined') || isMode('text') || isMode('elevated')) { + return colors.primary; } if (isMode('contained')) { - return isDark({ dark, backgroundColor }) ? white : black; + return colors.onPrimary; + } + + if (isMode('contained-tonal')) { + return colors.onSecondaryContainer; } - return theme.colors.primary; + return colors.primary; }; const getButtonBorderColor = ({ isMode, disabled, theme }: BaseProps) => { - if (theme.isV3) { - if (disabled && isMode('outlined')) { - return theme.colors.surfaceDisabled; - } - - if (isMode('outlined')) { - return theme.colors.outline; - } + const { colors } = theme as MD3Theme; + if (disabled && isMode('outlined')) { + return colors.surfaceDisabled; } if (isMode('outlined')) { - return color(theme.dark ? white : black) - .alpha(0.29) - .rgb() - .string(); + return colors.outline; } return 'transparent'; }; -const getButtonBorderWidth = ({ - isMode, - theme, -}: Omit) => { - if (theme.isV3) { - if (isMode('outlined')) { - return 1; - } - } - +const getButtonBorderWidth = ({ isMode }: Omit) => { if (isMode('outlined')) { - return StyleSheet.hairlineWidth; + return 1; } return 0; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 848be9fa13..422101b4e8 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -181,7 +181,7 @@ const Card = ( const { current: elevationDarkAdaptive } = React.useRef( new Animated.Value(cardElevation) ); - const { animation, dark, mode, roundness, isV3 } = theme; + const { animation, dark, mode, roundness } = theme; const prevDarkRef = React.useRef(dark); React.useEffect(() => { @@ -213,20 +213,20 @@ const Card = ( ]); const runElevationAnimation = (pressType: HandlePressType) => { - if (isV3 && isMode('contained')) { + if (isMode('contained')) { return; } const isPressTypeIn = pressType === 'in'; if (dark && isAdaptiveMode) { Animated.timing(elevationDarkAdaptive, { - toValue: isPressTypeIn ? (isV3 ? 2 : 8) : cardElevation, + toValue: isPressTypeIn ? 2 : cardElevation, duration: animationDuration, useNativeDriver: false, }).start(); } else { Animated.timing(elevation, { - toValue: isPressTypeIn ? (isV3 ? 2 : 8) : cardElevation, + toValue: isPressTypeIn ? 2 : cardElevation, duration: animationDuration, useNativeDriver: false, }).start(); @@ -267,7 +267,7 @@ const Card = ( ); const borderRadiusCombinedStyles = { - borderRadius: (isV3 ? 3 : 1) * roundness, + borderRadius: 3 * roundness, ...borderRadiusStyles, }; @@ -290,20 +290,12 @@ const Card = ( & { * ``` */ const CardActions = ({ theme, style, children, ...rest }: Props) => { - const { isV3 } = useInternalTheme(theme); + useInternalTheme(theme); - const justifyContent = ( - isV3 ? 'flex-end' : 'flex-start' - ) as ViewStyle['justifyContent']; + const justifyContent = 'flex-end' as ViewStyle['justifyContent']; const containerStyle = [styles.container, { justifyContent }, style]; return ( @@ -50,11 +48,10 @@ const CardActions = ({ theme, style, children, ...rest }: Props) => { return child; } - const compact = !isV3 && child.props.compact !== false; + const compact = child.props.compact; const mode = - child.props.mode ?? - (isV3 ? (index === 0 ? 'outlined' : 'contained') : undefined); - const childStyle = [isV3 && styles.button, child.props.style]; + child.props.mode ?? (index === 0 ? 'outlined' : 'contained'); + const childStyle = [styles.button, child.props.style]; return React.cloneElement(child, { ...child.props, diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index b3d78fc259..f24809c405 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -10,8 +10,6 @@ import { import { useInternalTheme } from '../../core/theming'; import type { MD3TypescaleKey, ThemeProp } from '../../types'; import Text from '../Typography/Text'; -import Caption from '../Typography/v2/Caption'; -import Title from '../Typography/v2/Title'; export type Props = React.ComponentPropsWithRef & { /** @@ -151,9 +149,7 @@ const CardTitle = ({ style, theme: themeOverrides, }: Props) => { - const theme = useInternalTheme(themeOverrides); - const TitleComponent = theme.isV3 ? Text : Title; - const SubtitleComponent = theme.isV3 ? Text : Caption; + useInternalTheme(themeOverrides); const minHeight = subtitle || left || right ? 72 : 50; const marginBottom = subtitle ? 0 : 2; @@ -170,24 +166,24 @@ const CardTitle = ({ {title && ( - {title} - + )} {subtitle && ( - {subtitle} - + )} {right ? right({ size: 24 }) : null} diff --git a/src/components/Card/utils.tsx b/src/components/Card/utils.tsx index c0f10951c5..9a4ed5df7d 100644 --- a/src/components/Card/utils.tsx +++ b/src/components/Card/utils.tsx @@ -1,9 +1,6 @@ import type { StyleProp, ViewStyle } from 'react-native'; -import color from 'color'; - -import { black, white } from '../../styles/themes/v2/colors'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme, MD3Theme } from '../../types'; type CardMode = 'elevated' | 'outlined' | 'contained'; @@ -20,8 +17,8 @@ export type CardActionChildProps = { export const getCardCoverStyle = ({ theme, - index, - total, + index: _index, + total: _total, borderRadiusStyles, }: { theme: InternalTheme; @@ -29,7 +26,7 @@ export const getCardCoverStyle = ({ index?: number; total?: number; }) => { - const { isV3, roundness } = theme; + const { roundness } = theme; if (Object.keys(borderRadiusStyles).length > 0) { return { @@ -38,43 +35,13 @@ export const getCardCoverStyle = ({ }; } - if (isV3) { - return { - borderRadius: 3 * roundness, - }; - } - - if (index === 0) { - if (total === 1) { - return { - borderRadius: roundness, - }; - } - - return { - borderTopLeftRadius: roundness, - borderTopRightRadius: roundness, - }; - } - - if (typeof total === 'number' && index === total - 1) { - return { - borderBottomLeftRadius: roundness, - }; - } - - return undefined; + return { + borderRadius: 3 * roundness, + }; }; const getBorderColor = ({ theme }: { theme: InternalTheme }) => { - if (theme.isV3) { - return theme.colors.outline; - } - - if (theme.dark) { - return color(white).alpha(0.12).rgb().string(); - } - return color(black).alpha(0.12).rgb().string(); + return (theme as MD3Theme).colors.outline; }; const getBackgroundColor = ({ @@ -84,13 +51,12 @@ const getBackgroundColor = ({ theme: InternalTheme; isMode: (mode: CardMode) => boolean; }) => { - if (theme.isV3) { - if (isMode('contained')) { - return theme.colors.surfaceVariant; - } - if (isMode('outlined')) { - return theme.colors.surface; - } + const { colors } = theme as MD3Theme; + if (isMode('contained')) { + return colors.surfaceVariant; + } + if (isMode('outlined')) { + return colors.surface; } return undefined; }; diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 92ea124e62..869edd674a 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -166,10 +166,8 @@ const CheckboxItem = ({ checkbox = ; } - const textColor = theme.isV3 ? theme.colors.onSurface : theme.colors.text; - const disabledTextColor = theme.isV3 - ? theme.colors.onSurfaceDisabled - : theme.colors.disabled; + const textColor = theme.colors.onSurface; + const disabledTextColor = theme.colors.onSurfaceDisabled; const textAlign = isLeading ? 'right' : 'left'; const computedStyle = { @@ -204,12 +202,7 @@ const CheckboxItem = ({ variant={labelVariant} testID={`${testID}-text`} maxFontSizeMultiplier={labelMaxFontSizeMultiplier} - style={[ - styles.label, - !theme.isV3 && styles.font, - computedStyle, - labelStyle, - ]} + style={[styles.label, computedStyle, labelStyle]} > {label} @@ -238,7 +231,4 @@ const styles = StyleSheet.create({ flexShrink: 1, flexGrow: 1, }, - font: { - fontSize: 16, - }, }); diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index 81677d46b2..4ba3615836 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -13,11 +13,7 @@ const getAndroidCheckedColor = ({ return customColor; } - if (theme.isV3) { - return theme.colors.primary; - } - - return theme.colors.accent; + return theme.colors.primary; }; const getAndroidUncheckedColor = ({ @@ -31,15 +27,7 @@ const getAndroidUncheckedColor = ({ return customUncheckedColor; } - if (theme.isV3) { - return theme.colors.onSurfaceVariant; - } - - if (theme.dark) { - return color(theme.colors.text).alpha(0.7).rgb().string(); - } - - return color(theme.colors.text).alpha(0.54).rgb().string(); + return theme.colors.onSurfaceVariant; }; const getAndroidRippleColor = ({ @@ -52,10 +40,7 @@ const getAndroidRippleColor = ({ disabled?: boolean; }) => { if (disabled) { - if (theme.isV3) { - return color(theme.colors.onSurface).alpha(0.16).rgb().string(); - } - return color(theme.colors.text).alpha(0.16).rgb().string(); + return color(theme.colors.onSurface).alpha(0.16).rgb().string(); } return color(checkedColor).fade(0.32).rgb().string(); @@ -75,10 +60,7 @@ const getAndroidControlColor = ({ disabled?: boolean; }) => { if (disabled) { - if (theme.isV3) { - return theme.colors.onSurfaceDisabled; - } - return theme.colors.disabled; + return theme.colors.onSurfaceDisabled; } if (checked) { @@ -127,21 +109,14 @@ const getIOSCheckedColor = ({ disabled?: boolean; }) => { if (disabled) { - if (theme.isV3) { - return theme.colors.onSurfaceDisabled; - } - return theme.colors.disabled; + return theme.colors.onSurfaceDisabled; } if (customColor) { return customColor; } - if (theme.isV3) { - return theme.colors.primary; - } - - return theme.colors.accent; + return theme.colors.primary; }; const getIOSRippleColor = ({ @@ -154,10 +129,7 @@ const getIOSRippleColor = ({ disabled?: boolean; }) => { if (disabled) { - if (theme.isV3) { - return color(theme.colors.onSurface).alpha(0.16).rgb().string(); - } - return color(theme.colors.text).alpha(0.16).rgb().string(); + return color(theme.colors.onSurface).alpha(0.16).rgb().string(); } return color(checkedColor).fade(0.32).rgb().string(); }; diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 0e48e5c145..3706239b71 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -19,7 +19,7 @@ import useLatestCallback from 'use-latest-callback'; import { ChipAvatarProps, getChipColors } from './helpers'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../styles/themes/v2/colors'; -import type { $Omit, EllipsizeProp, ThemeProp } from '../../types'; +import type { $Omit, EllipsizeProp, MD3Theme, ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; @@ -212,11 +212,11 @@ const Chip = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { isV3, roundness } = theme; + const { roundness } = theme; const isWeb = Platform.OS === 'web'; const { current: elevation } = React.useRef( - new Animated.Value(isV3 && elevated ? 1 : 0) + new Animated.Value(elevated ? 1 : 0) ); const hasPassedTouchHandler = hasTouchHandler({ @@ -232,7 +232,7 @@ const Chip = ({ const { scale } = theme.animation; onPressIn?.(e); Animated.timing(elevation, { - toValue: isV3 ? (elevated ? 2 : 0) : 4, + toValue: elevated ? 2 : 0, duration: 200 * scale, useNativeDriver: isWeb || Platform.constants.reactNativeVersion.minor <= 72, @@ -243,16 +243,16 @@ const Chip = ({ const { scale } = theme.animation; onPressOut?.(e); Animated.timing(elevation, { - toValue: isV3 && elevated ? 1 : 0, + toValue: elevated ? 1 : 0, duration: 150 * scale, useNativeDriver: isWeb || Platform.constants.reactNativeVersion.minor <= 72, }).start(); }); - const opacity = isV3 ? 0.38 : 0.26; - const defaultBorderRadius = roundness * (isV3 ? 2 : 4); - const iconSize = isV3 ? 18 : 16; + const opacity = 0.38; + const defaultBorderRadius = roundness * 2; + const iconSize = 18; const { backgroundColor: customBackgroundColor, @@ -281,8 +281,8 @@ const Chip = ({ disabled, }; - const elevationStyle = isV3 || Platform.OS === 'android' ? elevation : 0; - const multiplier = isV3 ? (compact ? 1.5 : 2) : 1; + const elevationStyle = elevation; + const multiplier = compact ? 1.5 : 2; const labelSpacings = { marginRight: onClose ? 0 : 8 * multiplier, marginLeft: @@ -291,20 +291,17 @@ const Chip = ({ : 8 * multiplier, }; const contentSpacings = { - paddingRight: isV3 ? (onClose ? 34 : 0) : onClose ? 32 : 4, + paddingRight: onClose ? 34 : 0, }; const labelTextStyle = { color: textColor, - ...(isV3 ? theme.fonts.labelLarge : theme.fonts.regular), + ...(theme as MD3Theme).fonts.labelLarge, }; return ( - + {avatar && !icon ? ( @@ -358,12 +353,12 @@ const Chip = ({ - + {closeIcon ? ( ) : ( theme as MD3Theme; export type ChipAvatarProps = { style?: StyleProp; @@ -20,40 +21,25 @@ const getBorderColor = ({ isOutlined, disabled, selectedColor, - backgroundColor, + backgroundColor: _backgroundColor, }: BaseProps & { backgroundColor: string; selectedColor?: string }) => { const isSelectedColor = selectedColor !== undefined; + const { colors } = md3(theme); - if (theme.isV3) { - if (!isOutlined) { - // If the Chip mode is "flat", set border color to transparent - return 'transparent'; - } - - if (disabled) { - return color(theme.colors.onSurfaceVariant).alpha(0.12).rgb().string(); - } - - if (isSelectedColor) { - return color(selectedColor).alpha(0.29).rgb().string(); - } - - return theme.colors.outline; + if (!isOutlined) { + // If the Chip mode is "flat", set border color to transparent + return 'transparent'; } - if (isOutlined) { - if (isSelectedColor) { - return color(selectedColor).alpha(0.29).rgb().string(); - } - - if (theme.dark) { - return color(white).alpha(0.29).rgb().string(); - } + if (disabled) { + return color(colors.onSurfaceVariant).alpha(0.12).rgb().string(); + } - return color(black).alpha(0.29).rgb().string(); + if (isSelectedColor) { + return color(selectedColor).alpha(0.29).rgb().string(); } - return backgroundColor; + return colors.outline; }; const getTextColor = ({ @@ -65,54 +51,32 @@ const getTextColor = ({ selectedColor?: string; }) => { const isSelectedColor = selectedColor !== undefined; - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - if (isSelectedColor) { - return selectedColor; - } - - if (isOutlined) { - return theme.colors.onSurfaceVariant; - } - - return theme.colors.onSecondaryContainer; - } - + const { colors } = md3(theme); if (disabled) { - return theme.colors.disabled; + return colors.onSurfaceDisabled; } if (isSelectedColor) { - return color(selectedColor).alpha(0.87).rgb().string(); + return selectedColor; + } + + if (isOutlined) { + return colors.onSurfaceVariant; } - return color(theme.colors.text).alpha(0.87).rgb().string(); + return colors.onSecondaryContainer; }; const getDefaultBackgroundColor = ({ theme, isOutlined, }: Omit) => { - if (theme.isV3) { - if (isOutlined) { - return theme.colors.surface; - } - - return theme.colors.secondaryContainer; - } - + const { colors } = md3(theme); if (isOutlined) { - return theme.colors?.surface; - } - - if (theme.dark) { - return '#383838'; + return colors.surface; } - return '#ebebeb'; + return colors.secondaryContainer; }; const getBackgroundColor = ({ @@ -123,17 +87,16 @@ const getBackgroundColor = ({ }: BaseProps & { customBackgroundColor?: ColorValue; }) => { + const { colors } = md3(theme); if (typeof customBackgroundColor === 'string') { return customBackgroundColor; } - if (theme.isV3) { - if (disabled) { - if (isOutlined) { - return 'transparent'; - } - return color(theme.colors.onSurfaceVariant).alpha(0.12).rgb().string(); + if (disabled) { + if (isOutlined) { + return 'transparent'; } + return color(colors.onSurfaceVariant).alpha(0.12).rgb().string(); } return getDefaultBackgroundColor({ theme, isOutlined }); @@ -149,6 +112,7 @@ const getSelectedBackgroundColor = ({ customBackgroundColor?: ColorValue; showSelectedOverlay?: boolean; }) => { + const { colors } = md3(theme); const backgroundColor = getBackgroundColor({ theme, disabled, @@ -156,45 +120,30 @@ const getSelectedBackgroundColor = ({ customBackgroundColor, }); - if (theme.isV3) { - if (isOutlined) { - if (showSelectedOverlay) { - return color(backgroundColor) - .mix(color(theme.colors.onSurfaceVariant), 0.12) - .rgb() - .string(); - } - return color(backgroundColor) - .mix(color(theme.colors.onSurfaceVariant), 0) - .rgb() - .string(); - } - + if (isOutlined) { if (showSelectedOverlay) { return color(backgroundColor) - .mix(color(theme.colors.onSecondaryContainer), 0.12) + .mix(color(colors.onSurfaceVariant), 0.12) .rgb() .string(); } - return color(backgroundColor) - .mix(color(theme.colors.onSecondaryContainer), 0) + .mix(color(colors.onSurfaceVariant), 0) .rgb() .string(); } - if (theme.dark) { - if (isOutlined) { - return color(backgroundColor).lighten(0.2).rgb().string(); - } - return color(backgroundColor).lighten(0.4).rgb().string(); - } - - if (isOutlined) { - return color(backgroundColor).darken(0.08).rgb().string(); + if (showSelectedOverlay) { + return color(backgroundColor) + .mix(color(colors.onSecondaryContainer), 0.12) + .rgb() + .string(); } - return color(backgroundColor).darken(0.2).rgb().string(); + return color(backgroundColor) + .mix(color(colors.onSecondaryContainer), 0) + .rgb() + .string(); }; const getIconColor = ({ @@ -206,31 +155,20 @@ const getIconColor = ({ selectedColor?: string; }) => { const isSelectedColor = selectedColor !== undefined; - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - if (isSelectedColor) { - return selectedColor; - } - - if (isOutlined) { - return theme.colors.onSurfaceVariant; - } - - return theme.colors.onSecondaryContainer; - } - + const { colors } = md3(theme); if (disabled) { - return theme.colors.disabled; + return colors.onSurfaceDisabled; } if (isSelectedColor) { - return color(selectedColor).alpha(0.54).rgb().string(); + return selectedColor; + } + + if (isOutlined) { + return colors.onSurfaceVariant; } - return color(theme.colors.text).alpha(0.54).rgb().string(); + return colors.onSecondaryContainer; }; const getRippleColor = ({ @@ -238,7 +176,7 @@ const getRippleColor = ({ isOutlined, disabled, selectedColor, - selectedBackgroundColor, + selectedBackgroundColor: _selectedBackgroundColor, customRippleColor, }: BaseProps & { selectedBackgroundColor: string; @@ -257,19 +195,11 @@ const getRippleColor = ({ isOutlined, }); - if (theme.isV3) { - if (isSelectedColor) { - return color(selectedColor).alpha(0.12).rgb().string(); - } - - return color(textColor).alpha(0.12).rgb().string(); - } - if (isSelectedColor) { - return color(selectedColor).fade(0.5).rgb().string(); + return color(selectedColor).alpha(0.12).rgb().string(); } - return selectedBackgroundColor; + return color(textColor).alpha(0.12).rgb().string(); }; export const getChipColors = ({ diff --git a/src/components/DataTable/DataTableHeader.tsx b/src/components/DataTable/DataTableHeader.tsx index a4619ebb99..1fee2a02ab 100644 --- a/src/components/DataTable/DataTableHeader.tsx +++ b/src/components/DataTable/DataTableHeader.tsx @@ -1,10 +1,7 @@ import * as React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; -import color from 'color'; - import { useInternalTheme } from '../../core/theming'; -import { black, white } from '../../styles/themes/v2/colors'; import type { ThemeProp } from '../../types'; export type Props = React.ComponentPropsWithRef & { @@ -52,12 +49,7 @@ const DataTableHeader = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const borderBottomColor = theme.isV3 - ? theme.colors.surfaceVariant - : color(theme.dark ? white : black) - .alpha(0.12) - .rgb() - .string(); + const borderBottomColor = theme.colors.surfaceVariant; return ( diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index dd72dbe54f..c7a5375f82 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -108,7 +108,7 @@ const PaginationControls = ({ }: PaginationControlsProps) => { const theme = useInternalTheme(themeOverrides); - const textColor = theme.isV3 ? theme.colors.onSurface : theme.colors.text; + const textColor = theme.colors.onSurface; return ( <> @@ -310,12 +310,7 @@ const DataTablePagination = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const labelColor = color( - theme.isV3 ? theme.colors.onSurface : theme?.colors.text - ) - .alpha(0.6) - .rgb() - .string(); + const labelColor = color(theme.colors.onSurface).alpha(0.6).rgb().string(); return ( { const theme = useInternalTheme(themeOverrides); - const borderBottomColor = theme.isV3 - ? theme.colors.surfaceVariant - : color(theme.dark ? white : black) - .alpha(0.12) - .rgb() - .string(); + const borderBottomColor = theme.colors.surfaceVariant; return ( { const { right, left } = useSafeAreaInsets(); const theme = useInternalTheme(themeOverrides); - const { isV3, dark, mode, colors, roundness } = theme; - const borderRadius = (isV3 ? 7 : 1) * roundness; + const { roundness, colors } = theme as MD3Theme; + const borderRadius = 7 * roundness; - const backgroundColorV2 = - dark && mode === 'adaptive' - ? overlay(DIALOG_ELEVATION, colors?.surface) - : colors?.surface; - const backgroundColor = isV3 - ? theme.colors.elevation.level3 - : backgroundColorV2; + const backgroundColor = colors.elevation.level3; return ( child != null && typeof child !== 'boolean') .map((child, i) => { - if (isV3) { - if (i === 0 && React.isValidElement(child)) { - return React.cloneElement(child, { - style: [{ marginTop: 24 }, child.props.style], - }); - } - } - - if ( - i === 0 && - React.isValidElement(child) && - child.type === DialogContent - ) { - // Dialog content is the first item, so we add a top padding + if (i === 0 && React.isValidElement(child)) { return React.cloneElement(child, { - style: [{ paddingTop: 24 }, child.props.style], + style: [{ marginTop: 24 }, child.props.style], }); } diff --git a/src/components/Dialog/DialogActions.tsx b/src/components/Dialog/DialogActions.tsx index cabebd2fe2..7b34739da5 100644 --- a/src/components/Dialog/DialogActions.tsx +++ b/src/components/Dialog/DialogActions.tsx @@ -47,21 +47,18 @@ export type Props = React.ComponentPropsWithRef & { * ``` */ const DialogActions = (props: Props) => { - const { isV3 } = useInternalTheme(props.theme); + useInternalTheme(props.theme); const actionsLength = React.Children.toArray(props.children).length; return ( - + {React.Children.map(props.children, (child, i) => React.isValidElement(child) ? React.cloneElement(child, { compact: true, - uppercase: !isV3, + uppercase: false, style: [ - isV3 && { + { marginRight: i + 1 === actionsLength ? 0 : 8, }, child.props.style, @@ -76,12 +73,6 @@ const DialogActions = (props: Props) => { DialogActions.displayName = 'Dialog.Actions'; const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'flex-end', - padding: 8, - }, v3Container: { flexDirection: 'row', flexGrow: 1, diff --git a/src/components/Dialog/DialogIcon.tsx b/src/components/Dialog/DialogIcon.tsx index ad5841b93a..7682054003 100644 --- a/src/components/Dialog/DialogIcon.tsx +++ b/src/components/Dialog/DialogIcon.tsx @@ -4,6 +4,7 @@ import { StyleSheet, View } from 'react-native'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; +import type { MD3Theme } from '../../types'; import Icon, { IconSource } from '../Icon'; export type Props = { @@ -69,13 +70,10 @@ const DialogIcon = ({ theme: themeOverrides, }: Props) => { const theme = useInternalTheme(themeOverrides); - - if (!theme.isV3) { - return null; - } + const { colors } = theme as MD3Theme; //@ts-ignore - const iconColor = color || theme.colors.secondary; + const iconColor = color || colors.secondary; return ( diff --git a/src/components/Dialog/DialogScrollArea.tsx b/src/components/Dialog/DialogScrollArea.tsx index 66fdcef6a0..5deeb4a5e6 100644 --- a/src/components/Dialog/DialogScrollArea.tsx +++ b/src/components/Dialog/DialogScrollArea.tsx @@ -4,6 +4,7 @@ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; +import type { MD3Theme } from '../../types'; export type Props = React.ComponentPropsWithRef & { /** @@ -50,22 +51,16 @@ export type Props = React.ComponentPropsWithRef & { */ const DialogScrollArea = (props: Props) => { const theme = useInternalTheme(props.theme); + const { colors } = theme as MD3Theme; const borderStyles = { - borderColor: theme.isV3 - ? theme.colors.surfaceVariant - : 'rgba(0, 0, 0, .12)', - borderTopWidth: theme.isV3 ? 1 : StyleSheet.hairlineWidth, - borderBottomWidth: theme.isV3 ? 1 : StyleSheet.hairlineWidth, + borderColor: colors.surfaceVariant, + borderTopWidth: 1, + borderBottomWidth: 1, }; return ( {props.children} diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index 9953fc506c..407acb68b3 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -2,11 +2,10 @@ import * as React from 'react'; import { StyleProp, StyleSheet, TextStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { MD3Theme, ThemeProp } from '../../types'; import Text from '../Typography/Text'; -import Title from '../Typography/v2/Title'; -export type Props = React.ComponentPropsWithRef & { +export type Props = React.ComponentPropsWithRef & { /** * Title text for the `DialogTitle`. */ @@ -53,24 +52,22 @@ const DialogTitle = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { isV3, colors, fonts } = theme; - - const TextComponent = isV3 ? Text : Title; + const { colors, fonts } = theme as MD3Theme; const headerTextStyle = { - color: isV3 ? colors.onSurface : colors?.text, - ...(isV3 ? fonts.headlineSmall : {}), + color: colors.onSurface, + ...fonts.headlineSmall, }; return ( - {children} - + ); }; diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index be30d94c7c..c697a2a04a 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -1,10 +1,7 @@ import * as React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; -import color from 'color'; - import { useInternalTheme } from '../core/theming'; -import { black, white } from '../styles/themes/v2/colors'; import type { $RemoveChildren, ThemeProp } from '../types'; export type Props = $RemoveChildren & { @@ -60,23 +57,17 @@ const Divider = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { dark: isDarkTheme, isV3 } = theme; - const dividerColor = isV3 - ? theme.colors.outlineVariant - : color(isDarkTheme ? white : black) - .alpha(0.12) - .rgb() - .string(); + const dividerColor = theme.colors.outlineVariant; return ( @@ -84,9 +75,6 @@ const Divider = ({ }; const styles = StyleSheet.create({ - leftInset: { - marginLeft: 72, - }, v3LeftInset: { marginLeft: 16, }, diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index 9fb461e9c5..c10c7ac0b6 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -111,7 +111,6 @@ const DrawerCollapsedItem = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { isV3 } = theme; const { scale } = theme.animation; const [numOfLines, setNumOfLines] = React.useState(1); @@ -126,10 +125,6 @@ const DrawerCollapsedItem = ({ } }, [animScale, active]); - if (!isV3) { - return null; - } - const handlePressOut = () => { Animated.timing(animScale, { toValue: 1, @@ -163,7 +158,7 @@ const DrawerCollapsedItem = ({ const labelTextStyle = { color: labelColor, - ...(isV3 ? theme.fonts.labelMedium : {}), + ...theme.fonts.labelMedium, }; const icon = diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 604df1b83b..7af3d9fcf4 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -108,27 +108,17 @@ const DrawerItem = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { roundness, isV3 } = theme; + const { roundness } = theme; - const backgroundColor = active - ? isV3 - ? theme.colors.secondaryContainer - : color(theme.colors.primary).alpha(0.12).rgb().string() - : undefined; + const backgroundColor = active ? theme.colors.secondaryContainer : undefined; const contentColor = active - ? isV3 - ? theme.colors.onSecondaryContainer - : theme.colors.primary - : isV3 - ? theme.colors.onSurfaceVariant - : color(theme.colors.text).alpha(0.68).rgb().string(); + ? theme.colors.onSecondaryContainer + : theme.colors.onSurfaceVariant; - const labelMargin = icon ? (isV3 ? 12 : 32) : 0; - const borderRadius = (isV3 ? 7 : 1) * roundness; - const rippleColor = isV3 - ? color(contentColor).alpha(0.12).rgb().string() - : undefined; - const font = isV3 ? theme.fonts.labelLarge : theme.fonts.medium; + const labelMargin = icon ? 12 : 0; + const borderRadius = 7 * roundness; + const rippleColor = color(contentColor).alpha(0.12).rgb().string(); + const font = theme.fonts.labelLarge; return ( @@ -139,8 +129,8 @@ const DrawerItem = ({ onPress={onPress} style={[ styles.container, + styles.v3Container, { backgroundColor, borderRadius }, - isV3 && styles.v3Container, style, ]} accessibilityRole="button" @@ -150,7 +140,7 @@ const DrawerItem = ({ theme={theme} hitSlop={hitSlop} > - + {icon ? ( diff --git a/src/components/Drawer/DrawerSection.tsx b/src/components/Drawer/DrawerSection.tsx index ed79c5e054..a701d38ad6 100644 --- a/src/components/Drawer/DrawerSection.tsx +++ b/src/components/Drawer/DrawerSection.tsx @@ -1,8 +1,6 @@ import * as React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; -import color from 'color'; - import { useInternalTheme } from '../../core/theming'; import { MD3Colors } from '../../styles/themes/v3/tokens'; import type { ThemeProp } from '../../types'; @@ -73,17 +71,14 @@ const DrawerSection = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { isV3 } = theme; - const titleColor = isV3 - ? theme.colors.onSurfaceVariant - : color(theme.colors.text).alpha(0.54).rgb().string(); - const titleMargin = isV3 ? 28 : 16; - const font = isV3 ? theme.fonts.titleSmall : theme.fonts.medium; + const titleColor = theme.colors.onSurfaceVariant; + const titleMargin = 28; + const font = theme.fonts.titleSmall; return ( {title && ( - + {title && ( )} diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index ecfe7e1c60..6b6964a4a9 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -129,7 +129,6 @@ export type Props = $Omit<$RemoveChildren, 'mode'> & { }; const SIZE = 56; -const SCALE = 0.9; /** * An animated, extending horizontally floating action button represents the primary action in an application. @@ -233,7 +232,7 @@ const AnimatedFAB = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const uppercase: boolean = uppercaseProp ?? !theme.isV3; + const uppercase: boolean = uppercaseProp ?? false; const isIOS = Platform.OS === 'ios'; const isWeb = Platform.OS === 'web'; const isAnimatedFromRight = animateFrom === 'right'; @@ -246,7 +245,7 @@ const AnimatedFAB = ({ const { current: animFAB } = React.useRef( new Animated.Value(0) ); - const { isV3, animation } = theme; + const { animation } = theme; const { scale } = animation; const labelSize = isWeb ? getLabelSizeWeb(labelRef) : null; @@ -257,7 +256,7 @@ const AnimatedFAB = ({ labelSize?.height ?? 0 ); - const borderRadius = SIZE / (isV3 ? 3.5 : 2); + const borderRadius = SIZE / 3.5; React.useEffect(() => { if (!isWeb) { @@ -364,17 +363,16 @@ const AnimatedFAB = ({ animFAB, }); - const font = isV3 ? theme.fonts.labelLarge : theme.fonts.medium; + const font = theme.fonts.labelLarge; const textStyle = { color: foregroundColor, ...font, }; - const md2Elevation = disabled || !isIOS ? 0 : 6; const md3Elevation = disabled || !isIOS ? 0 : 3; - const shadowStyle = isV3 ? styles.v3Shadow : styles.shadow; + const shadowStyle = styles.v3Shadow; const baseStyle = [ StyleSheet.absoluteFill, disabled ? styles.disabled : shadowStyle, @@ -396,32 +394,14 @@ const AnimatedFAB = ({ ], borderRadius, }, - !isV3 && { - elevation: md2Elevation, - }, styles.container, restStyle, ]} - {...(isV3 && { elevation: md3Elevation })} + elevation={md3Elevation} theme={theme} container > - + ( ref ) => { const theme = useInternalTheme(themeOverrides); - const uppercase = uppercaseProp ?? !theme.isV3; + const uppercase = uppercaseProp ?? false; const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) ); - const { isV3, animation } = theme; + const { animation } = theme; const { scale } = animation; React.useEffect(() => { @@ -255,7 +255,7 @@ const FAB = forwardRef( const isFlatMode = mode === 'flat'; const iconSize = isLargeSize ? 36 : 24; const loadingIndicatorSize = isLargeSize ? 24 : 18; - const font = isV3 ? theme.fonts.labelLarge : theme.fonts.medium; + const font = theme.fonts.labelLarge; const extendedStyle = getExtendedFabStyle({ customSize, theme }); const textStyle = { @@ -282,13 +282,11 @@ const FAB = forwardRef( }, ], }, - !isV3 && styles.elevated, - !isV3 && disabled && styles.disabled, style, ]} pointerEvents={visible ? 'auto' : 'none'} testID={`${testID}-container`} - {...(isV3 && { elevation: md3Elevation })} + elevation={md3Elevation} container > ( ); const styles = StyleSheet.create({ - elevated: { - elevation: 6, - }, content: { flexDirection: 'row', alignItems: 'center', @@ -361,9 +356,6 @@ const styles = StyleSheet.create({ uppercaseLabel: { textTransform: 'uppercase', }, - disabled: { - elevation: 0, - }, }); export default FAB; diff --git a/src/components/FAB/FABGroup.tsx b/src/components/FAB/FABGroup.tsx index cb66cf08e0..ec2cc85f68 100644 --- a/src/components/FAB/FABGroup.tsx +++ b/src/components/FAB/FABGroup.tsx @@ -250,7 +250,6 @@ const FABGroup = ({ >(null); const { scale } = theme.animation; - const { isV3 } = theme; React.useEffect(() => { if (open) { @@ -262,7 +261,7 @@ const FABGroup = ({ useNativeDriver: true, }), Animated.stagger( - isV3 ? 15 : 50 * scale, + 15, animations.current .map((animation) => Animated.timing(animation, { @@ -294,7 +293,7 @@ const FABGroup = ({ } }); } - }, [open, actions, backdrop, scale, isV3]); + }, [open, actions, backdrop, scale]); const close = () => onStateChange({ open: false }); const toggle = () => onStateChange({ open: !open }); @@ -326,14 +325,6 @@ const FABGroup = ({ : backdrop; const opacities = animations.current; - const scales = opacities.map((opacity) => - open - ? opacity.interpolate({ - inputRange: [0, 1], - outputRange: [0.5, 1], - }) - : 1 - ); const translations = opacities.map((opacity) => open @@ -395,7 +386,7 @@ const FABGroup = ({ {actions.map((it, i) => { const labelTextStyle = { color: it.labelTextColor ?? labelColor, - ...(isV3 ? theme.fonts.titleMedium : {}), + ...theme.fonts.titleMedium, }; const marginHorizontal = typeof it.size === 'undefined' || it.size === 'small' ? 24 : 16; @@ -430,7 +421,7 @@ const FABGroup = ({ {it.label && ( @@ -467,11 +454,10 @@ const FABGroup = ({ color={it.color} style={[ { - transform: [{ scale: scales[i] }], + transform: [{ translateY: translations[i] }], opacity: opacities[i], backgroundColor: stackedFABBackgroundColor, }, - isV3 && { transform: [{ translateY: translations[i] }] }, it.style, ]} accessibilityElementsHidden={true} diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index e87d24019f..42dd05ff7e 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -9,9 +9,7 @@ import { import color from 'color'; -import { black, white } from '../../styles/themes/v2/colors'; import type { InternalTheme } from '../../types'; -import getContrastingColor from '../../utils/getContrastingColor'; type GetCombinedStylesProps = { isAnimatedFromRight: boolean; @@ -174,88 +172,60 @@ const getBackgroundColor = ({ return customBackgroundColor; } - if (theme.isV3) { - if (disabled) { - return theme.colors.surfaceDisabled; - } - - if (isVariant('primary')) { - return theme.colors.primaryContainer; - } + if (disabled) { + return theme.colors.surfaceDisabled; + } - if (isVariant('secondary')) { - return theme.colors.secondaryContainer; - } + if (isVariant('primary')) { + return theme.colors.primaryContainer; + } - if (isVariant('tertiary')) { - return theme.colors.tertiaryContainer; - } + if (isVariant('secondary')) { + return theme.colors.secondaryContainer; + } - if (isVariant('surface')) { - return theme.colors.elevation.level3; - } + if (isVariant('tertiary')) { + return theme.colors.tertiaryContainer; } - if (disabled) { - if (theme.dark) { - return color(white).alpha(0.12).rgb().string(); - } - return color(black).alpha(0.12).rgb().string(); + if (isVariant('surface')) { + return theme.colors.elevation.level3; } - //@ts-ignore - return theme.colors?.accent; + return theme.colors.primaryContainer; }; const getForegroundColor = ({ theme, isVariant, disabled, - backgroundColor, customColor, -}: BaseProps & { backgroundColor: string; customColor?: string }) => { +}: BaseProps & { customColor?: string }) => { if (typeof customColor !== 'undefined' && !disabled) { return customColor; } - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - if (isVariant('primary')) { - return theme.colors.onPrimaryContainer; - } - - if (isVariant('secondary')) { - return theme.colors.onSecondaryContainer; - } + if (disabled) { + return theme.colors.onSurfaceDisabled; + } - if (isVariant('tertiary')) { - return theme.colors.onTertiaryContainer; - } + if (isVariant('primary')) { + return theme.colors.onPrimaryContainer; + } - if (isVariant('surface')) { - return theme.colors.primary; - } + if (isVariant('secondary')) { + return theme.colors.onSecondaryContainer; } - if (disabled) { - if (theme.dark) { - return color(white).alpha(0.32).rgb().string(); - } - return color(black).alpha(0.32).rgb().string(); + if (isVariant('tertiary')) { + return theme.colors.onTertiaryContainer; } - if (backgroundColor) { - return getContrastingColor( - backgroundColor || white, - white, - 'rgba(0, 0, 0, .54)' - ); + if (isVariant('surface')) { + return theme.colors.primary; } - return getContrastingColor(white, white, 'rgba(0, 0, 0, .54)'); + return theme.colors.onPrimaryContainer; }; export const getFABColors = ({ @@ -287,7 +257,6 @@ export const getFABColors = ({ const foregroundColor = getForegroundColor({ ...baseFABColorProps, customColor, - backgroundColor, }); return { @@ -299,15 +268,7 @@ export const getFABColors = ({ }; const getLabelColor = ({ theme }: { theme: InternalTheme }) => { - if (theme.isV3) { - return theme.colors.onSurface; - } - - if (theme.dark) { - return theme.colors.text; - } - - return color(theme.colors.text).fade(0.54).rgb().string(); + return theme.colors.onSurface; }; const getBackdropColor = ({ @@ -320,17 +281,11 @@ const getBackdropColor = ({ if (customBackdropColor) { return customBackdropColor; } - if (theme.isV3) { - return color(theme.colors.background).alpha(0.95).rgb().string(); - } - return theme.colors?.backdrop; + return color(theme.colors.background).alpha(0.95).rgb().string(); }; const getStackedFABBackgroundColor = ({ theme }: { theme: InternalTheme }) => { - if (theme.isV3) { - return theme.colors.elevation.level3; - } - return theme.colors.surface; + return theme.colors.elevation.level3; }; export const getFABGroupColors = ({ @@ -347,16 +302,6 @@ export const getFABGroupColors = ({ }; }; -const standardSize = { - height: 56, - width: 56, - borderRadius: 28, -}; -const smallSize = { - height: 40, - width: 40, - borderRadius: 28, -}; const v3SmallSize = { height: 40, width: 40, @@ -385,30 +330,18 @@ export const getFabStyle = ({ size: 'small' | 'medium' | 'large'; theme: InternalTheme; }) => { - const { isV3, roundness } = theme; + const { roundness } = theme; if (customSize) return getCustomFabSize(customSize, roundness); - if (isV3) { - switch (size) { - case 'small': - return { ...v3SmallSize, borderRadius: 3 * roundness }; - case 'medium': - return { ...v3MediumSize, borderRadius: 4 * roundness }; - case 'large': - return { ...v3LargeSize, borderRadius: 7 * roundness }; - } - } - - if (size === 'small') { - return smallSize; + switch (size) { + case 'small': + return { ...v3SmallSize, borderRadius: 3 * roundness }; + case 'medium': + return { ...v3MediumSize, borderRadius: 4 * roundness }; + case 'large': + return { ...v3LargeSize, borderRadius: 7 * roundness }; } - return standardSize; -}; - -const extended = { - height: 48, - paddingHorizontal: 16, }; const v3Extended = { @@ -424,16 +357,14 @@ const getExtendedFabDimensions = (customSize: number) => ({ export const getExtendedFabStyle = ({ customSize, - theme, + theme: _theme, }: { customSize?: number; theme: InternalTheme; }) => { if (customSize) return getExtendedFabDimensions(customSize); - const { isV3 } = theme; - - return isV3 ? v3Extended : extended; + return v3Extended; }; let cachedContext: CanvasRenderingContext2D | null = null; diff --git a/src/components/HelperText/utils.ts b/src/components/HelperText/utils.ts index 4134175416..ab9d2dde8e 100644 --- a/src/components/HelperText/utils.ts +++ b/src/components/HelperText/utils.ts @@ -1,5 +1,3 @@ -import color from 'color'; - import type { InternalTheme } from '../../types'; type BaseProps = { @@ -9,22 +7,14 @@ type BaseProps = { }; export function getTextColor({ theme, disabled, type }: BaseProps) { - const { colors, dark } = theme; + const { colors } = theme; if (type === 'error') { return colors?.error; } - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } else { - return theme.colors.onSurfaceVariant; - } + if (disabled) { + return theme.colors.onSurfaceDisabled; } - - return color(theme?.colors?.text) - .alpha(dark ? 0.7 : 0.54) - .rgb() - .string(); + return theme.colors.onSurfaceVariant; } diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index b3d26c699d..b886e06fe2 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -122,8 +122,7 @@ const Icon = ({ typeof source === 'object' && source.direction && source.source ? source.source : source; - const iconColor = - color || (theme.isV3 ? theme.colors.onSurface : theme.colors.text); + const iconColor = color || theme.colors.onSurface; if (isImageSource(s)) { return ( diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 47a87901ca..2da6e12fcf 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -138,7 +138,6 @@ const IconButton = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); - const { isV3 } = theme; const IconComponent = animated ? CrossFadeIcon : Icon; @@ -153,10 +152,10 @@ const IconButton = forwardRef( customRippleColor, }); - const buttonSize = isV3 ? size + 2 * PADDING : size * 1.5; + const buttonSize = size + 2 * PADDING; const { - borderWidth = isV3 && mode === 'outlined' && !selected ? 1 : 0, + borderWidth = mode === 'outlined' && !selected ? 1 : 0, borderRadius = buttonSize / 2, } = (StyleSheet.flatten(style) || {}) as ViewStyle; @@ -178,11 +177,10 @@ const IconButton = forwardRef( }, styles.container, borderStyles, - !isV3 && disabled && styles.disabled, style, ]} container - {...(isV3 && { elevation: 0 })} + elevation={0} > { - if (theme.isV3) { - if (disabled) { - return theme.colors.surfaceDisabled; - } - - return theme.colors.outline; + if (disabled) { + return theme.colors.surfaceDisabled; } - return undefined; + return theme.colors.outline; }; const getBackgroundColor = ({ @@ -38,40 +34,34 @@ const getBackgroundColor = ({ selected, customContainerColor, }: BaseProps & { customContainerColor?: string }) => { - if (theme.isV3) { - if (disabled) { - if (isMode('contained') || isMode('contained-tonal')) { - return theme.colors.surfaceDisabled; - } - } - - if (typeof customContainerColor !== 'undefined') { - return customContainerColor; + if (disabled) { + if (isMode('contained') || isMode('contained-tonal')) { + return theme.colors.surfaceDisabled; } + } - if (isMode('contained')) { - if (selected) { - return theme.colors.primary; - } - return theme.colors.surfaceVariant; - } + if (typeof customContainerColor !== 'undefined') { + return customContainerColor; + } - if (isMode('contained-tonal')) { - if (selected) { - return theme.colors.secondaryContainer; - } - return theme.colors.surfaceVariant; + if (isMode('contained')) { + if (selected) { + return theme.colors.primary; } + return theme.colors.surfaceVariant; + } - if (isMode('outlined')) { - if (selected) { - return theme.colors.inverseSurface; - } + if (isMode('contained-tonal')) { + if (selected) { + return theme.colors.secondaryContainer; } + return theme.colors.surfaceVariant; } - if (typeof customContainerColor !== 'undefined') { - return customContainerColor; + if (isMode('outlined')) { + if (selected) { + return theme.colors.inverseSurface; + } } return undefined; @@ -84,65 +74,52 @@ const getIconColor = ({ selected, customIconColor, }: BaseProps & { customIconColor?: string }) => { - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - if (typeof customIconColor !== 'undefined') { - return customIconColor; - } + if (disabled) { + return theme.colors.onSurfaceDisabled; + } - if (isMode('contained')) { - if (selected) { - return theme.colors.onPrimary; - } - return theme.colors.primary; - } + if (typeof customIconColor !== 'undefined') { + return customIconColor; + } - if (isMode('contained-tonal')) { - if (selected) { - return theme.colors.onSecondaryContainer; - } - return theme.colors.onSurfaceVariant; + if (isMode('contained')) { + if (selected) { + return theme.colors.onPrimary; } + return theme.colors.primary; + } - if (isMode('outlined')) { - if (selected) { - return theme.colors.inverseOnSurface; - } - return theme.colors.onSurfaceVariant; + if (isMode('contained-tonal')) { + if (selected) { + return theme.colors.onSecondaryContainer; } + return theme.colors.onSurfaceVariant; + } + if (isMode('outlined')) { if (selected) { - return theme.colors.primary; + return theme.colors.inverseOnSurface; } return theme.colors.onSurfaceVariant; } - if (typeof customIconColor !== 'undefined') { - return customIconColor; + if (selected) { + return theme.colors.primary; } - - return theme.colors.text; + return theme.colors.onSurfaceVariant; }; const getRippleColor = ({ - theme, iconColor, customRippleColor, }: { - theme: InternalTheme; iconColor: string; customRippleColor?: ColorValue; }) => { if (customRippleColor) { return customRippleColor; } - if (theme.isV3) { - return color(iconColor).alpha(0.12).rgb().string(); - } - return color(iconColor).alpha(0.32).rgb().string(); + return color(iconColor).alpha(0.12).rgb().string(); }; export const getIconButtonColor = ({ @@ -184,7 +161,7 @@ export const getIconButtonColor = ({ ...baseIconColorProps, customContainerColor, }), - rippleColor: getRippleColor({ theme, iconColor, customRippleColor }), + rippleColor: getRippleColor({ iconColor, customRippleColor }), borderColor: getBorderColor({ theme, disabled }), }; }; diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 75505c7367..ace8810a71 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -212,9 +212,6 @@ const ListAccordion = ({ const onDescriptionTextLayout = ( event: NativeSyntheticEvent ) => { - if (!theme.isV3) { - return; - } const { nativeEvent } = event; setAlignToTop(nativeEvent.lines.length >= 2); }; @@ -241,12 +238,11 @@ const ListAccordion = ({ ? groupContext.expandedId === id : expandedInternal; - const { titleColor, descriptionColor, titleTextColor, rippleColor } = - getAccordionColors({ - theme, - isExpanded, - customRippleColor, - }); + const { descriptionColor, titleTextColor, rippleColor } = getAccordionColors({ + theme, + isExpanded, + customRippleColor, + }); const handlePress = groupContext && id !== undefined @@ -256,7 +252,7 @@ const ListAccordion = ({ {left ? left({ color: isExpanded ? theme.colors?.primary : descriptionColor, - style: getLeftStyles(alignToTop, description, theme.isV3), + style: getLeftStyles(alignToTop, description), }) : null} - + {right ? ( right({ @@ -329,7 +322,7 @@ const ListAccordion = ({ ) : ( @@ -348,10 +341,7 @@ const ListAccordion = ({ !child.props.right ) { return React.cloneElement(child, { - style: [ - theme.isV3 ? styles.childV3 : styles.child, - child.props.style, - ], + style: [styles.child, child.props.style], theme, }); } @@ -367,17 +357,10 @@ ListAccordion.displayName = 'List.Accordion'; const styles = StyleSheet.create({ container: { - padding: 8, - }, - containerV3: { paddingVertical: 8, paddingRight: 24, }, row: { - flexDirection: 'row', - alignItems: 'center', - }, - rowV3: { flexDirection: 'row', marginVertical: 6, }, @@ -392,17 +375,14 @@ const styles = StyleSheet.create({ description: { fontSize: 14, }, - item: { + contentItem: { + paddingLeft: 16, + }, + trailingItem: { marginVertical: 6, paddingLeft: 8, }, - itemV3: { - paddingLeft: 16, - }, child: { - paddingLeft: 64, - }, - childV3: { paddingLeft: 40, }, content: { diff --git a/src/components/List/ListIcon.tsx b/src/components/List/ListIcon.tsx index f1c7194c43..108d642383 100644 --- a/src/components/List/ListIcon.tsx +++ b/src/components/List/ListIcon.tsx @@ -51,10 +51,7 @@ const ListIcon = ({ const theme = useInternalTheme(themeOverrides); return ( - + ); @@ -62,13 +59,6 @@ const ListIcon = ({ const styles = StyleSheet.create({ item: { - margin: 8, - height: 40, - width: 40, - alignItems: 'center', - justifyContent: 'center', - }, - itemV3: { alignItems: 'center', justifyContent: 'center', }, diff --git a/src/components/List/ListImage.tsx b/src/components/List/ListImage.tsx index 9839be7b02..8eb887bddc 100644 --- a/src/components/List/ListImage.tsx +++ b/src/components/List/ListImage.tsx @@ -7,7 +7,6 @@ import { ImageStyle, } from 'react-native'; -import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; export type Props = { @@ -42,16 +41,11 @@ const ListImage = ({ style, source, variant = 'image', - theme: themeOverrides, + theme: _theme, }: Props) => { - const theme = useInternalTheme(themeOverrides); const getStyles = () => { if (variant === 'video') { - if (!theme.isV3) { - return [style, styles.video]; - } - - return [style, styles.videoV3]; + return [style, styles.video]; } return [style, styles.image]; @@ -73,11 +67,6 @@ const styles = StyleSheet.create({ height: 56, }, video: { - width: 100, - height: 64, - marginLeft: 0, - }, - videoV3: { width: 114, height: 64, marginLeft: 0, diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 467eb9b67a..c50d075744 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -10,8 +10,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; - import { Style, getLeftStyles, getRightStyles } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, EllipsizeProp, ThemeProp } from '../../types'; @@ -169,9 +167,6 @@ const ListItem = ( const onDescriptionTextLayout = ( event: NativeSyntheticEvent ) => { - if (!theme.isV3) { - return; - } const { nativeEvent } = event; setAlignToTop(nativeEvent.lines.length >= 2); }; @@ -206,9 +201,7 @@ const ListItem = ( }; const renderTitle = () => { - const titleColor = theme.isV3 - ? theme.colors.onSurface - : color(theme.colors.text).alpha(0.87).rgb().string(); + const titleColor = theme.colors.onSurface; return typeof title === 'function' ? ( title({ @@ -230,32 +223,26 @@ const ListItem = ( ); }; - const descriptionColor = theme.isV3 - ? theme.colors.onSurfaceVariant - : color(theme.colors.text).alpha(0.54).rgb().string(); + const descriptionColor = theme.colors.onSurfaceVariant; return ( - + {left ? left({ color: descriptionColor, - style: getLeftStyles(alignToTop, description, theme.isV3), + style: getLeftStyles(alignToTop, description), }) : null} {renderTitle()} @@ -267,7 +254,7 @@ const ListItem = ( {right ? right({ color: descriptionColor, - style: getRightStyles(alignToTop, description, theme.isV3), + style: getRightStyles(alignToTop, description), }) : null} @@ -280,19 +267,12 @@ const Component = forwardRef(ListItem); const styles = StyleSheet.create({ container: { - padding: 8, - }, - containerV3: { paddingVertical: 8, paddingRight: 24, }, row: { width: '100%', flexDirection: 'row', - }, - rowV3: { - width: '100%', - flexDirection: 'row', marginVertical: 6, }, title: { @@ -302,10 +282,6 @@ const styles = StyleSheet.create({ fontSize: 14, }, item: { - marginVertical: 6, - paddingLeft: 8, - }, - itemV3: { paddingLeft: 16, }, content: { diff --git a/src/components/List/ListSubheader.tsx b/src/components/List/ListSubheader.tsx index 038b86a86e..38e0a9d41f 100644 --- a/src/components/List/ListSubheader.tsx +++ b/src/components/List/ListSubheader.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { StyleProp, StyleSheet, TextStyle } from 'react-native'; -import color from 'color'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; @@ -43,11 +42,9 @@ const ListSubheader = ({ }: Props) => { const theme = useInternalTheme(overrideTheme); - const textColor = theme.isV3 - ? theme.colors.onSurfaceVariant - : color(theme.colors.text).alpha(0.54).rgb().string(); + const textColor = theme.colors.onSurfaceVariant; - const font = theme.isV3 ? theme.fonts.bodyMedium : theme.fonts.medium; + const font = theme.fonts.bodyMedium; return ( { - const stylesV3 = { - marginRight: 0, - marginLeft: 16, + const stylesV3: Style = { + ...stylesV3Left, alignSelf: alignToTop ? 'flex-start' : 'center', }; @@ -47,14 +48,10 @@ export const getLeftStyles = ( return { ...styles.iconMarginLeft, ...styles.marginVerticalNone, - ...(isV3 && { ...stylesV3 }), + ...stylesV3, }; } - if (!isV3) { - return styles.iconMarginLeft; - } - return { ...styles.iconMarginLeft, ...stylesV3, @@ -63,11 +60,10 @@ export const getLeftStyles = ( export const getRightStyles = ( alignToTop: boolean, - description: Description, - isV3: boolean + description: Description ) => { - const stylesV3 = { - marginLeft: 16, + const stylesV3: Style = { + ...stylesV3Right, alignSelf: alignToTop ? 'flex-start' : 'center', }; @@ -75,14 +71,10 @@ export const getRightStyles = ( return { ...styles.iconMarginRight, ...styles.marginVerticalNone, - ...(isV3 && { ...stylesV3 }), + ...stylesV3, }; } - if (!isV3) { - return styles.iconMarginRight; - } - return { ...styles.iconMarginRight, ...stylesV3, @@ -104,13 +96,9 @@ export const getAccordionColors = ({ isExpanded?: boolean; customRippleColor?: ColorValue; }) => { - const titleColor = theme.isV3 - ? theme.colors.onSurface - : color(theme.colors.text).alpha(0.87).rgb().string(); + const titleColor = theme.colors.onSurface; - const descriptionColor = theme.isV3 - ? theme.colors.onSurfaceVariant - : color(theme.colors.text).alpha(0.54).rgb().string(); + const descriptionColor = theme.colors.onSurfaceVariant; const titleTextColor = isExpanded ? theme.colors?.primary : titleColor; @@ -118,7 +106,6 @@ export const getAccordionColors = ({ customRippleColor || color(titleTextColor).alpha(0.12).rgb().string(); return { - titleColor, descriptionColor, titleTextColor, rippleColor, diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 669656ca65..47727f7e10 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -23,7 +23,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import MenuItem from './MenuItem'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Elevation, ThemeProp } from '../../types'; +import type { MD3Elevation, MD3Theme, ThemeProp } from '../../types'; import { ElevationLevels } from '../../types'; import { addEventListener } from '../../utils/addEventListener'; import { BackHandler } from '../../utils/BackHandler/BackHandler'; @@ -196,6 +196,7 @@ const Menu = ({ keyboardShouldPersistTaps, }: Props) => { const theme = useInternalTheme(themeOverrides); + const { colors: md3Colors } = theme as MD3Theme; const insets = useSafeAreaInsets(); const [rendered, setRendered] = React.useState(visible); const [left, setLeft] = React.useState(0); @@ -618,7 +619,6 @@ const Menu = ({ }, ], borderRadius: theme.roundness, - ...(!theme.isV3 && { elevation: 8 }), ...(scrollableMenuHeight ? { height: scrollableMenuHeight } : {}), }; @@ -673,13 +673,13 @@ const Menu = ({ style={[ styles.shadowMenuContainer, shadowMenuContainerStyle, - theme.isV3 && { + { backgroundColor: - theme.colors.elevation[ELEVATION_LEVELS_MAP[elevation]], + md3Colors.elevation[ELEVATION_LEVELS_MAP[elevation]], }, contentStyle, ]} - {...(theme.isV3 && { elevation })} + elevation={elevation} testID={`${testID}-surface`} theme={theme} container diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 4b8eb3740a..652ffc9a4f 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -18,7 +18,7 @@ import { MIN_WIDTH, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { MD3Theme, ThemeProp } from '../../types'; import Icon, { IconSource } from '../Icon'; import TouchableRipple, { Props as TouchableRippleProps, @@ -156,16 +156,13 @@ const MenuItem = ({ disabled, customRippleColor, }); - const { isV3 } = theme; + const containerPadding = 12; - const containerPadding = isV3 ? 12 : 8; + const iconWidth = 24; - const iconWidth = isV3 ? 24 : 40; - - const minWidth = MIN_WIDTH - (isV3 ? 12 : 16); + const minWidth = MIN_WIDTH - 12; const maxWidth = getContentMaxWidth({ - isV3, iconWidth, leadingIcon, trailingIcon, @@ -173,7 +170,7 @@ const MenuItem = ({ const titleTextStyle = { color: titleColor, - ...(isV3 ? theme.fonts.bodyLarge : {}), + ...(theme as MD3Theme).fonts.bodyLarge, }; const newAccessibilityState = { ...accessibilityState, disabled }; @@ -198,22 +195,15 @@ const MenuItem = ({ > {leadingIcon ? ( - + ) : null} {title} - {isV3 && trailingIcon ? ( - + {trailingIcon ? ( + ) : null} @@ -257,12 +244,6 @@ const styles = StyleSheet.create({ row: { flexDirection: 'row', }, - title: { - fontSize: 16, - }, - item: { - marginHorizontal: 8, - }, content: { justifyContent: 'center', }, diff --git a/src/components/Menu/utils.ts b/src/components/Menu/utils.ts index c1b6189ba4..71c503bcc2 100644 --- a/src/components/Menu/utils.ts +++ b/src/components/Menu/utils.ts @@ -2,15 +2,13 @@ import type { ColorValue } from 'react-native'; import color from 'color'; -import { black, white } from '../../styles/themes/v2/colors'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme, MD3Theme } from '../../types'; import type { IconSource } from '../Icon'; export const MIN_WIDTH = 112; export const MAX_WIDTH = 280; type ContentProps = { - isV3: boolean; iconWidth: number; leadingIcon?: IconSource; trailingIcon?: IconSource; @@ -23,38 +21,25 @@ type ColorProps = { }; const getDisabledColor = (theme: InternalTheme) => { - if (theme.isV3) { - return theme.colors.onSurfaceDisabled; - } - - return color(theme.dark ? white : black) - .alpha(0.32) - .rgb() - .string(); + return (theme as MD3Theme).colors.onSurfaceDisabled; }; const getTitleColor = ({ theme, disabled }: ColorProps) => { + const { colors } = theme as MD3Theme; if (disabled) { return getDisabledColor(theme); } - if (theme.isV3) { - return theme.colors.onSurface; - } - - return color(theme.colors.text).alpha(0.87).rgb().string(); + return colors.onSurface; }; const getIconColor = ({ theme, disabled }: ColorProps) => { + const { colors } = theme as MD3Theme; if (disabled) { return getDisabledColor(theme); } - if (theme.isV3) { - return theme.colors.onSurfaceVariant; - } - - return color(theme.colors.text).alpha(0.54).rgb().string(); + return colors.onSurfaceVariant; }; const getRippleColor = ({ @@ -65,11 +50,10 @@ const getRippleColor = ({ return customRippleColor; } - if (theme.isV3) { - return color(theme.colors.onSurfaceVariant).alpha(0.12).rgb().string(); - } - - return undefined; + return color((theme as MD3Theme).colors.onSurfaceVariant) + .alpha(0.12) + .rgb() + .string(); }; export const getMenuItemColor = ({ @@ -85,26 +69,17 @@ export const getMenuItemColor = ({ }; export const getContentMaxWidth = ({ - isV3, iconWidth, leadingIcon, trailingIcon, }: ContentProps) => { - if (isV3) { - if (leadingIcon && trailingIcon) { - return MAX_WIDTH - (2 * iconWidth + 24); - } - - if (leadingIcon || trailingIcon) { - return MAX_WIDTH - (iconWidth + 24); - } - - return MAX_WIDTH - 12; + if (leadingIcon && trailingIcon) { + return MAX_WIDTH - (2 * iconWidth + 24); } - if (leadingIcon) { - return MAX_WIDTH - (iconWidth + 48); + if (leadingIcon || trailingIcon) { + return MAX_WIDTH - (iconWidth + 24); } - return MAX_WIDTH - 16; + return MAX_WIDTH - 12; }; diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index 790204f4f8..c307df2479 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -10,8 +10,6 @@ import { ViewStyle, } from 'react-native'; -import setColor from 'color'; - import { useInternalTheme } from '../core/theming'; import type { ThemeProp } from '../types'; @@ -191,9 +189,7 @@ const ProgressBar = ({ }; const tintColor = color || theme.colors?.primary; - const trackTintColor = theme.isV3 - ? theme.colors.surfaceVariant - : setColor(tintColor).alpha(0.38).rgb().string(); + const trackTintColor = theme.colors.surfaceVariant; return ( ; } - const textColor = theme.isV3 ? theme.colors.onSurface : theme.colors.text; - const disabledTextColor = theme.isV3 - ? theme.colors.onSurfaceDisabled - : theme.colors.disabled; + const textColor = theme.colors.onSurface; + const disabledTextColor = theme.colors.onSurfaceDisabled; const textAlign = isLeading ? 'right' : 'left'; const computedStyle = { @@ -232,12 +230,7 @@ const RadioButtonItem = ({ {isLeading && radioButton} {label} @@ -270,7 +263,4 @@ const styles = StyleSheet.create({ flexShrink: 1, flexGrow: 1, }, - font: { - fontSize: 16, - }, }); diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 891df22945..6377899280 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -23,7 +23,7 @@ import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; import { useInternalTheme } from '../core/theming'; -import type { ThemeProp } from '../types'; +import type { MD3Theme, ThemeProp } from '../types'; import { forwardRef } from '../utils/forwardRef'; interface Style { @@ -208,6 +208,7 @@ const Searchbar = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); + const { colors, fonts } = theme as MD3Theme; const root = React.useRef(null); React.useImperativeHandle(ref, () => ({ @@ -227,34 +228,26 @@ const Searchbar = forwardRef( onClearIconPress?.(e); }; - const { roundness, dark, isV3, fonts } = theme; + const { roundness, dark } = theme; - const placeholderTextColor = isV3 - ? theme.colors.onSurface - : theme.colors?.placeholder; - const textColor = isV3 ? theme.colors.onSurfaceVariant : theme.colors.text; - const md2IconColor = dark - ? textColor - : color(textColor).alpha(0.54).rgb().string(); - const iconColor = - customIconColor || (isV3 ? theme.colors.onSurfaceVariant : md2IconColor); + const placeholderTextColor = colors.onSurface; + const textColor = colors.onSurfaceVariant; + const iconColor = customIconColor || colors.onSurfaceVariant; const rippleColor = customRippleColor || color(textColor).alpha(0.32).rgb().string(); const traileringRippleColor = customTraileringRippleColor || color(textColor).alpha(0.32).rgb().string(); - const font = isV3 - ? { - ...fonts.bodyLarge, - lineHeight: Platform.select({ - ios: 0, - default: fonts.bodyLarge.lineHeight, - }), - } - : theme.fonts.regular; + const font = { + ...fonts.bodyLarge, + lineHeight: Platform.select({ + ios: 0, + default: fonts.bodyLarge.lineHeight, + }), + }; - const isBarMode = isV3 && mode === 'bar'; + const isBarMode = mode === 'bar'; const shouldRenderTraileringIcon = isBarMode && traileringIcon && @@ -265,16 +258,15 @@ const Searchbar = forwardRef( @@ -307,12 +299,12 @@ const Searchbar = forwardRef( ...font, ...Platform.select({ web: { outline: 'none' } }), }, - isV3 && (isBarMode ? styles.barModeInput : styles.viewModeInput), + isBarMode ? styles.barModeInput : styles.viewModeInput, inputStyle, ]} placeholder={placeholder || ''} placeholderTextColor={placeholderTextColor} - selectionColor={theme.colors?.primary} + selectionColor={colors.primary} underlineColorAndroid="transparent" returnKeyType="search" keyboardAppearance={dark ? 'dark' : 'light'} @@ -325,7 +317,7 @@ const Searchbar = forwardRef( {loading ? ( ) : ( // Clear icon should be always rendered within Searchbar – it's transparent, @@ -336,8 +328,8 @@ const Searchbar = forwardRef( pointerEvents={value ? 'auto' : 'none'} testID={`${testID}-icon-wrapper`} style={[ - isV3 && !value && styles.v3ClearIcon, - isV3 && right !== undefined && styles.v3ClearIconHidden, + !value && styles.v3ClearIcon, + right !== undefined && styles.v3ClearIconHidden, ]} > ( clearIcon || (({ size, color }) => ( ( accessibilityRole="button" borderless onPress={onTraileringIconPress} - iconColor={traileringIconColor || theme.colors.onSurfaceVariant} + iconColor={traileringIconColor || colors.onSurfaceVariant} rippleColor={traileringRippleColor} icon={traileringIcon} accessibilityLabel={traileringIconAccessibilityLabel} @@ -377,13 +369,13 @@ const Searchbar = forwardRef( ) : null} {isBarMode && right?.({ color: textColor, style: styles.rightStyle, testID })} - {isV3 && !isBarMode && showDivider && ( + {!isBarMode && showDivider && ( { if (checked) { - if (theme.isV3) { - return theme.colors.secondaryContainer; - } else { - return color(theme.colors.primary).alpha(0.12).rgb().string(); - } + return theme.colors.secondaryContainer; } return 'transparent'; }; -const getSegmentedButtonBorderColor = ({ - theme, - disabled, - checked, -}: BaseProps) => { - if (theme.isV3) { - if (disabled) { - return theme.colors.surfaceDisabled; - } - return theme.colors.outline; - } - if (checked) { - return theme.colors.primary; +const getSegmentedButtonBorderColor = ({ theme, disabled }: BaseProps) => { + if (disabled) { + return theme.colors.surfaceDisabled; } - - return color(theme.dark ? white : black) - .alpha(0.29) - .rgb() - .string(); + return theme.colors.outline; }; const getSegmentedButtonBorderWidth = ({ - theme, + theme: _t, }: Omit) => { - if (theme.isV3) { - return 1; - } - - return StyleSheet.hairlineWidth; + return 1; }; const getSegmentedButtonTextColor = ({ @@ -133,21 +107,13 @@ const getSegmentedButtonTextColor = ({ checkedColor, uncheckedColor, }: SegmentedButtonProps) => { - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - if (checked) { - return checkedColor ?? theme.colors.onSecondaryContainer; - } - return uncheckedColor ?? theme.colors.onSurface; - } - if (disabled) { - return theme.colors.disabled; + return theme.colors.onSurfaceDisabled; + } + if (checked) { + return checkedColor ?? theme.colors.onSecondaryContainer; } - // Primary color is used for checked state too. - return theme.colors.primary; + return uncheckedColor ?? theme.colors.onSurface; }; export const getSegmentedButtonColors = ({ diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index d11bc91a62..22b55bb02a 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -20,7 +20,7 @@ import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; -import type { $Omit, $RemoveChildren, ThemeProp } from '../types'; +import type { $Omit, $RemoveChildren, MD3Theme, ThemeProp } from '../types'; export type Props = $Omit, 'mode'> & { /** @@ -243,7 +243,7 @@ const Snackbar = ({ } }, [visible, handleOnVisible, handleOnHidden]); - const { colors, roundness, isV3 } = theme; + const { colors, roundness } = theme as MD3Theme; if (hidden) { return null; @@ -257,11 +257,11 @@ const Snackbar = ({ ...actionProps } = action || {}; - const buttonTextColor = isV3 ? colors.inversePrimary : colors.accent; - const textColor = isV3 ? colors.inverseOnSurface : colors?.surface; - const backgroundColor = isV3 ? colors.inverseSurface : colors?.onSurface; + const buttonTextColor = colors.inversePrimary; + const textColor = colors.inverseOnSurface; + const backgroundColor = colors.inverseSurface; - const isIconButton = isV3 && onIconPress; + const isIconButton = Boolean(onIconPress); const marginLeft = action ? -12 : -16; @@ -301,7 +301,6 @@ const Snackbar = ({ accessibilityLiveRegion="polite" theme={theme} style={[ - !isV3 && styles.elevation, styles.container, { backgroundColor, @@ -322,7 +321,7 @@ const Snackbar = ({ ]} testID={testID} container - {...(isV3 && { elevation })} + elevation={elevation} {...rest} > {renderChildrenWithWrapper()} @@ -336,7 +335,7 @@ const Snackbar = ({ }} style={[styles.button, actionStyle]} textColor={buttonTextColor} - compact={!isV3} + compact={false} mode="text" theme={theme} rippleColor={actionRippleColor} @@ -350,7 +349,7 @@ const Snackbar = ({ accessibilityRole="button" borderless onPress={onIconPress} - iconColor={theme.colors.inverseOnSurface} + iconColor={colors.inverseOnSurface} rippleColor={rippleColor} theme={theme} icon={ @@ -423,9 +422,6 @@ const styles = StyleSheet.create({ marginRight: 8, marginLeft: 4, }, - elevation: { - elevation: 6, - }, icon: { width: 40, height: 40, diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index 3b46d099e1..2c8fad2461 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -10,9 +10,9 @@ import { } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import overlay, { isAnimatedValue } from '../styles/overlay'; +import { isAnimatedValue } from '../styles/overlay'; import shadow from '../styles/shadow'; -import type { ThemeProp, MD3Elevation } from '../types'; +import type { MD3Elevation, MD3Theme, ThemeProp } from '../types'; import { forwardRef } from '../utils/forwardRef'; import { splitStyles } from '../utils/splitStyles'; @@ -57,30 +57,6 @@ export type Props = Omit, 'style'> & { container?: boolean; }; -const MD2Surface = forwardRef( - ({ style, theme: overrideTheme, ...rest }: Omit, ref) => { - const { elevation = 4 } = (StyleSheet.flatten(style) || {}) as ViewStyle; - const { dark: isDarkTheme, mode, colors } = useInternalTheme(overrideTheme); - - return ( - - ); - } -); - const outerLayerStyleProperties: (keyof ViewStyle)[] = [ 'position', 'alignSelf', @@ -277,14 +253,7 @@ const Surface = forwardRef( ) => { const theme = useInternalTheme(overridenTheme); - if (!theme.isV3) - return ( - - {children} - - ); - - const { colors } = theme; + const { colors } = theme as MD3Theme; const inputRange = [0, 1, 2, 3, 4, 5]; @@ -313,7 +282,7 @@ const Surface = forwardRef( testID={testID} style={[ { backgroundColor }, - elevation && isElevated ? shadow(elevation, theme.isV3) : null, + elevation && isElevated ? shadow(elevation) : null, style, ]} > diff --git a/src/components/Switch/utils.ts b/src/components/Switch/utils.ts index 2994495af9..34ffc26eb5 100644 --- a/src/components/Switch/utils.ts +++ b/src/components/Switch/utils.ts @@ -29,11 +29,7 @@ const getCheckedColor = ({ return color; } - if (theme.isV3) { - return theme.colors.primary; - } - - return theme.colors.accent; + return theme.colors.primary; }; const getThumbTintColor = ({ @@ -79,10 +75,7 @@ const getOnTintColor = ({ if (disabled) { if (theme.dark) { - if (theme.isV3) { - return setColor(white).alpha(0.06).rgb().string(); - } - return setColor(white).alpha(0.1).rgb().string(); + return setColor(white).alpha(0.06).rgb().string(); } return setColor(black).alpha(0.12).rgb().string(); } diff --git a/src/components/TextInput/Addons/Outline.tsx b/src/components/TextInput/Addons/Outline.tsx index 45b675fd8b..39ffa48b56 100644 --- a/src/components/TextInput/Addons/Outline.tsx +++ b/src/components/TextInput/Addons/Outline.tsx @@ -10,11 +10,9 @@ import { import { TextInputLabelProp } from '../types'; type OutlineProps = { - isV3: boolean; activeColor: string; backgroundColor: ColorValue; hasActiveOutline?: boolean; - focused?: boolean; outlineColor?: string; roundness?: number; label?: TextInputLabelProp; @@ -22,12 +20,10 @@ type OutlineProps = { }; export const Outline = ({ - isV3, label, activeColor, backgroundColor, hasActiveOutline, - focused, outlineColor, roundness, style, @@ -42,7 +38,7 @@ export const Outline = ({ { backgroundColor, borderRadius: roundness, - borderWidth: (isV3 ? hasActiveOutline : focused) ? 2 : 1, + borderWidth: hasActiveOutline ? 2 : 1, borderColor: hasActiveOutline ? activeColor : outlineColor, }, style, diff --git a/src/components/TextInput/Addons/Underline.tsx b/src/components/TextInput/Addons/Underline.tsx index 184cbe63b5..20b49233dc 100644 --- a/src/components/TextInput/Addons/Underline.tsx +++ b/src/components/TextInput/Addons/Underline.tsx @@ -3,8 +3,6 @@ import { Animated, StyleSheet, StyleProp, ViewStyle } from 'react-native'; import type { ThemeProp } from 'src/types'; -import { useInternalTheme } from '../../../core/theming'; - type UnderlineProps = { parentState: { focused: boolean; @@ -28,32 +26,28 @@ export const Underline = ({ underlineColorCustom, hasActiveOutline, style, - theme: themeOverrides, + theme: _themeOverrides, }: UnderlineProps) => { - const { isV3 } = useInternalTheme(themeOverrides); - let backgroundColor = parentState.focused ? activeColor : underlineColorCustom; if (error) backgroundColor = colors?.error; - const activeScale = isV3 ? 2 : 1; + const activeScale = 2; return ( = ({ forceFocus, paddingHorizontal, maxFontSizeMultiplier, - theme, disabled, }) => { if (adornmentConfig.length) { @@ -174,7 +168,6 @@ const TextInputAdornment: React.FunctionComponent = ({ return ( { const theme = useInternalTheme(themeOverrides); - const { AFFIX_OFFSET } = getConstants(theme.isV3); + const { AFFIX_OFFSET } = getConstants(); const { textStyle, diff --git a/src/components/TextInput/Adornment/TextInputIcon.tsx b/src/components/TextInput/Adornment/TextInputIcon.tsx index bd38ca64ab..8f4ce7c4df 100644 --- a/src/components/TextInput/Adornment/TextInputIcon.tsx +++ b/src/components/TextInput/Adornment/TextInputIcon.tsx @@ -69,7 +69,6 @@ const IconAdornment: React.FunctionComponent< icon: React.ReactNode; topPosition: number; side: 'left' | 'right'; - theme?: ThemeProp; disabled?: boolean; } & Omit > = ({ @@ -79,11 +78,9 @@ const IconAdornment: React.FunctionComponent< isTextInputFocused, forceFocus, testID, - theme: themeOverrides, disabled, }) => { - const { isV3 } = useInternalTheme(themeOverrides); - const { ICON_OFFSET } = getConstants(isV3); + const { ICON_OFFSET } = getConstants(); const style = { top: topPosition, diff --git a/src/components/TextInput/Adornment/utils.ts b/src/components/TextInput/Adornment/utils.ts index e7dae2b487..38d9541706 100644 --- a/src/components/TextInput/Adornment/utils.ts +++ b/src/components/TextInput/Adornment/utils.ts @@ -1,5 +1,3 @@ -import color from 'color'; - import type { InternalTheme } from '../../../types'; type BaseProps = { @@ -8,16 +6,11 @@ type BaseProps = { }; export function getTextColor({ theme, disabled }: BaseProps) { - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - return theme.colors.onSurfaceVariant; + if (disabled) { + return theme.colors.onSurfaceDisabled; } - return color(theme.colors?.text) - .alpha(theme.dark ? 0.7 : 0.54) - .rgb() - .string(); + + return theme.colors.onSurfaceVariant; } export function getIconColor({ @@ -36,10 +29,6 @@ export function getIconColor({ return customColor; } - if (!theme.isV3) { - return theme.colors.text; - } - if (disabled) { return theme.colors.onSurfaceDisabled; } diff --git a/src/components/TextInput/Label/InputLabel.tsx b/src/components/TextInput/Label/InputLabel.tsx index f0818f98f0..4877bc236e 100644 --- a/src/components/TextInput/Label/InputLabel.tsx +++ b/src/components/TextInput/Label/InputLabel.tsx @@ -48,12 +48,11 @@ const InputLabel = (props: InputLabelProps) => { labelTranslationXOffset, maxFontSizeMultiplier, testID, - isV3, inputContainerLayout, scaledLabel, } = props; - const { INPUT_PADDING_HORIZONTAL } = getConstants(isV3); + const { INPUT_PADDING_HORIZONTAL } = getConstants(); const { width } = useWindowDimensions(); const isWeb = Platform.OS === 'web'; diff --git a/src/components/TextInput/TextInputFlat.tsx b/src/components/TextInput/TextInputFlat.tsx index 378366e176..ee8e438fb4 100644 --- a/src/components/TextInput/TextInputFlat.tsx +++ b/src/components/TextInput/TextInputFlat.tsx @@ -78,12 +78,12 @@ const TextInputFlat = ({ ...rest }: ChildTextInputProps) => { const isAndroid = Platform.OS === 'android'; - const { colors, isV3, roundness } = theme; - const font = isV3 ? theme.fonts.bodyLarge : theme.fonts.regular; + const { colors, roundness } = theme; + const font = theme.fonts.bodyLarge; const hasActiveOutline = parentState.focused || error; const { LABEL_PADDING_TOP, FLAT_INPUT_OFFSET, MIN_HEIGHT, MIN_WIDTH } = - getConstants(isV3); + getConstants(); const { fontSize: fontSizeStyle, @@ -108,7 +108,6 @@ const TextInputFlat = ({ let { paddingLeft, paddingRight } = calculateFlatInputHorizontalPadding({ adornmentConfig, - isV3, }); if (isPaddingHorizontalPassed) { @@ -134,7 +133,6 @@ const TextInputFlat = ({ paddingHorizontal, inputOffset: FLAT_INPUT_OFFSET, mode: InputMode.Flat, - isV3, }); const { @@ -304,7 +302,6 @@ const TextInputFlat = ({ ? 1 : 0 : 1, - isV3, }; const affixTopPosition = { diff --git a/src/components/TextInput/TextInputOutlined.tsx b/src/components/TextInput/TextInputOutlined.tsx index 70b590fabb..0bd85fb8d9 100644 --- a/src/components/TextInput/TextInputOutlined.tsx +++ b/src/components/TextInput/TextInputOutlined.tsx @@ -81,12 +81,12 @@ const TextInputOutlined = ({ }: ChildTextInputProps) => { const adornmentConfig = getAdornmentConfig({ left, right }); - const { colors, isV3, roundness } = theme; - const font = isV3 ? theme.fonts.bodyLarge : theme.fonts.regular; + const { colors, roundness } = theme; + const font = theme.fonts.bodyLarge; const hasActiveOutline = parentState.focused || error; const { INPUT_PADDING_HORIZONTAL, MIN_HEIGHT, ADORNMENT_OFFSET, MIN_WIDTH } = - getConstants(isV3); + getConstants(); const { fontSize: fontSizeStyle, @@ -148,8 +148,8 @@ const TextInputOutlined = ({ if (isAdornmentLeftIcon) { labelTranslationXOffset = - (I18nManager.getConstants().isRTL ? -1 : 1) * - (ADORNMENT_SIZE + ADORNMENT_OFFSET - (isV3 ? 0 : 8)); + (I18nManager.getConstants().isRTL ? -1 : 1) * ADORNMENT_SIZE + + ADORNMENT_OFFSET; } const minInputHeight = @@ -254,7 +254,6 @@ const TextInputOutlined = ({ ? 1 : 0 : 1, - isV3, }; const onLayoutChange = React.useCallback( @@ -303,7 +302,6 @@ const TextInputOutlined = ({ rightAffixWidth, leftAffixWidth, mode: 'outlined', - isV3, }); const affixTopPosition = { [AdornmentSide.Left]: leftAffixTopPosition, @@ -344,12 +342,10 @@ const TextInputOutlined = ({ Otherwise the border will cut off the label on Android */} { const { LABEL_PADDING_HORIZONTAL, ADORNMENT_OFFSET, FLAT_INPUT_OFFSET } = - getConstants(isV3); + getConstants(); let paddingLeft = LABEL_PADDING_HORIZONTAL; let paddingRight = LABEL_PADDING_HORIZONTAL; @@ -320,19 +309,11 @@ const getInputTextColor = ({ return textColor; } - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - return theme.colors.onSurface; - } - if (disabled) { - return color(theme.colors.text).alpha(0.54).rgb().string(); + return theme.colors.onSurfaceDisabled; } - return theme.colors.text; + return theme.colors.onSurface; }; const getActiveColor = ({ @@ -360,30 +341,18 @@ const getActiveColor = ({ } if (disabled) { - if (theme.isV3) { - return theme.colors.onSurfaceDisabled; - } - - return color(theme.colors.text).alpha(0.54).rgb().string(); + return theme.colors.onSurfaceDisabled; } return theme.colors.primary; }; const getPlaceholderColor = ({ theme, disabled }: BaseProps) => { - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - return theme.colors.onSurfaceVariant; - } - if (disabled) { - return theme.colors.disabled; + return theme.colors.onSurfaceDisabled; } - return theme.colors.placeholder; + return theme.colors.onSurfaceVariant; }; const getSelectionColor = ({ @@ -405,21 +374,11 @@ const getSelectionColor = ({ }; const getFlatBackgroundColor = ({ theme, disabled }: BaseProps) => { - if (theme.isV3) { - if (disabled) { - return color(theme.colors.onSurface).alpha(0.04).rgb().string(); - } else { - return theme.colors.surfaceVariant; - } - } - if (disabled) { - return undefined; + return color(theme.colors.onSurface).alpha(0.04).rgb().string(); } - return theme.dark - ? color(theme.colors?.background).lighten(0.24).rgb().string() - : color(theme.colors?.background).darken(0.06).rgb().string(); + return theme.colors.surfaceVariant; }; const getFlatUnderlineColor = ({ @@ -431,19 +390,11 @@ const getFlatUnderlineColor = ({ return underlineColor; } - if (theme.isV3) { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - - return theme.colors.onSurfaceVariant; - } - if (disabled) { - return 'transparent'; + return theme.colors.onSurfaceDisabled; } - return theme.colors.disabled; + return theme.colors.onSurfaceVariant; }; const getOutlinedOutlineInputColor = ({ @@ -451,30 +402,19 @@ const getOutlinedOutlineInputColor = ({ disabled, customOutlineColor, }: BaseProps & { customOutlineColor?: string }) => { - const isTransparent = color(customOutlineColor).alpha() === 0; - if (!disabled && customOutlineColor) { return customOutlineColor; } - if (theme.isV3) { - if (disabled) { - if (theme.dark) { - return 'transparent'; - } - return theme.colors.surfaceDisabled; + if (disabled) { + if (theme.dark) { + return 'transparent'; } - return theme.colors.outline; + return theme.colors.surfaceDisabled; } - if (disabled) { - if (isTransparent) { - return customOutlineColor; - } - return theme.colors.disabled; - } - return theme.colors.placeholder; + return theme.colors.outline; }; export const getFlatInputColors = ({ @@ -560,53 +500,17 @@ export const getOutlinedInputColors = ({ }; }; -export const getConstants = (isV3?: boolean) => { - // Text input affix - let AFFIX_OFFSET; - // Text input icon - let ICON_OFFSET; - //Text input flat - let LABEL_PADDING_TOP; - let LABEL_PADDING_HORIZONTAL; - let FLAT_INPUT_OFFSET; - let MIN_HEIGHT; - // Text input outlined; - let INPUT_PADDING_HORIZONTAL; - let ADORNMENT_OFFSET; - let OUTLINED_INPUT_OFFSET; - - if (isV3) { - AFFIX_OFFSET = MD3_AFFIX_OFFSET; - ICON_OFFSET = MD3_ICON_OFFSET; - LABEL_PADDING_TOP = MD3_LABEL_PADDING_TOP; - LABEL_PADDING_HORIZONTAL = MD3_LABEL_PADDING_HORIZONTAL; - FLAT_INPUT_OFFSET = MD3_FLAT_INPUT_OFFSET; - MIN_HEIGHT = MD3_MIN_HEIGHT; - INPUT_PADDING_HORIZONTAL = MD3_INPUT_PADDING_HORIZONTAL; - ADORNMENT_OFFSET = MD3_ADORNMENT_OFFSET; - OUTLINED_INPUT_OFFSET = MD3_OUTLINED_INPUT_OFFSET; - } else { - AFFIX_OFFSET = MD2_AFFIX_OFFSET; - ICON_OFFSET = MD2_ICON_OFFSET; - LABEL_PADDING_TOP = MD2_LABEL_PADDING_TOP; - LABEL_PADDING_HORIZONTAL = MD2_LABEL_PADDING_HORIZONTAL; - FLAT_INPUT_OFFSET = MD2_FLAT_INPUT_OFFSET; - MIN_HEIGHT = MD2_MIN_HEIGHT; - INPUT_PADDING_HORIZONTAL = MD2_INPUT_PADDING_HORIZONTAL; - ADORNMENT_OFFSET = MD2_ADORNMENT_OFFSET; - OUTLINED_INPUT_OFFSET = MD2_OUTLINED_INPUT_OFFSET; - } - +export const getConstants = () => { return { - AFFIX_OFFSET, - ICON_OFFSET, - LABEL_PADDING_TOP, - LABEL_PADDING_HORIZONTAL, - FLAT_INPUT_OFFSET, - MIN_HEIGHT, - INPUT_PADDING_HORIZONTAL, - ADORNMENT_OFFSET, - OUTLINED_INPUT_OFFSET, + AFFIX_OFFSET: MD3_AFFIX_OFFSET, + ICON_OFFSET: MD3_ICON_OFFSET, + LABEL_PADDING_TOP: MD3_LABEL_PADDING_TOP, + LABEL_PADDING_HORIZONTAL: MD3_LABEL_PADDING_HORIZONTAL, + FLAT_INPUT_OFFSET: MD3_FLAT_INPUT_OFFSET, + MIN_HEIGHT: MD3_MIN_HEIGHT, + INPUT_PADDING_HORIZONTAL: MD3_INPUT_PADDING_HORIZONTAL, + ADORNMENT_OFFSET: MD3_ADORNMENT_OFFSET, + OUTLINED_INPUT_OFFSET: MD3_OUTLINED_INPUT_OFFSET, MIN_WIDTH, }; }; diff --git a/src/components/TextInput/types.tsx b/src/components/TextInput/types.tsx index 4ec2952366..1f710e4089 100644 --- a/src/components/TextInput/types.tsx +++ b/src/components/TextInput/types.tsx @@ -141,7 +141,6 @@ export type InputLabelProps = { inputContainerLayout: { width: number }; labelBackground?: any; maxFontSizeMultiplier?: number | undefined | null; - isV3?: boolean; scaledLabel?: boolean; } & LabelProps; diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index bfdd3fca5c..e115dec7ec 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -9,12 +9,9 @@ import { ColorValue, } from 'react-native'; -import color from 'color'; - import { ToggleButtonGroupContext } from './ToggleButtonGroup'; import { getToggleButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; -import { black, white } from '../../styles/themes/v2/colors'; import type { ThemeProp } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; @@ -128,12 +125,7 @@ const ToggleButton = forwardRef( (context && context.value === value) || status === 'checked'; const backgroundColor = getToggleButtonColor({ theme, checked }); - const borderColor = theme.isV3 - ? theme.colors.outline - : color(theme.dark ? white : black) - .alpha(0.29) - .rgb() - .string(); + const borderColor = theme.colors.outline; return ( { if (checked) { - if (theme.isV3) { - return color(theme.colors.onSecondaryContainer) - .alpha(tokens.md.ref.opacity.level2) - .rgb() - .string(); - } - if (theme.dark) { - return 'rgba(255, 255, 255, .12)'; - } - return 'rgba(0, 0, 0, .08)'; + return color(theme.colors.onSecondaryContainer) + .alpha(tokens.md.ref.opacity.level2) + .rgb() + .string(); } return 'transparent'; }; diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index f5a483db5a..fdfef8571f 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -204,9 +204,7 @@ const Tooltip = ({ style={[ styles.tooltip, { - backgroundColor: theme.isV3 - ? theme.colors.onSurface - : theme.colors.tooltip, + backgroundColor: theme.colors.onSurface, ...getTooltipPosition( measurement as Measurement, children as React.ReactElement diff --git a/src/components/TouchableRipple/utils.ts b/src/components/TouchableRipple/utils.ts index 29c2b18e25..e3a1e9726f 100644 --- a/src/components/TouchableRipple/utils.ts +++ b/src/components/TouchableRipple/utils.ts @@ -5,11 +5,9 @@ import color from 'color'; import type { InternalTheme } from '../../types'; const getUnderlayColor = ({ - theme, calculatedRippleColor, underlayColor, }: { - theme: InternalTheme; calculatedRippleColor: ColorValue; underlayColor?: string; }) => { @@ -17,11 +15,7 @@ const getUnderlayColor = ({ return underlayColor; } - if (theme.isV3) { - return color(calculatedRippleColor).rgb().string(); - } - - return color(calculatedRippleColor).fade(0.5).rgb().string(); + return color(calculatedRippleColor).rgb().string(); }; const getRippleColor = ({ @@ -35,14 +29,7 @@ const getRippleColor = ({ return rippleColor; } - if (theme.isV3) { - return color(theme.colors.onSurface).alpha(0.12).rgb().string(); - } - - if (theme.dark) { - return color(theme.colors.text).alpha(0.32).rgb().string(); - } - return color(theme.colors.text).alpha(0.2).rgb().string(); + return color(theme.colors.onSurface).alpha(0.12).rgb().string(); }; export const getTouchableRippleColors = ({ @@ -58,7 +45,6 @@ export const getTouchableRippleColors = ({ return { calculatedRippleColor, calculatedUnderlayColor: getUnderlayColor({ - theme, calculatedRippleColor, underlayColor, }), diff --git a/src/components/Typography/AnimatedText.tsx b/src/components/Typography/AnimatedText.tsx index 6504ad4b92..19872e2d93 100644 --- a/src/components/Typography/AnimatedText.tsx +++ b/src/components/Typography/AnimatedText.tsx @@ -50,7 +50,7 @@ const AnimatedText = forwardRef>( const theme = useInternalTheme(themeOverrides); const writingDirection = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'; - if (theme.isV3 && variant) { + if (variant) { const font = theme.fonts[variant]; if (typeof font !== 'object') { throw new Error( @@ -73,10 +73,10 @@ const AnimatedText = forwardRef>( /> ); } else { - const font = !theme.isV3 ? theme.fonts.regular : theme.fonts.bodyMedium; + const font = theme.fonts.bodyMedium; const textStyle = { ...font, - color: theme.isV3 ? theme.colors.onSurface : theme.colors.text, + color: theme.colors.onSurface, }; return ( root.current?.setNativeProps(args), })); - if (theme.isV3 && variant) { + if (variant) { let font = theme.fonts[variant]; let textStyle = [font, style]; if ( React.isValidElement(rest.children) && - (rest.children.type === Component || - rest.children.type === AnimatedText || - rest.children.type === StyledText) + (rest.children.type === Component || rest.children.type === AnimatedText) ) { const { props } = rest.children as { props: { variant?: string; style?: StyleProp }; @@ -155,10 +152,10 @@ const Text = ( /> ); } else { - const font = theme.isV3 ? theme.fonts.default : theme.fonts?.regular; + const font = theme.fonts.default; const textStyle = { ...font, - color: theme.isV3 ? theme.colors?.onSurface : theme.colors.text, + color: theme.colors?.onSurface, }; return ( & { - style?: StyleProp; - children: React.ReactNode; -}; - -// @component-group Typography -/** - * @deprecated Deprecated in v5.x - use `` instead. - * Typography component for showing a caption. - * - *
- * - *
- * - * ## Usage - * ```js - * import * as React from 'react'; - * import { Caption } from 'react-native-paper'; - * - * const MyComponent = () => ( - * Caption - * ); - * - * export default MyComponent; - * ``` - */ -const Caption = (props: Props) => ( - -); - -export default Caption; - -const styles = StyleSheet.create({ - text: { - fontSize: 12, - lineHeight: 20, - marginVertical: 2, - letterSpacing: 0.4, - }, -}); diff --git a/src/components/Typography/v2/Headline.tsx b/src/components/Typography/v2/Headline.tsx deleted file mode 100644 index c635f81d07..0000000000 --- a/src/components/Typography/v2/Headline.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from 'react'; -import { Text, TextStyle, StyleSheet, StyleProp } from 'react-native'; - -import StyledText from './StyledText'; - -export type Props = React.ComponentProps & { - style?: StyleProp; - children: React.ReactNode; -}; - -// @component-group Typography - -/** - * @deprecated Deprecated in v5.x - use `` instead. - * Typography component for showing a headline. - * - *
- * - *
- * - * ## Usage - * ```js - * import * as React from 'react'; - * import { Headline } from 'react-native-paper'; - * - * const MyComponent = () => ( - * Headline - * ); - * - * export default MyComponent; - * ``` - */ -const Headline = (props: Props) => { - return ( - - ); -}; - -export default Headline; - -const styles = StyleSheet.create({ - text: { - fontSize: 24, - lineHeight: 32, - marginVertical: 2, - letterSpacing: 0, - }, -}); diff --git a/src/components/Typography/v2/Paragraph.tsx b/src/components/Typography/v2/Paragraph.tsx deleted file mode 100644 index 26149d37e3..0000000000 --- a/src/components/Typography/v2/Paragraph.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from 'react'; -import { TextProps, StyleSheet } from 'react-native'; - -import StyledText from './StyledText'; - -export type Props = TextProps & { - children: React.ReactNode; -}; - -// @component-group Typography - -/** - * @deprecated Deprecated in v5.x - use `` instead. - * Typography component for showing a paragraph. - * - *
- * - *
- * - * ## Usage - * ```js - * import * as React from 'react'; - * import { Paragraph } from 'react-native-paper'; - * - * const MyComponent = () => ( - * Paragraph - * ); - * - * export default MyComponent; - * ``` - */ -const Paragraph = (props: Props) => ( - -); - -export default Paragraph; - -const styles = StyleSheet.create({ - text: { - fontSize: 14, - lineHeight: 20, - marginVertical: 2, - letterSpacing: 0.25, - }, -}); diff --git a/src/components/Typography/v2/StyledText.tsx b/src/components/Typography/v2/StyledText.tsx deleted file mode 100644 index 5d976ff267..0000000000 --- a/src/components/Typography/v2/StyledText.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import * as React from 'react'; -import { I18nManager, StyleProp, StyleSheet, TextStyle } from 'react-native'; - -import color from 'color'; -import type { ThemeProp } from 'src/types'; - -import Text from './Text'; -import { useInternalTheme } from '../../../core/theming'; - -type Props = React.ComponentProps & { - alpha?: number; - family: 'regular' | 'medium' | 'light' | 'thin'; - style?: StyleProp; - theme?: ThemeProp; -}; - -const StyledText = ({ - alpha = 1, - family, - style, - theme: themeOverrides, - ...rest -}: Props) => { - const theme = useInternalTheme(themeOverrides); - - const textColor = color( - theme.isV3 ? theme.colors.onSurface : theme.colors?.text - ) - .alpha(alpha) - .rgb() - .string(); - const writingDirection = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'; - - return ( - - ); -}; - -const styles = StyleSheet.create({ - text: { - textAlign: 'left', - }, -}); - -export default StyledText; diff --git a/src/components/Typography/v2/Subheading.tsx b/src/components/Typography/v2/Subheading.tsx deleted file mode 100644 index d7c849d10b..0000000000 --- a/src/components/Typography/v2/Subheading.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import * as React from 'react'; -import { Text, TextStyle, StyleSheet, StyleProp } from 'react-native'; - -import StyledText from './StyledText'; - -export type Props = React.ComponentProps & { - style?: StyleProp; - children: React.ReactNode; -}; - -// @component-group Typography - -/** - * @deprecated Deprecated in v5.x - use `` instead. - * Typography component for showing a subheading. - * - *
- * - *
- * - * ## Usage - * ```js - * import * as React from 'react'; - * import { Subheading } from 'react-native-paper'; - * - * const MyComponent = () => ( - * Subheading - * ); - * - * export default MyComponent; - * ``` - */ -const Subheading = (props: Props) => ( - -); - -export default Subheading; - -const styles = StyleSheet.create({ - text: { - fontSize: 16, - lineHeight: 24, - marginVertical: 2, - letterSpacing: 0.5, - }, -}); diff --git a/src/components/Typography/v2/Text.tsx b/src/components/Typography/v2/Text.tsx deleted file mode 100644 index 8114915189..0000000000 --- a/src/components/Typography/v2/Text.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react'; -import { - StyleProp, - StyleSheet, - Text as NativeText, - TextStyle, -} from 'react-native'; - -import type { MD2Theme } from 'src/types'; - -import { useInternalTheme } from '../../../core/theming'; -import { forwardRef } from '../../../utils/forwardRef'; - -type Props = React.ComponentProps & { - style?: StyleProp; - /** - * @optional - */ - theme?: MD2Theme; -}; - -// @component-group Typography - -/** - * Text component which follows styles from the theme. - * - * @extends Text props https://reactnative.dev/docs/text#props - */ -const Text: React.ForwardRefRenderFunction<{}, Props> = ( - { style, theme: overrideTheme, ...rest }: Props, - ref -) => { - const root = React.useRef(null); - const theme = useInternalTheme(overrideTheme); - - React.useImperativeHandle(ref, () => ({ - setNativeProps: (args: Object) => root.current?.setNativeProps(args), - })); - - return ( - - ); -}; - -const styles = StyleSheet.create({ - text: { - textAlign: 'left', - }, -}); - -export default forwardRef(Text); diff --git a/src/components/Typography/v2/Title.tsx b/src/components/Typography/v2/Title.tsx deleted file mode 100644 index cbddd1e22e..0000000000 --- a/src/components/Typography/v2/Title.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from 'react'; -import { Text, StyleSheet } from 'react-native'; - -import StyledText from './StyledText'; - -export type Props = React.ComponentProps & { - children: React.ReactNode; -}; - -// @component-group Typography - -/** - * @deprecated Deprecated in v5.x - use `` instead. - * Typography component for showing a title. - * - *
- * - *
- * - * ## Usage - * ```js - * import * as React from 'react'; - * import { Title } from 'react-native-paper'; - * - * const MyComponent = () => ( - * Title - * ); - * - * export default MyComponent; - * ``` - */ -const Title = (props: Props) => ( - -); - -export default Title; - -const styles = StyleSheet.create({ - text: { - fontSize: 20, - lineHeight: 30, - marginVertical: 2, - letterSpacing: 0.15, - }, -}); diff --git a/src/components/Typography/v2/index.ts b/src/components/Typography/v2/index.ts deleted file mode 100644 index 42fdec5730..0000000000 --- a/src/components/Typography/v2/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default as Caption } from './Caption'; -export { default as Headline } from './Headline'; -export { default as Paragraph } from './Paragraph'; -export { default as Subheading } from './Subheading'; -export { default as Title } from './Title'; diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 6f76dab75b..60a7827b6e 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -1,12 +1,10 @@ import React from 'react'; -import { Animated, Platform } from 'react-native'; +import { Animated } from 'react-native'; import { act, render } from '@testing-library/react-native'; import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'; -import PaperProvider from '../../../core/PaperProvider'; import { getTheme } from '../../../core/theming'; -import overlay from '../../../styles/overlay'; import { tokens } from '../../../styles/themes/v3/tokens'; import Appbar from '../../Appbar'; import { @@ -17,7 +15,6 @@ import { } from '../../Appbar/utils'; import Menu from '../../Menu/Menu'; import Searchbar from '../../Searchbar'; -import Tooltip from '../../Tooltip/Tooltip'; import Text from '../../Typography/Text'; const renderAppbarContent = utilRenderAppbarContent as ( @@ -58,7 +55,6 @@ describe('renderAppbarContent', () => { it('should render all children types if renderOnly is not specified', () => { const result = renderAppbarContent({ - isV3: false, children, isDark: false, }); @@ -68,7 +64,6 @@ describe('renderAppbarContent', () => { it('should render all children types except specified in renderExcept', () => { const result = renderAppbarContent({ - isV3: false, children: [ ...children, { it('should render only children types specifed in renderOnly', () => { const result = renderAppbarContent({ - isV3: false, children, isDark: false, renderOnly: ['Appbar.Action'], @@ -104,7 +98,6 @@ describe('renderAppbarContent', () => { it('should render AppbarContent with correct mode', () => { const result = renderAppbarContent({ - isV3: false, children, isDark: false, renderOnly: ['Appbar.Content'], @@ -115,87 +108,34 @@ describe('renderAppbarContent', () => { }); it('should render centered AppbarContent', () => { - const renderResult = (isV3 = true) => - renderAppbarContent({ - children, - isDark: false, - isV3, - renderOnly: ['Appbar.Content'], - mode: 'center-aligned', - shouldCenterContent: true, - }); - - const centerAlignedContent = { - alignItems: 'center', - }; - - expect(renderResult()[0].props.style).toEqual( - expect.arrayContaining([expect.objectContaining(centerAlignedContent)]) - ); - - expect(renderResult(false)[0].props.style).toEqual( - expect.arrayContaining([expect.objectContaining(centerAlignedContent)]) - ); - }); - - it('should not render centered AppbarContent for Android, if not V3', () => { - Platform.OS = 'android'; - const renderResult = (isV3 = true) => - renderAppbarContent({ - children, - isDark: false, - isV3, - renderOnly: ['Appbar.Content'], - mode: 'center-aligned', - shouldCenterContent: !isV3 && Platform.OS === 'ios', - }); - - const centerAlignedContent = { - alignItems: 'center', - }; - - expect(renderResult(false)[0].props.style).not.toEqual( - expect.arrayContaining([expect.objectContaining(centerAlignedContent)]) - ); - }); - - it('should render centered AppbarContent always for iOS, if not V3', () => { - Platform.OS = 'ios'; - const renderResult = (isV3 = true) => - renderAppbarContent({ - children, - isDark: false, - isV3, - renderOnly: ['Appbar.Content'], - mode: 'center-aligned', - shouldCenterContent: !isV3 && Platform.OS === 'ios', - }); + const result = renderAppbarContent({ + children, + isDark: false, + renderOnly: ['Appbar.Content'], + mode: 'center-aligned', + shouldCenterContent: true, + }); const centerAlignedContent = { alignItems: 'center', }; - expect(renderResult(false)[0].props.style).toEqual( + expect(result[0].props.style).toEqual( expect.arrayContaining([expect.objectContaining(centerAlignedContent)]) ); }); it('should render AppbarContent with correct spacings', () => { - const renderResult = (isV3 = true, withAppbarBackAction = false) => + const renderResult = (withAppbarBackAction = false) => renderAppbarContent({ children, isDark: false, - isV3, renderOnly: [ 'Appbar.Content', withAppbarBackAction && 'Appbar.BackAction', ], }); - const v2Spacing = { - marginLeft: 8, - }; - const v3Spacing = { marginLeft: 12, }; @@ -203,10 +143,6 @@ describe('renderAppbarContent', () => { expect(renderResult()[0].props.style).toEqual( expect.arrayContaining([expect.objectContaining(v3Spacing)]) ); - - expect(renderResult(false, true)[1].props.style).toEqual( - expect.arrayContaining([expect.objectContaining(v2Spacing)]) - ); }); it('Is recognized as a header when no onPress callback has been pressed', () => { @@ -321,40 +257,6 @@ describe('AppbarAction', () => { .children; expect(appbarBackActionIcon.props.color).toBe('purple'); }); - - describe('When V2', () => { - const theme = { isV3: false }; - - it('should be rendered with the right color when no color is passed', () => { - const { getByTestId } = render( - - - - ); - - const appbarActionIcon = getByTestId('cross-fade-icon-current').props - .children; - - expect(appbarActionIcon.props.color).toBe('#ffffff'); - }); - - it('should be rendered with the right color when no color is passed but is wrapped by a Tooltip', () => { - const { getByTestId } = render( - - - - - - - - ); - - const appbarActionIcon = getByTestId('cross-fade-icon-current').props - .children; - - expect(appbarActionIcon.props.color).toBe('#ffffff'); - }); - }); }); describe('AppbarContent', () => { @@ -410,18 +312,6 @@ describe('getAppbarColors', () => { tokens.md.ref.palette.neutral10 ); }); - - it('should return v2 light color if theme version is 2', () => { - expect(getAppbarBackgroundColor(getTheme(false, false), elevation)).toBe( - '#6200ee' - ); - }); - - it('should return v2 dark color if theme version is 2', () => { - expect(getAppbarBackgroundColor(getTheme(true, false), elevation)).toBe( - overlay(elevation, '#121212') - ); - }); }); describe('animated value changes correctly', () => { diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 4a62c376d8..14afac63f3 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -679,7 +679,6 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A "letterSpacing": 0, "lineHeight": 28, }, - false, undefined, ], ], diff --git a/src/components/__tests__/Banner.test.tsx b/src/components/__tests__/Banner.test.tsx index 8e0d8c90d1..048bfa7275 100644 --- a/src/components/__tests__/Banner.test.tsx +++ b/src/components/__tests__/Banner.test.tsx @@ -91,7 +91,7 @@ it('render visible banner, with custom theme', () => { visible theme={{ colors: { - text: '#00f', + onSurface: '#00f', surface: '#ccc', primary: '#043', }, diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index 43c1e9383b..f5bdeced00 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -2,10 +2,8 @@ import * as React from 'react'; import { Animated, Easing, Platform, StyleSheet } from 'react-native'; import { act, fireEvent, render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; -import { red300 } from '../../styles/themes/v2/colors'; import { MD3Colors } from '../../styles/themes/v3/tokens'; import BottomNavigation from '../BottomNavigation/BottomNavigation'; import BottomNavigationRouteScreen from '../BottomNavigation/BottomNavigationRouteScreen'; @@ -18,7 +16,7 @@ import Icon from '../Icon'; const styles = StyleSheet.create({ backgroundColor: { - backgroundColor: red300, + backgroundColor: MD3Colors.error60, }, }); @@ -378,7 +376,7 @@ it('renders custom background color passed to barStyle property', () => { ); const wrapper = getByTestId('bottom-navigation-bar-content'); - expect(wrapper).toHaveStyle({ backgroundColor: red300 }); + expect(wrapper).toHaveStyle({ backgroundColor: MD3Colors.error60 }); }); it('renders a single tab', () => { @@ -446,7 +444,7 @@ it('does not apply maxTabBarWidth styling if compact prop is falsy', () => { }); }); -it('displays ripple animation view if shifting is truthy', () => { +it('renders bar content when shifting is enabled', () => { const { getByTestId } = render( { renderScene={({ route }) => route.title} getLazy={({ route }) => route.key === 'key-2'} testID="bottom-navigation" - theme={{ isV3: false }} shifting /> ); - expect(getByTestId('bottom-navigation-bar-content-ripple')).toBeDefined(); + expect(getByTestId('bottom-navigation-bar-content')).toBeDefined(); }); -it('does not display ripple animation view if shifting is falsy', () => { +it('does not render legacy ripple overlay when shifting is disabled', () => { const { queryByTestId } = render( { describe('getActiveTintColor', () => { it.each` - activeColor | defaultColor | useV3 | expected - ${'#FBF7DB'} | ${'#fff'} | ${true} | ${'#FBF7DB'} - ${undefined} | ${'#fff'} | ${true} | ${MD3Colors.secondary10} - ${undefined} | ${'#fff'} | ${false} | ${'#fff'} + activeColor | expected + ${'#FBF7DB'} | ${'#FBF7DB'} + ${undefined} | ${MD3Colors.secondary10} `( - 'returns $expected when activeColor: $activeColor and useV3: $useV3', - ({ activeColor, defaultColor, useV3, expected }) => { - const theme = getTheme(false, useV3); - const color = getActiveTintColor({ activeColor, defaultColor, theme }); - expect(color).toBe(expected); + 'returns $expected when activeColor: $activeColor', + ({ activeColor, expected }) => { + const theme = getTheme(false); + const result = getActiveTintColor({ activeColor, theme }); + expect(result).toBe(expected); } ); }); describe('getInactiveTintColor', () => { it.each` - inactiveColor | defaultColor | useV3 | expected - ${'#853D4B'} | ${'#fff'} | ${true} | ${'#853D4B'} - ${undefined} | ${'#fff'} | ${true} | ${MD3Colors.neutralVariant30} - ${undefined} | ${'#fff'} | ${false} | ${color('#fff').alpha(0.5).rgb().string()} + inactiveColor | expected + ${'#853D4B'} | ${'#853D4B'} + ${undefined} | ${MD3Colors.neutralVariant30} `( - 'returns $expected when inactiveColor: $inactiveColor and useV3: $useV3', - ({ inactiveColor, defaultColor, useV3, expected }) => { - const theme = getTheme(false, useV3); - const color = getInactiveTintColor({ + 'returns $expected when inactiveColor: $inactiveColor', + ({ inactiveColor, expected }) => { + const theme = getTheme(false); + const result = getInactiveTintColor({ inactiveColor, - defaultColor, theme, }); - expect(color).toBe(expected); + expect(result).toBe(expected); } ); }); describe('getLabelColor', () => { it.each` - tintColor | focused | defaultColor | useV3 | expected - ${'#FBF7DB'} | ${true} | ${'#fff'} | ${true} | ${'#FBF7DB'} - ${'#853D4B'} | ${true} | ${'#fff'} | ${true} | ${'#853D4B'} - ${undefined} | ${true} | ${'#fff'} | ${true} | ${MD3Colors.neutral10} - ${undefined} | ${false} | ${'#fff'} | ${true} | ${MD3Colors.neutralVariant30} - ${undefined} | ${false} | ${'#fff'} | ${false} | ${'#fff'} - ${undefined} | ${true} | ${'#fff'} | ${false} | ${'#fff'} + tintColor | focused | expected + ${'#FBF7DB'} | ${true} | ${'#FBF7DB'} + ${'#853D4B'} | ${true} | ${'#853D4B'} + ${undefined} | ${true} | ${MD3Colors.neutral10} + ${undefined} | ${false} | ${MD3Colors.neutralVariant30} `( - 'returns $expected when tintColor: $tintColor, focused: $focused useV3: $useV3', - ({ tintColor, focused, defaultColor, useV3, expected }) => { - const theme = getTheme(false, useV3); - const color = getLabelColor({ - tintColor, + 'returns $expected when tintColor: $tintColor, focused: $focused', + ({ tintColor, focused, expected }) => { + const theme = getTheme(false); + const result = getLabelColor({ + tintColor: tintColor ?? '', hasColor: Boolean(tintColor), focused, - defaultColor, theme, }); - expect(color).toBe(expected); + expect(result).toBe(expected); } ); }); diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 8917eaca92..3d16801c15 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -2,10 +2,9 @@ import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; import { act, fireEvent, render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; -import { black, pink500, white } from '../../styles/themes/v2/colors'; +import { pink500, white } from '../../styles/themes/v2/colors'; import Button from '../Button/Button'; import { getButtonColors } from '../Button/utils'; @@ -461,95 +460,6 @@ describe('getButtonColors - background color', () => { }); }) ); - - it('should return correct theme color, for theme version 2, contained mode', () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode: 'contained', - }) - ).toMatchObject({ - backgroundColor: getTheme(false, false).colors.primary, - }); - }); - - it('should return correct theme color, for theme version 2, when disabled, contained mode', () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode: 'contained', - disabled: true, - }) - ).toMatchObject({ - backgroundColor: color(black).alpha(0.12).rgb().string(), - }); - }); - - it('should return correct theme color, for theme version 2, when disabled, dark theme, contained mode', () => { - expect( - getButtonColors({ - theme: getTheme(true, false), - mode: 'contained', - disabled: true, - }) - ).toMatchObject({ - backgroundColor: color(white).alpha(0.12).rgb().string(), - }); - }); - - (['text', 'outlined'] as const).forEach((mode) => - it(`should return correct theme color, for theme version 2, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode, - }) - ).toMatchObject({ - backgroundColor: 'transparent', - }); - }) - ); - - (['text', 'outlined'] as const).forEach((mode) => - it(`should return correct theme color, for theme version 2, dark theme, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(true, false), - mode, - }) - ).toMatchObject({ - backgroundColor: 'transparent', - }); - }) - ); - - (['text', 'outlined'] as const).forEach((mode) => - it(`should return correct theme color, for theme version 2, when disabled, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode, - disabled: true, - }) - ).toMatchObject({ - backgroundColor: 'transparent', - }); - }) - ); - - (['text', 'outlined'] as const).forEach((mode) => - it(`should return correct theme color, for theme version 2, when disabled, dark theme, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(true, false), - mode, - disabled: true, - }) - ).toMatchObject({ - backgroundColor: 'transparent', - }); - }) - ); }); describe('getButtonColors - text color', () => { @@ -675,68 +585,6 @@ describe('getButtonColors - text color', () => { textColor: getTheme(true).colors.onSecondaryContainer, }); }); - - it('should return correct theme text color, for theme version 2, when disabled, no matter what the mode is', () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - disabled: true, - mode: 'text', - }) - ).toMatchObject({ - textColor: color(black).alpha(0.32).rgb().string(), - }); - }); - - it('should return correct theme text color, for theme version 2, when disabled, dark theme, no matter what the mode is', () => { - expect( - getButtonColors({ - theme: getTheme(true, false), - disabled: true, - mode: 'text', - }) - ).toMatchObject({ - textColor: color(white).alpha(0.32).rgb().string(), - }); - }); - - it('should return correct theme text color, for theme version 2, contained mode', () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode: 'contained', - dark: true, - }) - ).toMatchObject({ - textColor: '#ffffff', - }); - }); - - (['text', 'outlined'] as const).forEach((mode) => - it(`should return correct theme text color, for theme version 2, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode, - }) - ).toMatchObject({ - textColor: getTheme(false, false).colors.primary, - }); - }) - ); - - (['text', 'outlined'] as const).forEach((mode) => - it(`should return correct theme text color, for theme version 2, dark theme, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(true, false), - mode, - }) - ).toMatchObject({ - textColor: getTheme(true, false).colors.primary, - }); - }) - ); }); describe('getButtonColors - border color', () => { @@ -813,56 +661,6 @@ describe('getButtonColors - border color', () => { }); }) ); - - it('should return correct border color, for theme version 2, outlined mode', () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode: 'outlined', - }) - ).toMatchObject({ - borderColor: color(black).alpha(0.29).rgb().string(), - }); - }); - - it('should return correct border color, for theme version 2, dark theme, outlined mode', () => { - expect( - getButtonColors({ - theme: getTheme(true, false), - mode: 'outlined', - }) - ).toMatchObject({ - borderColor: color(white).alpha(0.29).rgb().string(), - }); - }); - - (['text', 'contained', 'contained-tonal', 'elevated'] as const).forEach( - (mode) => - it(`should return transparent border, for theme version 2, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode, - }) - ).toMatchObject({ - borderColor: 'transparent', - }); - }) - ); - - (['text', 'contained', 'contained-tonal', 'elevated'] as const).forEach( - (mode) => - it(`should return transparent border, for theme version 2, dark theme, ${mode} mode`, () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode, - }) - ).toMatchObject({ - borderColor: 'transparent', - }); - }) - ); }); describe('getButtonColors - border width', () => { @@ -877,17 +675,6 @@ describe('getButtonColors - border width', () => { }); }); - it('should return correct border width, for theme version 2, outlined mode', () => { - expect( - getButtonColors({ - theme: getTheme(false, false), - mode: 'outlined', - }) - ).toMatchObject({ - borderWidth: StyleSheet.hairlineWidth, - }); - }); - (['text', 'contained', 'contained-tonal', 'elevated'] as const).forEach( (mode) => it(`should return correct border width, for ${mode} mode`, () => { diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index 3da4aa2c51..a7dc736c62 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -2,10 +2,8 @@ import React from 'react'; import { Animated, StyleSheet, Text } from 'react-native'; import { act, render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../../core/theming'; -import { black, white } from '../../../styles/themes/v2/colors'; import { MD3Colors } from '../../../styles/themes/v3/tokens'; import Button from '../../Button/Button'; import Card from '../../Card/Card'; @@ -193,15 +191,6 @@ describe('getCardColors - background color', () => { }) ).toMatchObject({ backgroundColor: undefined }); }); - - it('should return undefined, for theme version 2', () => { - expect( - getCardColors({ - theme: getTheme(false, false), - mode: undefined as any, - }) - ).toMatchObject({ backgroundColor: undefined }); - }); }); describe('getCardColors - border color', () => { @@ -213,28 +202,6 @@ describe('getCardColors - border color', () => { }) ).toMatchObject({ borderColor: getTheme().colors.outline }); }); - - it('should return correct color, for theme version 2, dark mode', () => { - expect( - getCardColors({ - theme: getTheme(true, false), - mode: undefined as any, - }) - ).toMatchObject({ - borderColor: color(white).alpha(0.12).rgb().string(), - }); - }); - - it('should return correct color, for theme version 2, light mode', () => { - expect( - getCardColors({ - theme: getTheme(false, false), - mode: undefined as any, - }) - ).toMatchObject({ - borderColor: color(black).alpha(0.12).rgb().string(), - }); - }); }); describe('getCardCoverStyle - border radius', () => { @@ -255,44 +222,6 @@ describe('getCardCoverStyle - border radius', () => { }) ).toMatchObject({ borderRadius: 3 * getTheme().roundness }); }); - - it('should return correct border radius based on roundness, for theme version 2, when index is 0 and total is 1', () => { - expect( - getCardCoverStyle({ - theme: getTheme(false, false), - borderRadiusStyles: {}, - index: 0, - total: 1, - }) - ).toMatchObject({ borderRadius: getTheme(false, false).roundness }); - }); - - it('should return correct border radius based on roundness, for theme version 2, when index is 0 and total is other than 1', () => { - expect( - getCardCoverStyle({ - theme: getTheme(false, false), - borderRadiusStyles: {}, - index: 0, - total: 2, - }) - ).toMatchObject({ - borderTopLeftRadius: getTheme(false, false).roundness, - borderTopRightRadius: getTheme(false, false).roundness, - }); - }); - - it('should return correct border radius based on roundness, for theme version 2, when index is equal to total - 1', () => { - expect( - getCardCoverStyle({ - theme: getTheme(false, false), - borderRadiusStyles: {}, - index: 1, - total: 2, - }) - ).toMatchObject({ - borderBottomLeftRadius: getTheme(false, false).roundness, - }); - }); }); it('animated value changes correctly', () => { diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index b5f1d87e77..dbd992a134 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -163,7 +163,6 @@ exports[`can render leading checkbox control 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "right", @@ -261,7 +260,6 @@ exports[`can render the Android checkbox on different platforms 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "left", @@ -478,7 +476,6 @@ exports[`can render the iOS checkbox on different platforms 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "left", @@ -659,7 +656,6 @@ exports[`renders unchecked 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "left", diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index 580e3dbedf..3e551140c5 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -22,21 +22,6 @@ describe('getAndroidSelectionControlColor - ripple color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(false, false), - disabled: true, - checked: false, - }) - ).toMatchObject({ - rippleColor: color(getTheme(false, false).colors.text) - .alpha(0.16) - .rgb() - .string(), - }); - }); - it('should return custom color', () => { expect( getAndroidSelectionControlColor({ @@ -59,20 +44,6 @@ describe('getAndroidSelectionControlColor - ripple color', () => { rippleColor: color(getTheme().colors.primary).fade(0.32).rgb().string(), }); }); - - it('should return theme color, for theme version 2', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(false, false), - checked: false, - }) - ).toMatchObject({ - rippleColor: color(getTheme(false, false).colors.accent) - .fade(0.32) - .rgb() - .string(), - }); - }); }); describe('getAndroidSelectionControlColor - checkbox color', () => { @@ -88,18 +59,6 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(false, false), - disabled: true, - checked: false, - }) - ).toMatchObject({ - selectionControlColor: getTheme(false, false).colors.disabled, - }); - }); - it('should return custom color, checked', () => { expect( getAndroidSelectionControlColor({ @@ -123,17 +82,6 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { }); }); - it('should return theme color, for theme version 2, checked', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(false, false), - checked: true, - }) - ).toMatchObject({ - selectionControlColor: getTheme(false, false).colors.accent, - }); - }); - it('should return custom color, unchecked', () => { expect( getAndroidSelectionControlColor({ @@ -156,34 +104,6 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { selectionControlColor: getTheme().colors.onSurfaceVariant, }); }); - - it('should return theme color, for theme version 2, unchecked, dark mode', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(true, false), - checked: false, - }) - ).toMatchObject({ - selectionControlColor: color(getTheme(true, false).colors.text) - .alpha(0.7) - .rgb() - .string(), - }); - }); - - it('should return theme color, for theme version 2, unchecked, light mode', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(false, false), - checked: false, - }) - ).toMatchObject({ - selectionControlColor: color(getTheme(false, false).colors.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); }); describe('getSelectionControlIOSColor - ripple color', () => { @@ -201,20 +121,6 @@ describe('getSelectionControlIOSColor - ripple color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(false, false), - disabled: true, - }) - ).toMatchObject({ - rippleColor: color(getTheme(false, false).colors.text) - .alpha(0.16) - .rgb() - .string(), - }); - }); - it('should return custom color', () => { expect( getSelectionControlIOSColor({ @@ -235,19 +141,6 @@ describe('getSelectionControlIOSColor - ripple color', () => { rippleColor: color(getTheme().colors.primary).fade(0.32).rgb().string(), }); }); - - it('should return theme color, for theme version 2', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - rippleColor: color(getTheme(false, false).colors.accent) - .fade(0.32) - .rgb() - .string(), - }); - }); }); describe('getSelectionControlIOSColor - checked color', () => { @@ -262,17 +155,6 @@ describe('getSelectionControlIOSColor - checked color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(false, false), - disabled: true, - }) - ).toMatchObject({ - checkedColor: getTheme(false, false).colors.disabled, - }); - }); - it('should return custom color, checked', () => { expect( getSelectionControlIOSColor({ @@ -293,14 +175,4 @@ describe('getSelectionControlIOSColor - checked color', () => { checkedColor: getTheme().colors.primary, }); }); - - it('should return theme color, for theme version 2, checked', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - checkedColor: getTheme(false, false).colors.accent, - }); - }); }); diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index ca3055e35b..6c0e851f22 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -5,7 +5,6 @@ import { act, render } from '@testing-library/react-native'; import color from 'color'; import { getTheme } from '../../core/theming'; -import { black, white } from '../../styles/themes/v2/colors'; import Chip from '../Chip/Chip'; import { getChipColors } from '../Chip/helpers'; @@ -104,18 +103,6 @@ describe('getChipColors - text color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getChipColors({ - disabled: true, - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - textColor: getTheme(false, false).colors.disabled, - }); - }); - it('should return correct theme color, for theme version 3, flat mode', () => { expect( getChipColors({ @@ -138,20 +125,6 @@ describe('getChipColors - text color', () => { }); }); - it('should return correct theme color, for theme version 2', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - textColor: color(getTheme(false, false).colors.text) - .alpha(0.87) - .rgb() - .string(), - }); - }); - it('should return custom color, for theme version 3', () => { expect( getChipColors({ @@ -163,18 +136,6 @@ describe('getChipColors - text color', () => { textColor: 'purple', }); }); - - it('should return custom color, for theme version 2', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - selectedColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - textColor: color('purple').alpha(0.87).rgb().string(), - }); - }); }); describe('getChipColors - icon color', () => { @@ -190,18 +151,6 @@ describe('getChipColors - icon color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getChipColors({ - disabled: true, - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - iconColor: getTheme(false, false).colors.disabled, - }); - }); - it('should return correct theme color, for theme version 3, flat mode', () => { expect( getChipColors({ @@ -224,20 +173,6 @@ describe('getChipColors - icon color', () => { }); }); - it('should return correct theme color, for theme version 2', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - iconColor: color(getTheme(false, false).colors.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); - it('should return custom color, for theme version 3', () => { expect( getChipColors({ @@ -249,18 +184,6 @@ describe('getChipColors - icon color', () => { iconColor: 'purple', }); }); - - it('should return custom color, for theme version 2', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - selectedColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - iconColor: color('purple').alpha(0.54).rgb().string(), - }); - }); }); describe('getChipColors - ripple color', () => { @@ -303,116 +226,6 @@ describe('getChipColors - ripple color', () => { rippleColor: color('purple').alpha(0.12).rgb().string(), }); }); - - it('should return custom color, for theme version 2', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - selectedColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - rippleColor: color('purple').fade(0.5).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, dark mode, outline mode, customBackgroundColor', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - customBackgroundColor: 'purple', - isOutlined: true, - }) - ).toMatchObject({ - rippleColor: color('purple').lighten(0.2).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, dark mode, flat mode, customBackgroundColor', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - rippleColor: color('purple').lighten(0.4).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, light mode, outline mode, customBackgroundColor', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - customBackgroundColor: 'purple', - isOutlined: true, - }) - ).toMatchObject({ - rippleColor: color('purple').darken(0.08).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, light mode, flat mode, customBackgroundColor', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - rippleColor: color('purple').darken(0.2).rgb().string(), - }); - }); - - it('should return theme color, for theme version 2, light mode, outline mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: true, - }) - ).toMatchObject({ - rippleColor: color(getTheme(false, false).colors.surface) - .darken(0.08) - .rgb() - .string(), - }); - }); - - it('should return theme color, for theme version 2, light mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - rippleColor: color('#ebebeb').darken(0.2).rgb().string(), - }); - }); - - it('should return theme color, for theme version 2, dark mode, outline mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - isOutlined: true, - }) - ).toMatchObject({ - rippleColor: color(getTheme(true, false).colors.surface) - .lighten(0.2) - .rgb() - .string(), - }); - }); - - it('should return theme color, for theme version 2, dark mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - isOutlined: false, - }) - ).toMatchObject({ - rippleColor: color('#383838').lighten(0.4).rgb().string(), - }); - }); }); describe('getChipColor - selected background color', () => { @@ -505,90 +318,6 @@ describe('getChipColor - selected background color', () => { .string(), }); }); - - it('should return custom color, for theme version 2, light mode, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - customBackgroundColor: 'purple', - isOutlined: true, - }) - ).toMatchObject({ - selectedBackgroundColor: color('purple').darken(0.08).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, light mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - selectedBackgroundColor: color('purple').darken(0.2).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, dark mode, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - customBackgroundColor: 'purple', - isOutlined: true, - }) - ).toMatchObject({ - selectedBackgroundColor: color('purple').lighten(0.2).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, dark mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - selectedBackgroundColor: color('purple').lighten(0.4).rgb().string(), - }); - }); - - it('should return theme color, for theme version 2, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: true, - }) - ).toMatchObject({ - selectedBackgroundColor: color(getTheme(false, false).colors.surface) - .darken(0.08) - .rgb() - .string(), - }); - }); - - it('should return theme color, for theme version 2, light mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - selectedBackgroundColor: color('#ebebeb').darken(0.2).rgb().string(), - }); - }); - - it('should return theme color, for theme version 2, dark mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - isOutlined: false, - }) - ).toMatchObject({ - selectedBackgroundColor: color('#383838').lighten(0.4).rgb().string(), - }); - }); }); describe('getChipColor - background color', () => { @@ -625,39 +354,6 @@ describe('getChipColor - background color', () => { backgroundColor: getTheme().colors.secondaryContainer, }); }); - - it('should return theme color, for theme version 2, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: true, - }) - ).toMatchObject({ - backgroundColor: getTheme(false, false).colors.surface, - }); - }); - - it('should return theme color, for theme version 2, light mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - backgroundColor: '#ebebeb', - }); - }); - - it('should return theme color, for theme version 2, dark mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - isOutlined: false, - }) - ).toMatchObject({ - backgroundColor: '#383838', - }); - }); }); describe('getChipColor - border color', () => { @@ -695,74 +391,6 @@ describe('getChipColor - border color', () => { borderColor: 'transparent', }); }); - - it('should return custom color, for theme version 2, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - selectedColor: 'purple', - isOutlined: true, - }) - ).toMatchObject({ - borderColor: color('purple').alpha(0.29).rgb().string(), - }); - }); - - it('should return custom color, for theme version 2, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - borderColor: 'purple', - }); - }); - - it('should return theme color, for theme version 2, light mode, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: true, - }) - ).toMatchObject({ - borderColor: color(black).alpha(0.29).rgb().string(), - }); - }); - - it('should return theme color, for theme version 2, dark mode, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - isOutlined: true, - }) - ).toMatchObject({ - borderColor: color(white).alpha(0.29).rgb().string(), - }); - }); - - it('should return theme background color, for theme version 2, light mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(false, false), - isOutlined: false, - }) - ).toMatchObject({ - borderColor: '#ebebeb', - }); - }); - - it('should return theme background color, for theme version 2, dark mode, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(true, false), - isOutlined: false, - }) - ).toMatchObject({ - borderColor: '#383838', - }); - }); }); it('animated value changes correctly', () => { diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index d59639efc1..74f636cf5c 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -2,12 +2,9 @@ import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; -import color from 'color'; import { act } from 'react-test-renderer'; import { getTheme } from '../../core/theming'; -import { black, white } from '../../styles/themes/v2/colors'; -import getContrastingColor from '../../utils/getContrastingColor'; import FAB from '../FAB'; import { getFABColors } from '../FAB/utils'; @@ -211,30 +208,6 @@ describe('getFABColors - background color', () => { }); }); - it('should return correct disabled color, for theme version 2, light mode', () => { - expect( - getFABColors({ - theme: getTheme(false, false), - disabled: true, - variant: 'primary', - }) - ).toMatchObject({ - backgroundColor: color(black).alpha(0.12).rgb().string(), - }); - }); - - it('should return correct disabled color, for theme version 2, dark mode', () => { - expect( - getFABColors({ - theme: getTheme(true, false), - disabled: true, - variant: 'primary', - }) - ).toMatchObject({ - backgroundColor: color(white).alpha(0.12).rgb().string(), - }); - }); - it('should return correct theme color, for theme version 3, primary variant', () => { expect( getFABColors({ @@ -278,17 +251,6 @@ describe('getFABColors - background color', () => { backgroundColor: getTheme().colors.elevation.level3, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getFABColors({ - theme: getTheme(false, false), - variant: 'primary', - }) - ).toMatchObject({ - backgroundColor: getTheme(false, false).colors.accent, - }); - }); }); describe('getFABColors - foreground color', () => { @@ -316,30 +278,6 @@ describe('getFABColors - foreground color', () => { }); }); - it('should return correct disabled color, for theme version 2, light mode', () => { - expect( - getFABColors({ - theme: getTheme(false, false), - disabled: true, - variant: 'primary', - }) - ).toMatchObject({ - foregroundColor: color(black).alpha(0.32).rgb().string(), - }); - }); - - it('should return correct disabled color, for theme version 2, dark mode', () => { - expect( - getFABColors({ - theme: getTheme(true, false), - disabled: true, - variant: 'primary', - }) - ).toMatchObject({ - foregroundColor: color(white).alpha(0.32).rgb().string(), - }); - }); - it('should return correct theme color, for theme version 3, primary variant', () => { expect( getFABColors({ @@ -383,21 +321,6 @@ describe('getFABColors - foreground color', () => { foregroundColor: getTheme().colors.primary, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getFABColors({ - theme: getTheme(false, false), - variant: 'primary', - }) - ).toMatchObject({ - foregroundColor: getContrastingColor( - getTheme(false, false).colors.accent, - white, - 'rgba(0, 0, 0, .54)' - ), - }); - }); }); it('animated value changes correctly', () => { diff --git a/src/components/__tests__/FABGroup.test.tsx b/src/components/__tests__/FABGroup.test.tsx index 50cc0a7173..3eb51c78d6 100644 --- a/src/components/__tests__/FABGroup.test.tsx +++ b/src/components/__tests__/FABGroup.test.tsx @@ -32,16 +32,6 @@ describe('getFABGroupColors - backdrop color', () => { .string(), }); }); - - it('should return correct backdrop color, for theme version 2', () => { - expect( - getFABGroupColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - backdropColor: getTheme(false, false).colors.backdrop, - }); - }); }); describe('getFABGroupColors - label color', () => { @@ -54,29 +44,6 @@ describe('getFABGroupColors - label color', () => { labelColor: getTheme().colors.onSurface, }); }); - - it('should return correct theme color, dark mode, for theme version 2', () => { - expect( - getFABGroupColors({ - theme: getTheme(true, false), - }) - ).toMatchObject({ - labelColor: getTheme(true, false).colors.text, - }); - }); - - it('should return correct theme color, light mode, for theme version 2', () => { - expect( - getFABGroupColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - labelColor: color(getTheme(false, false).colors.text) - .fade(0.54) - .rgb() - .string(), - }); - }); }); describe('getFABGroupColors - stacked FAB background color', () => { @@ -89,16 +56,6 @@ describe('getFABGroupColors - stacked FAB background color', () => { stackedFABBackgroundColor: getTheme().colors.elevation.level3, }); }); - - it('should return correct theme color, dark mode, for theme version 2', () => { - expect( - getFABGroupColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - stackedFABBackgroundColor: getTheme(false, false).colors.surface, - }); - }); }); describe('FABActions - labelStyle - containerStyle', () => { diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index 381ea5ae24..11f47cb49c 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -190,16 +190,6 @@ describe('getIconButtonColor - icon color', () => { iconColor: getTheme().colors.primary, }); }); - - it('should return theme icon color, for theme version 2', () => { - expect( - getIconButtonColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - iconColor: getTheme(false, false).colors.text, - }); - }); }); describe('getIconButtonColor - background color', () => { @@ -316,16 +306,6 @@ describe('getIconButtonColor - border color', () => { borderColor: getTheme().colors.outline, }); }); - - it('should return undefined, for theme version 2', () => { - expect( - getIconButtonColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - borderColor: undefined, - }); - }); }); describe('getIconButtonColor - ripple color', () => { @@ -341,19 +321,6 @@ describe('getIconButtonColor - ripple color', () => { .string(), }); }); - - it('should return theme color, for theme version 2', () => { - expect( - getIconButtonColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - rippleColor: color(getTheme(false, false).colors.text) - .alpha(0.32) - .rgb() - .string(), - }); - }); }); it('action animated value changes correctly', () => { diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index 0c24538348..9695b3c186 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -115,31 +115,6 @@ describe('ListAccordion', () => { }); }); -describe('getAccordionColors - title color', () => { - it('should return theme color, for theme version 3', () => { - expect( - getAccordionColors({ - theme: getTheme(), - }) - ).toMatchObject({ - titleColor: getTheme().colors.onSurface, - }); - }); - - it('should return theme color, for theme version 2', () => { - expect( - getAccordionColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - titleColor: color(getTheme(false, false).colors.text) - .alpha(0.87) - .rgb() - .string(), - }); - }); -}); - describe('getAccordionColors - description color', () => { it('should return theme color, for theme version 3', () => { expect( @@ -150,19 +125,6 @@ describe('getAccordionColors - description color', () => { descriptionColor: getTheme().colors.onSurfaceVariant, }); }); - - it('should return theme color, for theme version 2', () => { - expect( - getAccordionColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - descriptionColor: color(getTheme(false, false).colors.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); }); describe('getAccordionColors - title text color', () => { @@ -176,19 +138,6 @@ describe('getAccordionColors - title text color', () => { }); }); - it('should return theme color, for theme version 2', () => { - expect( - getAccordionColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - titleTextColor: color(getTheme(false, false).colors.text) - .alpha(0.87) - .rgb() - .string(), - }); - }); - it('should return primary color if it is expanded', () => { expect( getAccordionColors({ @@ -215,21 +164,6 @@ describe('getAccordionColors - ripple color', () => { }); }); - it('should return theme color, for theme version 2', () => { - const v2TextColor = color(getTheme(false, false).colors.text) - .alpha(0.87) - .rgb() - .string(); - - expect( - getAccordionColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - rippleColor: color(v2TextColor).alpha(0.12).rgb().string(), - }); - }); - it('should return primary color if it is expanded', () => { expect( getAccordionColors({ diff --git a/src/components/__tests__/ListUtils.test.tsx b/src/components/__tests__/ListUtils.test.tsx index 4aeefb6ca9..a9889834d8 100644 --- a/src/components/__tests__/ListUtils.test.tsx +++ b/src/components/__tests__/ListUtils.test.tsx @@ -5,18 +5,11 @@ import { getLeftStyles, getRightStyles } from '../List/utils'; import Text from '../Typography/Text'; const styles = StyleSheet.create({ - leftItem: { - marginLeft: 0, - marginRight: 16, - }, leftItemV3: { marginLeft: 16, marginRight: 0, alignSelf: 'center', }, - rightItem: { - marginRight: 0, - }, rightItemV3: { marginLeft: 16, marginRight: 0, @@ -30,23 +23,13 @@ const description = Test; * ********************** getLeftStyles ********************** * */ -it('returns styles for left item without description for V2', () => { - const style = getLeftStyles(false, null, false); - expect(style).toStrictEqual({ ...styles.leftItem, marginVertical: 0 }); -}); - -it('returns styles for left item w/ desctiption for V2', () => { - const style = getLeftStyles(false, description, false); - expect(style).toStrictEqual(styles.leftItem); -}); - -it('returns styles for left item without description for V3', () => { - const style = getLeftStyles(false, null, true); +it('returns styles for left item without description', () => { + const style = getLeftStyles(false, null); expect(style).toStrictEqual({ ...styles.leftItemV3, marginVertical: 0 }); }); -it('returns styles for left item w/ desctiption for V3', () => { - const style = getLeftStyles(true, description, true); +it('returns styles for left item w/ desctiption', () => { + const style = getLeftStyles(true, description); expect(style).toStrictEqual({ ...styles.leftItemV3, alignSelf: 'flex-start', @@ -57,23 +40,13 @@ it('returns styles for left item w/ desctiption for V3', () => { * ********************** getRightStyles ********************** * */ -it('returns styles for right item without description for V2', () => { - const style = getRightStyles(false, null, false); - expect(style).toStrictEqual({ ...styles.rightItem, marginVertical: 0 }); -}); - -it('returns styles for right item w/ desctiption for V2', () => { - const style = getRightStyles(false, description, false); - expect(style).toStrictEqual(styles.rightItem); -}); - -it('returns styles for right item without description for V3', () => { - const style = getRightStyles(false, null, true); +it('returns styles for right item without description', () => { + const style = getRightStyles(false, null); expect(style).toStrictEqual({ ...styles.rightItemV3, marginVertical: 0 }); }); -it('returns styles for right item w/ desctiption for V3', () => { - const style = getRightStyles(true, description, true); +it('returns styles for right item w/ desctiption', () => { + const style = getRightStyles(true, description); expect(style).toStrictEqual({ ...styles.rightItemV3, alignSelf: 'flex-start', diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 29397d1262..21ed4d0802 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -70,7 +70,7 @@ it('renders menu with content styles', () => { ([0, 1, 2, 3, 4, 5] as MD3Elevation[]).forEach((elevation) => it(`renders menu with background color based on elevation value = ${elevation}`, () => { - const theme = getTheme(false, true); + const theme = getTheme(); const { getByTestId } = render( diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index 7413f43622..d482800582 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -4,7 +4,6 @@ import { render } from '@testing-library/react-native'; import color from 'color'; import { getTheme } from '../../core/theming'; -import { black, white } from '../../styles/themes/v2/colors'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; @@ -75,28 +74,6 @@ describe('getMenuItemColor - title color', () => { ).toMatchObject({ titleColor: getTheme().colors.onSurfaceDisabled }); }); - it('should return disabled color if disabled, for theme version 2, light theme', () => { - expect( - getMenuItemColor({ - theme: getTheme(false, false), - disabled: true, - }) - ).toMatchObject({ - titleColor: color(black).alpha(0.32).rgb().string(), - }); - }); - - it('should return disabled color if disabled, for theme version 2, dark theme', () => { - expect( - getMenuItemColor({ - theme: getTheme(true, false), - disabled: true, - }) - ).toMatchObject({ - titleColor: color(white).alpha(0.32).rgb().string(), - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getMenuItemColor({ @@ -106,19 +83,6 @@ describe('getMenuItemColor - title color', () => { titleColor: getTheme().colors.onSurface, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getMenuItemColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - titleColor: color(getTheme(false, false).colors.text) - .alpha(0.87) - .rgb() - .string(), - }); - }); }); describe('getMenuItemColor - icon color', () => { @@ -131,28 +95,6 @@ describe('getMenuItemColor - icon color', () => { ).toMatchObject({ iconColor: getTheme().colors.onSurfaceDisabled }); }); - it('should return disabled color if disabled, for theme version 2, light theme', () => { - expect( - getMenuItemColor({ - theme: getTheme(false, false), - disabled: true, - }) - ).toMatchObject({ - iconColor: color(black).alpha(0.32).rgb().string(), - }); - }); - - it('should return disabled color if disabled, for theme version 2, dark theme', () => { - expect( - getMenuItemColor({ - theme: getTheme(true, false), - disabled: true, - }) - ).toMatchObject({ - iconColor: color(white).alpha(0.32).rgb().string(), - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getMenuItemColor({ @@ -162,19 +104,6 @@ describe('getMenuItemColor - icon color', () => { iconColor: getTheme().colors.onSurfaceVariant, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getMenuItemColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - iconColor: color(getTheme(false, false).colors.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); }); describe('getMenuItemColor - ripple color', () => { @@ -190,14 +119,4 @@ describe('getMenuItemColor - ripple color', () => { .string(), }); }); - - it('should return undefined, for theme version 2', () => { - expect( - getMenuItemColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - rippleColor: undefined, - }); - }); }); diff --git a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap index dfbe5fa131..e74650efd2 100644 --- a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap +++ b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap @@ -161,7 +161,6 @@ exports[`can render leading radio button control 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "right", @@ -256,7 +255,6 @@ exports[`can render the Android radio button on different platforms 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "left", @@ -408,7 +406,6 @@ exports[`can render the iOS radio button on different platforms 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "left", @@ -586,7 +583,6 @@ exports[`renders unchecked 1`] = ` "flexGrow": 1, "flexShrink": 1, }, - false, { "color": "rgba(28, 27, 31, 1)", "textAlign": "left", diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index d36babb5bb..8475751f41 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -1,10 +1,8 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; -import { black } from '../../styles/themes/v2/colors'; import SegmentedButtons from '../SegmentedButtons/SegmentedButtons'; import { getDisabledSegmentedButtonStyle, @@ -56,24 +54,18 @@ it('renders checked segmented button with selected check', async () => { describe('getSegmentedButtonColors', () => { it.each` - theme | disabled | checked | checkedColor | uncheckedColor | expected - ${getTheme()} | ${false} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSecondaryContainer} - ${getTheme()} | ${false} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} - ${getTheme()} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} - ${getTheme()} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} - ${getTheme()} | ${false} | ${true} | ${'a125f5'} | ${undefined} | ${'a125f5'} - ${getTheme()} | ${false} | ${false} | ${undefined} | ${'000'} | ${'000'} - ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${'000'} | ${'000'} - ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${undefined} | ${getTheme().colors.onSurface} - ${getTheme()} | ${false} | ${true} | ${undefined} | ${'000'} | ${getTheme().colors.onSecondaryContainer} - ${getTheme(false, false)} | ${false} | ${false} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.primary} - ${getTheme(false, false)} | ${false} | ${true} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.primary} - ${getTheme(false, false)} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.disabled} - ${getTheme(false, false)} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.disabled} - ${getTheme(false, false)} | ${false} | ${false} | ${'a125f5'} | ${undefined} | ${getTheme(false, false).colors.primary} - ${getTheme(false, false)} | ${false} | ${true} | ${undefined} | ${'000'} | ${getTheme(false, false).colors.primary} + theme | disabled | checked | checkedColor | uncheckedColor | expected + ${getTheme()} | ${false} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSecondaryContainer} + ${getTheme()} | ${false} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} + ${getTheme()} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} + ${getTheme()} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} + ${getTheme()} | ${false} | ${true} | ${'a125f5'} | ${undefined} | ${'a125f5'} + ${getTheme()} | ${false} | ${false} | ${undefined} | ${'000'} | ${'000'} + ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${'000'} | ${'000'} + ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${undefined} | ${getTheme().colors.onSurface} + ${getTheme()} | ${false} | ${true} | ${undefined} | ${'000'} | ${getTheme().colors.onSecondaryContainer} `( - 'returns $expected when disabled: $disabled, checked: $checked, checkedColor is $checkedColor and uncheckedColor is $uncheckedColor and isV3: $theme.isV3', + 'returns $expected when disabled: $disabled, checked: $checked, checkedColor is $checkedColor and uncheckedColor is $uncheckedColor', ({ theme, disabled, checked, checkedColor, uncheckedColor, expected }) => { expect( getSegmentedButtonColors({ @@ -97,25 +89,10 @@ describe('getSegmentedButtonColors', () => { ).toMatchObject({ backgroundColor: getTheme().colors.secondaryContainer }); }); - it('should return correct background color when checked and theme version 2', () => { - expect( - getSegmentedButtonColors({ - theme: getTheme(false, false), - disabled: false, - checked: true, - }) - ).toMatchObject({ - backgroundColor: color(getTheme(false, false).colors.primary) - .alpha(0.12) - .rgb() - .string(), - }); - }); - it('should return correct background color when uncheked (V3 & V2)', () => { expect( getSegmentedButtonColors({ - theme: getTheme(false, false), + theme: getTheme(), disabled: false, checked: false, }) @@ -136,18 +113,6 @@ describe('getSegmentedButtonColors', () => { }); }); - it('should return correct border color with theme version 2', () => { - expect( - getSegmentedButtonColors({ - theme: getTheme(false, false), - disabled: false, - checked: false, - }) - ).toMatchObject({ - borderColor: color(black).alpha(0.29).rgb().string(), - }); - }); - it('should return correct border color when disabled and theme version 3', () => { expect( getSegmentedButtonColors({ @@ -172,18 +137,6 @@ describe('getSegmentedButtonColors', () => { }); }); - it('should return correct textColor with theme version 2', () => { - expect( - getSegmentedButtonColors({ - theme: getTheme(false, false), - disabled: false, - checked: false, - }) - ).toMatchObject({ - textColor: getTheme(false, false).colors.primary, - }); - }); - it('should return correct textColor when disabled and theme version 3', () => { expect( getSegmentedButtonColors({ @@ -195,18 +148,6 @@ describe('getSegmentedButtonColors', () => { textColor: getTheme().colors.onSurfaceDisabled, }); }); - - it('should return correct textColor when disabled and theme version 2', () => { - expect( - getSegmentedButtonColors({ - theme: getTheme(false, false), - disabled: true, - checked: false, - }) - ).toMatchObject({ - textColor: getTheme(false, false).colors.disabled, - }); - }); }); describe('getDisabledSegmentedButtonBorderWidth', () => { diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index e475b9a166..dfaa028b43 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -62,16 +62,6 @@ describe('getSwitchColor - checked color', () => { checkedColor: getTheme().colors.primary, }); }); - - it('should return theme color, for theme version 2', () => { - expect( - getSwitchColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - checkedColor: getTheme(false, false).colors.accent, - }); - }); }); describe('getSwitchColor - thumb tint color', () => { @@ -163,24 +153,12 @@ describe('getSwitchColor - on tint color', () => { }); }); - it('should return checked color for iOS platform, for theme version 2', () => { - Platform.OS = 'ios'; - - expect( - getSwitchColor({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - onTintColor: getTheme(false, false).colors.accent, - }); - }); - it('should return custom color for iOS platform', () => { Platform.OS = 'ios'; expect( getSwitchColor({ - theme: getTheme(false, false), + theme: getTheme(), color: 'purple', }) ).toMatchObject({ @@ -201,17 +179,6 @@ describe('getSwitchColor - on tint color', () => { }); }); - it('should return correct disabled color, for theme version 2, dark mode', () => { - expect( - getSwitchColor({ - theme: getTheme(true, false), - disabled: true, - }) - ).toMatchObject({ - onTintColor: color(white).alpha(0.1).rgb().string(), - }); - }); - it('should return correct disabled color, light mode', () => { expect( getSwitchColor({ diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index e190a537d6..c92ed138c4 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -551,17 +551,6 @@ describe('getFlatInputColor - underline color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - underlineColorCustom: 'transparent', - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getFlatInputColors({ @@ -572,16 +561,6 @@ describe('getFlatInputColor - underline color', () => { }); }); - it('should return correct theme color, for theme version 2', () => { - expect( - getFlatInputColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - underlineColorCustom: getTheme(false, false).colors.disabled, - }); - }); - it('should return custom color, no matter what the theme is', () => { expect( getFlatInputColors({ @@ -595,7 +574,7 @@ describe('getFlatInputColor - underline color', () => { expect( getFlatInputColors({ underlineColor: 'beige', - theme: getTheme(false, false), + theme: getTheme(), }) ).toMatchObject({ underlineColorCustom: 'beige', @@ -617,7 +596,7 @@ describe('getFlatInputColor - input text color', () => { expect( getOutlinedInputColors({ textColor: 'beige', - theme: getTheme(false, false), + theme: getTheme(), }) ).toMatchObject({ inputTextColor: 'beige', @@ -635,20 +614,6 @@ describe('getFlatInputColor - input text color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - inputTextColor: color(getTheme(false, false).colors?.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getFlatInputColors({ @@ -658,16 +623,6 @@ describe('getFlatInputColor - input text color', () => { inputTextColor: getTheme().colors.onSurface, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getFlatInputColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - inputTextColor: getTheme(false, false).colors.text, - }); - }); }); describe('getFlatInputColor - placeholder color', () => { @@ -682,17 +637,6 @@ describe('getFlatInputColor - placeholder color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - placeholderColor: getTheme(false, false).colors.disabled, - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getFlatInputColors({ @@ -702,16 +646,6 @@ describe('getFlatInputColor - placeholder color', () => { placeholderColor: getTheme().colors.onSurfaceVariant, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getFlatInputColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - placeholderColor: getTheme(false, false).colors.placeholder, - }); - }); }); describe('getFlatInputColor - background color', () => { @@ -740,17 +674,6 @@ describe('getFlatInputColor - background color', () => { }); }); - it('should return undefined when disabled, for theme version 2', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - backgroundColor: undefined, - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getFlatInputColors({ @@ -760,32 +683,6 @@ describe('getFlatInputColor - background color', () => { backgroundColor: getTheme().colors.surfaceVariant, }); }); - - it('should return correct theme color, for theme version 2, light mode', () => { - expect( - getFlatInputColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - backgroundColor: color(getTheme(false, false).colors?.background) - .darken(0.06) - .rgb() - .string(), - }); - }); - - it('should return correct theme color, for theme version 2, dark mode', () => { - expect( - getFlatInputColors({ - theme: getTheme(true, false), - }) - ).toMatchObject({ - backgroundColor: color(getTheme(true, false).colors?.background) - .lighten(0.24) - .rgb() - .string(), - }); - }); }); describe('getFlatInputColor - error color', () => { @@ -802,10 +699,10 @@ describe('getFlatInputColor - error color', () => { expect( getFlatInputColors({ error: true, - theme: getTheme(false, true), + theme: getTheme(), }) ).toMatchObject({ - errorColor: getTheme(false, true).colors.error, + errorColor: getTheme().colors.error, }); }); }); @@ -822,20 +719,6 @@ describe('getFlatInputColor - active color', () => { }); }); - it('should return disabled color, for theme version 2', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - activeColor: color(getTheme(false, false).colors?.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); - it('should return correct active color, if error, no matter what the theme is', () => { expect( getFlatInputColors({ @@ -849,10 +732,10 @@ describe('getFlatInputColor - active color', () => { expect( getFlatInputColors({ error: true, - theme: getTheme(false, true), + theme: getTheme(), }) ).toMatchObject({ - activeColor: getTheme(false, true).colors.error, + activeColor: getTheme().colors.error, }); }); @@ -869,7 +752,7 @@ describe('getFlatInputColor - active color', () => { expect( getFlatInputColors({ activeUnderlineColor: 'beige', - theme: getTheme(false, false), + theme: getTheme(), }) ).toMatchObject({ activeColor: 'beige', @@ -885,16 +768,6 @@ describe('getFlatInputColor - active color', () => { activeColor: getTheme().colors.primary, }); }); - - it('should return theme active color, for theme version 2', () => { - expect( - getFlatInputColors({ - theme: getTheme(false, true), - }) - ).toMatchObject({ - activeColor: getTheme(false, true).colors.primary, - }); - }); }); describe('getOutlinedInputColors - outline color', () => { @@ -920,29 +793,6 @@ describe('getOutlinedInputColors - outline color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - outlineColor: getTheme(false, false).colors.disabled, - }); - }); - - it('should return custom color as disabled, when it is transparent, for theme version 2', () => { - expect( - getOutlinedInputColors({ - disabled: true, - customOutlineColor: 'transparent', - theme: getTheme(false, false), - }) - ).toMatchObject({ - outlineColor: 'transparent', - }); - }); - it('should return custom color, if not disabled, no matter what the theme is', () => { expect( getOutlinedInputColors({ @@ -956,7 +806,7 @@ describe('getOutlinedInputColors - outline color', () => { expect( getOutlinedInputColors({ customOutlineColor: 'beige', - theme: getTheme(false, false), + theme: getTheme(), }) ).toMatchObject({ outlineColor: 'beige', @@ -972,16 +822,6 @@ describe('getOutlinedInputColors - outline color', () => { outlineColor: getTheme().colors.outline, }); }); - - it('should return theme color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - outlineColor: getTheme(false, false).colors.placeholder, - }); - }); }); describe('getOutlinedInputColors - input text color', () => { @@ -996,20 +836,6 @@ describe('getOutlinedInputColors - input text color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - inputTextColor: color(getTheme(false, false).colors?.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getOutlinedInputColors({ @@ -1019,16 +845,6 @@ describe('getOutlinedInputColors - input text color', () => { inputTextColor: getTheme().colors.onSurface, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - inputTextColor: getTheme(false, false).colors.text, - }); - }); }); describe('getOutlinedInputColors - placeholder color', () => { @@ -1043,17 +859,6 @@ describe('getOutlinedInputColors - placeholder color', () => { }); }); - it('should return correct disabled color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - placeholderColor: getTheme(false, false).colors.disabled, - }); - }); - it('should return correct theme color, for theme version 3', () => { expect( getOutlinedInputColors({ @@ -1063,16 +868,6 @@ describe('getOutlinedInputColors - placeholder color', () => { placeholderColor: getTheme().colors.onSurfaceVariant, }); }); - - it('should return correct theme color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(false, false), - }) - ).toMatchObject({ - placeholderColor: getTheme(false, false).colors.placeholder, - }); - }); }); describe('getOutlinedInputColors - error color', () => { @@ -1089,10 +884,10 @@ describe('getOutlinedInputColors - error color', () => { expect( getOutlinedInputColors({ error: true, - theme: getTheme(false, true), + theme: getTheme(), }) ).toMatchObject({ - errorColor: getTheme(false, true).colors.error, + errorColor: getTheme().colors.error, }); }); }); @@ -1109,20 +904,6 @@ describe('getOutlinedInputColors - active color', () => { }); }); - it('should return disabled color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(false, false), - }) - ).toMatchObject({ - activeColor: color(getTheme(false, false).colors?.text) - .alpha(0.54) - .rgb() - .string(), - }); - }); - it('should return correct active color, if error, no matter what the theme is', () => { expect( getOutlinedInputColors({ @@ -1136,10 +917,10 @@ describe('getOutlinedInputColors - active color', () => { expect( getOutlinedInputColors({ error: true, - theme: getTheme(false, true), + theme: getTheme(), }) ).toMatchObject({ - activeColor: getTheme(false, true).colors.error, + activeColor: getTheme().colors.error, }); }); @@ -1156,7 +937,7 @@ describe('getOutlinedInputColors - active color', () => { expect( getOutlinedInputColors({ activeOutlineColor: 'beige', - theme: getTheme(false, false), + theme: getTheme(), }) ).toMatchObject({ activeColor: 'beige', @@ -1172,16 +953,6 @@ describe('getOutlinedInputColors - active color', () => { activeColor: getTheme().colors.primary, }); }); - - it('should return theme active color, for theme version 2', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(false, true), - }) - ).toMatchObject({ - activeColor: getTheme(false, true).colors.primary, - }); - }); }); describe('outlineStyle - underlineStyle', () => { diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx index 3ab234d0f3..a0e2bbafcb 100644 --- a/src/components/__tests__/ToggleButton.test.tsx +++ b/src/components/__tests__/ToggleButton.test.tsx @@ -68,18 +68,6 @@ describe('getToggleButtonColor', () => { ); }); - it('should return correct color when checked and theme version 2', () => { - expect( - getToggleButtonColor({ theme: getTheme(false, false), checked: true }) - ).toBe('rgba(0, 0, 0, .08)'); - }); - - it('should return correct color when checked and theme version 2, dark theme', () => { - expect( - getToggleButtonColor({ theme: getTheme(true, false), checked: true }) - ).toBe('rgba(255, 255, 255, .12)'); - }); - it('should return transparent color when not checked', () => { expect(getToggleButtonColor({ theme: getTheme(), checked: false })).toBe( 'transparent' diff --git a/src/components/__tests__/Typography/Caption.test.tsx b/src/components/__tests__/Typography/Caption.test.tsx deleted file mode 100644 index b85bee50d4..0000000000 --- a/src/components/__tests__/Typography/Caption.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from 'react'; -import { StyleSheet } from 'react-native'; - -import { render } from '@testing-library/react-native'; - -import { red500 } from '../../../styles/themes/v2/colors'; -import Caption from '../../Typography/v2/Caption'; - -const style = StyleSheet.create({ - caption: { - fontSize: 20, - color: red500, - }, -}); - -it('renders caption with children as content', () => { - const tree = render(Caption content).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders caption applying style', () => { - const tree = render( - Big and red caption - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); diff --git a/src/components/__tests__/Typography/__snapshots__/Caption.test.tsx.snap b/src/components/__tests__/Typography/__snapshots__/Caption.test.tsx.snap deleted file mode 100644 index 1120e943ca..0000000000 --- a/src/components/__tests__/Typography/__snapshots__/Caption.test.tsx.snap +++ /dev/null @@ -1,74 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders caption applying style 1`] = ` - - Big and red caption - -`; - -exports[`renders caption with children as content 1`] = ` - - Caption content - -`; diff --git a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap index b1aaff7ab0..175383e4ad 100644 --- a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap @@ -82,21 +82,25 @@ exports[`render visible banner, with custom theme 1`] = ` }, { "color": "rgba(28, 27, 31, 1)", - "fontFamily": "System", - "fontWeight": "400", - "letterSpacing": 0, - }, - { "writingDirection": "ltr", }, [ { - "flex": 1, - "margin": 8, - }, - { - "color": "rgba(28, 27, 31, 1)", + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, }, + [ + { + "flex": 1, + "margin": 8, + }, + { + "color": "#00f", + }, + ], ], ] } @@ -234,7 +238,6 @@ exports[`render visible banner, with custom theme 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -364,21 +367,25 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` }, { "color": "rgba(28, 27, 31, 1)", - "fontFamily": "System", - "fontWeight": "400", - "letterSpacing": 0, - }, - { "writingDirection": "ltr", }, [ { - "flex": 1, - "margin": 8, - }, - { - "color": "rgba(28, 27, 31, 1)", + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, }, + [ + { + "flex": 1, + "margin": 8, + }, + { + "color": "rgba(28, 27, 31, 1)", + }, + ], ], ] } @@ -505,21 +512,25 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` }, { "color": "rgba(28, 27, 31, 1)", - "fontFamily": "System", - "fontWeight": "400", - "letterSpacing": 0, - }, - { "writingDirection": "ltr", }, [ { - "flex": 1, - "margin": 8, - }, - { - "color": "rgba(28, 27, 31, 1)", + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, }, + [ + { + "flex": 1, + "margin": 8, + }, + { + "color": "rgba(28, 27, 31, 1)", + }, + ], ], ] } @@ -657,7 +668,6 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -775,21 +785,25 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` }, { "color": "rgba(28, 27, 31, 1)", - "fontFamily": "System", - "fontWeight": "400", - "letterSpacing": 0, - }, - { "writingDirection": "ltr", }, [ { - "flex": 1, - "margin": 8, - }, - { - "color": "rgba(28, 27, 31, 1)", + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, }, + [ + { + "flex": 1, + "margin": 8, + }, + { + "color": "rgba(28, 27, 31, 1)", + }, + ], ], ] } @@ -927,7 +941,6 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -1077,7 +1090,6 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -1217,21 +1229,25 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` }, { "color": "rgba(28, 27, 31, 1)", - "fontFamily": "System", - "fontWeight": "400", - "letterSpacing": 0, - }, - { "writingDirection": "ltr", }, [ { - "flex": 1, - "margin": 8, - }, - { - "color": "rgba(28, 27, 31, 1)", + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, }, + [ + { + "flex": 1, + "margin": 8, + }, + { + "color": "rgba(28, 27, 31, 1)", + }, + ], ], ] } @@ -1336,21 +1352,25 @@ exports[`renders visible banner, without action buttons and without image 1`] = }, { "color": "rgba(28, 27, 31, 1)", - "fontFamily": "System", - "fontWeight": "400", - "letterSpacing": 0, - }, - { "writingDirection": "ltr", }, [ { - "flex": 1, - "margin": 8, - }, - { - "color": "rgba(28, 27, 31, 1)", + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, }, + [ + { + "flex": 1, + "margin": 8, + }, + { + "color": "rgba(28, 27, 31, 1)", + }, + ], ], ] } diff --git a/src/components/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/__tests__/__snapshots__/Button.test.tsx.snap index db8e187690..d5f335c3f1 100644 --- a/src/components/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Button.test.tsx.snap @@ -122,7 +122,6 @@ exports[`renders button with an accessibility hint 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -273,7 +272,6 @@ exports[`renders button with an accessibility label 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -423,7 +421,6 @@ exports[`renders button with button color 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -573,7 +570,6 @@ exports[`renders button with color 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -723,7 +719,6 @@ exports[`renders button with custom testID 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -922,7 +917,6 @@ exports[`renders button with icon 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 16, }, @@ -1123,7 +1117,6 @@ exports[`renders button with icon in reverse order 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 16, }, @@ -1273,7 +1266,6 @@ exports[`renders contained contained with mode 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 24, "marginVertical": 10, @@ -1424,7 +1416,6 @@ exports[`renders disabled button 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -1782,7 +1773,6 @@ exports[`renders loading button 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 16, }, @@ -1932,7 +1922,6 @@ exports[`renders outlined button with mode 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 24, "marginVertical": 10, @@ -2083,7 +2072,6 @@ exports[`renders text button by default 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, @@ -2233,7 +2221,6 @@ exports[`renders text button with mode 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index a6ffbfcb06..59e80d0aef 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -1801,7 +1801,6 @@ exports[`DataTable.Pagination renders data table pagination with options select "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 24, "marginVertical": 10, diff --git a/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap b/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap index 78e3ed8612..e3d5e8ca8b 100644 --- a/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap @@ -43,10 +43,6 @@ exports[`renders DrawerItem with icon 1`] = ` "marginHorizontal": 10, "marginVertical": 4, }, - { - "backgroundColor": undefined, - "borderRadius": 28, - }, { "height": 56, "justifyContent": "center", @@ -54,6 +50,10 @@ exports[`renders DrawerItem with icon 1`] = ` "marginRight": 12, "marginVertical": 0, }, + { + "backgroundColor": undefined, + "borderRadius": 28, + }, undefined, ], ] @@ -202,10 +202,6 @@ exports[`renders active DrawerItem 1`] = ` "marginHorizontal": 10, "marginVertical": 4, }, - { - "backgroundColor": "rgba(232, 222, 248, 1)", - "borderRadius": 28, - }, { "height": 56, "justifyContent": "center", @@ -213,6 +209,10 @@ exports[`renders active DrawerItem 1`] = ` "marginRight": 12, "marginVertical": 0, }, + { + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 28, + }, undefined, ], ] @@ -361,10 +361,6 @@ exports[`renders basic DrawerItem 1`] = ` "marginHorizontal": 10, "marginVertical": 4, }, - { - "backgroundColor": undefined, - "borderRadius": 28, - }, { "height": 56, "justifyContent": "center", @@ -372,6 +368,10 @@ exports[`renders basic DrawerItem 1`] = ` "marginRight": 12, "marginVertical": 0, }, + { + "backgroundColor": undefined, + "borderRadius": 28, + }, undefined, ], ] diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index b5bc07c4b8..a2ed0f8c8e 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -170,9 +170,7 @@ exports[`renders list section with custom title style 1`] = ` "lineHeight": 20, }, }, - "isV3": true, "roundness": 4, - "version": 3, } } > @@ -697,9 +695,7 @@ exports[`renders list section with subheader 1`] = ` "lineHeight": 20, }, }, - "isV3": true, "roundness": 4, - "version": 3, } } > @@ -1222,9 +1218,7 @@ exports[`renders list section without subheader 1`] = ` "lineHeight": 20, }, }, - "isV3": true, "roundness": 4, - "version": 3, } } > diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index fc3ac61473..f2a20c8047 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -134,7 +134,6 @@ exports[`renders menu with content styles 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 24, "marginVertical": 10, @@ -371,7 +370,6 @@ exports[`renders menu with content styles 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -408,7 +406,6 @@ exports[`renders menu with content styles 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 1)", "fontFamily": "System", @@ -494,7 +491,6 @@ exports[`renders menu with content styles 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -531,7 +527,6 @@ exports[`renders menu with content styles 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 1)", "fontFamily": "System", @@ -693,7 +688,6 @@ exports[`renders not visible menu 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 24, "marginVertical": 10, @@ -859,7 +853,6 @@ exports[`renders visible menu 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 24, "marginVertical": 10, @@ -1092,7 +1085,6 @@ exports[`renders visible menu 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -1129,7 +1121,6 @@ exports[`renders visible menu 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 1)", "fontFamily": "System", @@ -1215,7 +1206,6 @@ exports[`renders visible menu 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -1252,7 +1242,6 @@ exports[`renders visible menu 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 1)", "fontFamily": "System", diff --git a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap index e16c6134f4..a34067f576 100644 --- a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap @@ -67,7 +67,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="box-none" style={ [ - false, { "width": 24, }, @@ -108,7 +107,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -145,7 +143,6 @@ exports[`Menu Item renders menu item 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 1)", "fontFamily": "System", @@ -231,7 +228,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="box-none" style={ [ - false, { "width": 24, }, @@ -272,7 +268,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -309,7 +304,6 @@ exports[`Menu Item renders menu item 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 1)", "fontFamily": "System", @@ -395,7 +389,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="box-none" style={ [ - false, { "width": 24, }, @@ -436,7 +429,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -473,7 +465,6 @@ exports[`Menu Item renders menu item 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 0.38)", "fontFamily": "System", @@ -559,7 +550,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="box-none" style={ [ - false, { "width": 24, }, @@ -600,7 +590,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -637,7 +626,6 @@ exports[`Menu Item renders menu item 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 0.38)", "fontFamily": "System", @@ -723,7 +711,6 @@ exports[`Menu Item renders menu item 1`] = ` pointerEvents="none" style={ [ - false, { "justifyContent": "center", }, @@ -760,7 +747,6 @@ exports[`Menu Item renders menu item 1`] = ` "lineHeight": 24, }, [ - false, { "color": "rgba(28, 27, 31, 1)", "fontFamily": "System", diff --git a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap index 9fdd75d4dc..b859166348 100644 --- a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap @@ -440,7 +440,6 @@ exports[`renders snackbar with action button 1`] = ` "marginVertical": 9, "textAlign": "center", }, - false, { "marginHorizontal": 12, }, diff --git a/src/core/PaperProvider.tsx b/src/core/PaperProvider.tsx index 44fd426ab5..01a80fdcd9 100644 --- a/src/core/PaperProvider.tsx +++ b/src/core/PaperProvider.tsx @@ -8,7 +8,7 @@ import { import SafeAreaProviderCompat from './SafeAreaProviderCompat'; import { Provider as SettingsProvider, Settings } from './settings'; -import { defaultThemesByVersion, ThemeProvider } from './theming'; +import { defaultThemes, ThemeProvider } from './theming'; import MaterialCommunityIcon from '../components/MaterialCommunityIcon'; import PortalHost from '../components/Portal/PortalHost'; import type { ThemeProp } from '../types'; @@ -21,12 +21,8 @@ export type Props = { }; const PaperProvider = (props: Props) => { - const isOnlyVersionInTheme = - props.theme && Object.keys(props.theme).length === 1 && props.theme.version; - const colorSchemeName = - ((!props.theme || isOnlyVersionInTheme) && Appearance?.getColorScheme()) || - 'light'; + (!props.theme && Appearance?.getColorScheme()) || 'light'; const [reduceMotionEnabled, setReduceMotionEnabled] = React.useState(false); @@ -59,13 +55,13 @@ const PaperProvider = (props: Props) => { React.useEffect(() => { let appearanceSubscription: NativeEventSubscription | undefined; - if (!props.theme || isOnlyVersionInTheme) { + if (!props.theme) { appearanceSubscription = Appearance?.addChangeListener( handleAppearanceChange ) as NativeEventSubscription | undefined; } return () => { - if (!props.theme || isOnlyVersionInTheme) { + if (!props.theme) { if (appearanceSubscription) { appearanceSubscription.remove(); } else { @@ -74,27 +70,20 @@ const PaperProvider = (props: Props) => { } } }; - }, [props.theme, isOnlyVersionInTheme]); + }, [props.theme]); const theme = React.useMemo(() => { - const themeVersion = props.theme?.version || 3; const scheme = colorScheme === 'dark' ? 'dark' : 'light'; - const defaultThemeBase = defaultThemesByVersion[themeVersion][scheme]; + const defaultThemeBase = defaultThemes[scheme]; - const extendedThemeBase = { + return { ...defaultThemeBase, ...props.theme, - version: themeVersion, animation: { ...props.theme?.animation, scale: reduceMotionEnabled ? 0 : 1, }, }; - - return { - ...extendedThemeBase, - isV3: extendedThemeBase.version === 3, - }; }, [colorScheme, props.theme, reduceMotionEnabled]); const { children, settings } = props; diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 11c4beff49..03ebbdc9f7 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -8,12 +8,7 @@ import { import { render, act } from '@testing-library/react-native'; -import { - MD2LightTheme, - MD2DarkTheme, - MD3LightTheme, - MD3DarkTheme, -} from '../../styles/themes'; +import { MD3LightTheme, MD3DarkTheme } from '../../styles/themes'; import type { ThemeProp } from '../../types'; import PaperProvider from '../PaperProvider'; import { useTheme } from '../theming'; @@ -107,8 +102,8 @@ const createProvider = (theme?: ThemeProp) => { ); }; -const ExtendedLightTheme = { ...MD3LightTheme, isV3: true } as ThemeProp; -const ExtendedDarkTheme = { ...MD3DarkTheme, isV3: true } as ThemeProp; +const ExtendedLightTheme = { ...MD3LightTheme } as ThemeProp; +const ExtendedDarkTheme = { ...MD3DarkTheme } as ThemeProp; describe('PaperProvider', () => { beforeEach(() => { @@ -229,7 +224,6 @@ describe('PaperProvider', () => { colors: { ...ExtendedLightTheme.colors, primary: 'tomato', - accent: 'yellow', }, } as ThemeProp; const { getByTestId } = render(createProvider(customTheme)); @@ -237,23 +231,4 @@ describe('PaperProvider', () => { customTheme ); }); - - it.each` - version | colorScheme | expectedTheme - ${2} | ${'light'} | ${MD2LightTheme} - ${2} | ${'dark'} | ${MD2DarkTheme} - ${3} | ${'light'} | ${MD3LightTheme} - ${3} | ${'dark'} | ${MD3DarkTheme} - `( - 'uses correct theme, $colorScheme mode, version $version', - async ({ version, colorScheme, expectedTheme }) => { - mockAppearance(); - (Appearance.getColorScheme as jest.Mock).mockReturnValue(colorScheme); - const { getByTestId } = render(createProvider({ version })); - - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( - expectedTheme - ); - } - ); }); diff --git a/src/core/theming.tsx b/src/core/theming.tsx index 44223f032a..042a83d960 100644 --- a/src/core/theming.tsx +++ b/src/core/theming.tsx @@ -3,12 +3,7 @@ import type { ComponentType } from 'react'; import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; import color from 'color'; -import { - MD2DarkTheme, - MD2LightTheme, - MD3DarkTheme, - MD3LightTheme, -} from '../styles/themes'; +import { MD3DarkTheme, MD3LightTheme } from '../styles/themes'; import type { InternalTheme, MD3Theme, @@ -36,30 +31,17 @@ export const withInternalTheme = ( WrappedComponent: ComponentType & C ) => withTheme(WrappedComponent); -export const defaultThemesByVersion = { - 2: { - light: MD2LightTheme, - dark: MD2DarkTheme, - }, - 3: { - light: MD3LightTheme, - dark: MD3DarkTheme, - }, +export const defaultThemes = { + light: MD3LightTheme, + dark: MD3DarkTheme, }; -export const getTheme = < - Scheme extends boolean = false, - IsVersion3 extends boolean = true ->( - isDark: Scheme = false as Scheme, - isV3: IsVersion3 = true as IsVersion3 -): (typeof defaultThemesByVersion)[IsVersion3 extends true - ? 3 - : 2][Scheme extends true ? 'dark' : 'light'] => { - const themeVersion = isV3 ? 3 : 2; +export const getTheme = ( + isDark: Scheme = false as Scheme +): (typeof defaultThemes)[Scheme extends true ? 'dark' : 'light'] => { const scheme = isDark ? 'dark' : 'light'; - return defaultThemesByVersion[themeVersion][scheme]; + return defaultThemes[scheme]; }; // eslint-disable-next-line no-redeclare diff --git a/src/index.tsx b/src/index.tsx index 1b20528787..ec0c4d4786 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -19,9 +19,7 @@ export { default as configureFonts } from './styles/fonts'; import * as Avatar from './components/Avatar/Avatar'; import * as Drawer from './components/Drawer/Drawer'; import * as List from './components/List/List'; -import * as MD2Colors from './styles/themes/v2/colors'; -export { MD2Colors }; export { Avatar, List, Drawer }; export * from './components/FAB/AnimatedFAB'; @@ -58,13 +56,6 @@ export { default as ToggleButton } from './components/ToggleButton'; export { default as SegmentedButtons } from './components/SegmentedButtons/SegmentedButtons'; export { default as Tooltip } from './components/Tooltip/Tooltip'; -export { - Caption, - Headline, - Paragraph, - Subheading, - Title, -} from './components/Typography/v2'; export { default as Text, customText } from './components/Typography/Text'; // Types @@ -144,11 +135,6 @@ export type { Props as ToggleButtonProps } from './components/ToggleButton/Toggl export type { Props as ToggleButtonGroupProps } from './components/ToggleButton/ToggleButtonGroup'; export type { Props as ToggleButtonRowProps } from './components/ToggleButton/ToggleButtonRow'; export type { Props as TouchableRippleProps } from './components/TouchableRipple/TouchableRipple'; -export type { Props as CaptionProps } from './components/Typography/v2/Caption'; -export type { Props as HeadlineProps } from './components/Typography/v2/Headline'; -export type { Props as ParagraphProps } from './components/Typography/v2/Paragraph'; -export type { Props as SubheadingProps } from './components/Typography/v2/Subheading'; -export type { Props as TitleProps } from './components/Typography/v2/Title'; export type { Props as TextProps } from './components/Typography/Text'; export type { Props as SegmentedButtonsProps } from './components/SegmentedButtons/SegmentedButtons'; export type { Props as ListImageProps } from './components/List/ListImage'; @@ -161,7 +147,6 @@ export type { } from './react-navigation'; export type { - MD2Theme, MD3Theme, ThemeBase, MD3Elevation, diff --git a/src/react-navigation/views/MaterialBottomTabView.tsx b/src/react-navigation/views/MaterialBottomTabView.tsx index df846ae2fb..98378e0c79 100644 --- a/src/react-navigation/views/MaterialBottomTabView.tsx +++ b/src/react-navigation/views/MaterialBottomTabView.tsx @@ -99,7 +99,6 @@ export default function MaterialBottomTabView({ ? options.title : (route as Route).name; }} - getColor={({ route }) => descriptors[route.key].options.tabBarColor} getBadge={({ route }) => descriptors[route.key].options.tabBarBadge} getAccessibilityLabel={({ route }) => descriptors[route.key].options.tabBarAccessibilityLabel diff --git a/src/styles/__tests__/fonts.test.js b/src/styles/__tests__/fonts.test.js index fb8dce5ba5..6deed087a4 100644 --- a/src/styles/__tests__/fonts.test.js +++ b/src/styles/__tests__/fonts.test.js @@ -1,10 +1,3 @@ -const customFont = { - custom: { - fontFamily: 'sans-serif', - fontWeight: 'bold', - }, -}; - const customFontV3 = { displayLarge: { fontFamily: 'NotoSans', @@ -131,18 +124,16 @@ const mockPlatform = (OS) => { const loadFonts = () => { let configureFonts; - let fontConfig; let typescale; jest.isolateModules(() => { const fonts = require('../fonts'); configureFonts = fonts.default; - fontConfig = fonts.fontConfig; typescale = require('../themes/v3/tokens').typescale; }); - return { configureFonts, fontConfig, typescale }; + return { configureFonts, typescale }; }; describe('configureFonts', () => { @@ -150,66 +141,6 @@ describe('configureFonts', () => { jest.dontMock('react-native'); }); - it('adds custom fonts to the iOS config', () => { - mockPlatform('ios'); - const { configureFonts, fontConfig } = loadFonts(); - - expect( - configureFonts({ - config: { - ios: { - ...fontConfig.ios, - customFont, - }, - }, - isV3: false, - }) - ).toEqual({ - ...fontConfig.ios, - customFont, - }); - }); - - it('adds custom fonts to the Android config', () => { - mockPlatform('android'); - const { configureFonts, fontConfig } = loadFonts(); - - expect( - configureFonts({ - config: { - android: { - ...fontConfig.android, - customFont, - }, - }, - isV3: false, - }) - ).toEqual({ - ...fontConfig.android, - customFont, - }); - }); - - it('adds custom fonts to the Web config', () => { - mockPlatform('web'); - const { configureFonts, fontConfig } = loadFonts(); - - expect( - configureFonts({ - config: { - web: { - ...fontConfig.web, - customFont, - }, - }, - isV3: false, - }) - ).toEqual({ - ...fontConfig.web, - customFont, - }); - }); - it('overrides properties passed in config for all variants', () => { mockPlatform('ios'); const { configureFonts } = loadFonts(); diff --git a/src/styles/fonts.tsx b/src/styles/fonts.tsx index e3b5017fc4..8d26388853 100644 --- a/src/styles/fonts.tsx +++ b/src/styles/fonts.tsx @@ -1,69 +1,6 @@ -import { Platform, PlatformOSType } from 'react-native'; - -import type { Fonts, MD3Type, MD3Typescale, MD3TypescaleKey } from '../types'; +import type { MD3Type, MD3Typescale, MD3TypescaleKey } from '../types'; import { typescale } from './themes/v3/tokens'; -export const fontConfig = { - web: { - regular: { - fontFamily: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', - fontWeight: '400' as '400', - }, - medium: { - fontFamily: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', - fontWeight: '500' as '500', - }, - light: { - fontFamily: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', - fontWeight: '300' as '300', - }, - thin: { - fontFamily: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', - fontWeight: '100' as '100', - }, - }, - ios: { - regular: { - fontFamily: 'System', - fontWeight: '400' as '400', - }, - medium: { - fontFamily: 'System', - fontWeight: '500' as '500', - }, - light: { - fontFamily: 'System', - fontWeight: '300' as '300', - }, - thin: { - fontFamily: 'System', - fontWeight: '100' as '100', - }, - }, - default: { - regular: { - fontFamily: 'sans-serif', - fontWeight: 'normal' as 'normal', - }, - medium: { - fontFamily: 'sans-serif-medium', - fontWeight: 'normal' as 'normal', - }, - light: { - fontFamily: 'sans-serif-light', - fontWeight: 'normal' as 'normal', - }, - thin: { - fontFamily: 'sans-serif-thin', - fontWeight: 'normal' as 'normal', - }, - }, -}; - -type MD2FontsConfig = { - [platform in PlatformOSType | 'default']?: Fonts; -}; - type MD3FontsConfig = | { [key in MD3TypescaleKey]: Partial; @@ -73,11 +10,6 @@ type MD3FontsConfig = } | Partial; -function configureV2Fonts(config: MD2FontsConfig): Fonts { - const fonts = Platform.select({ ...fontConfig, ...config }) as Fonts; - return fonts; -} - function configureV3Fonts( config: MD3FontsConfig ): MD3Typescale | (MD3Typescale & { [key: string]: MD3Type }) { @@ -110,34 +42,20 @@ function configureV3Fonts( ); } -// eslint-disable-next-line no-redeclare -export default function configureFonts(params: { isV3: false }): Fonts; -// eslint-disable-next-line no-redeclare -export default function configureFonts(params: { - config?: MD2FontsConfig; - isV3: false; -}): Fonts; // eslint-disable-next-line no-redeclare export default function configureFonts(params?: { config?: Partial; - isV3?: true; }): MD3Typescale; // eslint-disable-next-line no-redeclare export default function configureFonts(params?: { config?: Partial>>; - isV3?: true; }): MD3Typescale; // eslint-disable-next-line no-redeclare export default function configureFonts(params: { config: Record; - isV3?: true; }): MD3Typescale & { [key: string]: MD3Type }; // eslint-disable-next-line no-redeclare export default function configureFonts(params?: any) { - const { isV3 = true, config } = params || {}; - - if (isV3) { - return configureV3Fonts(config); - } - return configureV2Fonts(config); + const { config } = params || {}; + return configureV3Fonts(config); } diff --git a/src/styles/overlay.tsx b/src/styles/overlay.tsx index f9e6f8f9a5..050e0c06d1 100644 --- a/src/styles/overlay.tsx +++ b/src/styles/overlay.tsx @@ -2,7 +2,7 @@ import { Animated } from 'react-native'; import color from 'color'; -import { MD2DarkTheme } from './themes/v2/DarkTheme'; +import { MD3DarkTheme } from './themes'; export const isAnimatedValue = ( it: number | string | Animated.AnimatedInterpolation @@ -10,7 +10,7 @@ export const isAnimatedValue = ( export default function overlay( elevation: T, - surfaceColor: string = MD2DarkTheme.colors?.surface + surfaceColor: string = MD3DarkTheme.colors.surface ): T extends number ? string : Animated.AnimatedInterpolation { if (isAnimatedValue(elevation)) { const inputRange = [0, 1, 2, 3, 8, 24]; diff --git a/src/styles/shadow.tsx b/src/styles/shadow.tsx index dcc11e8b65..57cc3612de 100644 --- a/src/styles/shadow.tsx +++ b/src/styles/shadow.tsx @@ -1,73 +1,12 @@ import { Animated } from 'react-native'; -import * as MD2Colors from './themes/v2/colors'; import { MD3Colors } from './themes/v3/tokens'; -const SHADOW_COLOR = MD2Colors.black; -const SHADOW_OPACITY = 0.24; const MD3_SHADOW_OPACITY = 0.3; const MD3_SHADOW_COLOR = MD3Colors.primary0; -export default function shadow( - elevation: number | Animated.Value = 0, - isV3 = false -) { - return isV3 ? v3Shadow(elevation) : v2Shadow(elevation); -} - -function v2Shadow(elevation: number | Animated.Value = 0) { - if (elevation instanceof Animated.Value) { - const inputRange = [0, 1, 2, 3, 8, 24]; - - return { - shadowColor: SHADOW_COLOR, - shadowOffset: { - width: new Animated.Value(0), - height: elevation.interpolate({ - inputRange, - outputRange: [0, 0.5, 0.75, 2, 7, 23], - }), - }, - shadowOpacity: elevation.interpolate({ - inputRange: [0, 1], - outputRange: [0, SHADOW_OPACITY], - extrapolate: 'clamp', - }), - shadowRadius: elevation.interpolate({ - inputRange, - outputRange: [0, 0.75, 1.5, 3, 8, 24], - }), - }; - } else { - if (elevation === 0) { - return {}; - } - - let height, radius; - switch (elevation) { - case 1: - height = 0.5; - radius = 0.75; - break; - case 2: - height = 0.75; - radius = 1.5; - break; - default: - height = elevation - 1; - radius = elevation; - } - - return { - shadowColor: SHADOW_COLOR, - shadowOffset: { - width: 0, - height, - }, - shadowOpacity: SHADOW_OPACITY, - shadowRadius: radius, - }; - } +export default function shadow(elevation: number | Animated.Value = 0) { + return v3Shadow(elevation); } function v3Shadow(elevation: number | Animated.Value = 0) { diff --git a/src/styles/themes/index.ts b/src/styles/themes/index.ts index 3db1a460ad..39bcb6648c 100644 --- a/src/styles/themes/index.ts +++ b/src/styles/themes/index.ts @@ -1,4 +1,2 @@ export { MD3LightTheme } from './v3/LightTheme'; export { MD3DarkTheme } from './v3/DarkTheme'; -export { MD2LightTheme } from './v2/LightTheme'; -export { MD2DarkTheme } from './v2/DarkTheme'; diff --git a/src/styles/themes/v2/DarkTheme.tsx b/src/styles/themes/v2/DarkTheme.tsx deleted file mode 100644 index 9eaa576dcb..0000000000 --- a/src/styles/themes/v2/DarkTheme.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import color from 'color'; - -import { black, pinkA100, white } from './colors'; -import { MD2LightTheme } from './LightTheme'; -import type { Fonts, MD2Theme } from '../../../types'; -import configureFonts from '../../fonts'; - -export const MD2DarkTheme: MD2Theme = { - ...MD2LightTheme, - dark: true, - mode: 'adaptive', - version: 2, - isV3: false, - colors: { - ...MD2LightTheme.colors, - primary: '#BB86FC', - accent: '#03dac6', - background: '#121212', - surface: '#121212', - error: '#CF6679', - onSurface: '#FFFFFF', - text: white, - disabled: color(white).alpha(0.38).rgb().string(), - placeholder: color(white).alpha(0.54).rgb().string(), - backdrop: color(black).alpha(0.5).rgb().string(), - notification: pinkA100, - tooltip: 'rgba(230, 225, 229, 1)', - }, - fonts: configureFonts({ isV3: false }) as Fonts, -}; diff --git a/src/styles/themes/v2/LightTheme.tsx b/src/styles/themes/v2/LightTheme.tsx deleted file mode 100644 index 8566bdcc40..0000000000 --- a/src/styles/themes/v2/LightTheme.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import color from 'color'; - -import { black, pinkA400, white } from './colors'; -import type { Fonts, MD2Theme } from '../../../types'; -import configureFonts from '../../fonts'; - -export const MD2LightTheme: MD2Theme = { - dark: false, - roundness: 4, - version: 2, - isV3: false, - colors: { - primary: '#6200ee', - accent: '#03dac4', - background: '#f6f6f6', - surface: white, - error: '#B00020', - text: black, - onSurface: '#000000', - disabled: color(black).alpha(0.26).rgb().string(), - placeholder: color(black).alpha(0.54).rgb().string(), - backdrop: color(black).alpha(0.5).rgb().string(), - notification: pinkA400, - tooltip: 'rgba(28, 27, 31, 1)', - }, - fonts: configureFonts({ isV3: false }) as Fonts, - animation: { - scale: 1.0, - }, -}; diff --git a/src/styles/themes/v3/DarkTheme.tsx b/src/styles/themes/v3/DarkTheme.tsx index 9823e677bd..246925859c 100644 --- a/src/styles/themes/v3/DarkTheme.tsx +++ b/src/styles/themes/v3/DarkTheme.tsx @@ -10,8 +10,6 @@ export const MD3DarkTheme: MD3Theme = { ...MD3LightTheme, dark: true, mode: 'adaptive', - version: 3, - isV3: true, colors: { primary: palette.primary80, primaryContainer: palette.primary30, diff --git a/src/styles/themes/v3/LightTheme.tsx b/src/styles/themes/v3/LightTheme.tsx index 99e0d11ab4..19ec23fcfc 100644 --- a/src/styles/themes/v3/LightTheme.tsx +++ b/src/styles/themes/v3/LightTheme.tsx @@ -9,8 +9,6 @@ const { palette, opacity } = tokens.md.ref; export const MD3LightTheme: MD3Theme = { dark: false, roundness: 4, - version: 3, - isV3: true, colors: { primary: palette.primary40, primaryContainer: palette.primary90, diff --git a/src/types.tsx b/src/types.tsx index 175131cab3..25e8d4d5ba 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -28,21 +28,6 @@ export type Fonts = { type Mode = 'adaptive' | 'exact'; -export type MD2Colors = { - primary: string; - background: string; - surface: string; - accent: string; - error: string; - text: string; - onSurface: string; - disabled: string; - placeholder: string; - backdrop: string; - notification: string; - tooltip: string; -}; - export type MD3Colors = { primary: string; primaryContainer: string; @@ -126,20 +111,11 @@ export type ThemeBase = { }; export type MD3Theme = ThemeBase & { - version: 3; - isV3: true; colors: MD3Colors; fonts: MD3Typescale; }; -export type MD2Theme = ThemeBase & { - version: 2; - isV3: false; - colors: MD2Colors; - fonts: Fonts; -}; - -export type InternalTheme = MD2Theme | MD3Theme; +export type InternalTheme = MD3Theme; // MD3 types export enum MD3TypescaleKey { From f39da669f9d672cc02b9a7fc235ba8727841456b Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Wed, 29 Apr 2026 13:38:54 +0300 Subject: [PATCH 02/94] fix: ripple not showing (#4897) * docs: remove md2 references from docs * fix: pre-commit hook lint issues * fix: ripple not showing --- docs/docs/guides/02-theming.mdx | 4 - docs/docs/guides/10-migration-guide-to-5.0.md | 629 ------------------ docs/static/llms.txt | 1 - example/src/Examples/IconButtonExample.tsx | 5 +- example/src/index.native.tsx | 2 +- .../BottomNavigation/BottomNavigation.tsx | 2 - .../TouchableRipple.native.tsx | 11 +- 7 files changed, 11 insertions(+), 643 deletions(-) delete mode 100644 docs/docs/guides/10-migration-guide-to-5.0.md diff --git a/docs/docs/guides/02-theming.mdx b/docs/docs/guides/02-theming.mdx index beeb8bfaff..fe1e445a83 100644 --- a/docs/docs/guides/02-theming.mdx +++ b/docs/docs/guides/02-theming.mdx @@ -473,10 +473,6 @@ export default function HomeScreen() { React Native Paper ships with Material Design 3 (Material You) only. Customize the default experience by extending `MD3LightTheme` or `MD3DarkTheme` (see [Extending the theme](#extending-the-theme) and [Advanced theme overrides](#advanced-theme-overrides)). -### Migrating from Material Design 2 - -If you are upgrading from Material Design 2 (4.x and lower), follow the [Migration Guide](https://callstack.github.io/react-native-paper/docs/guides/migration-guide-to-5.0). - ## Applying a theme to a paper component If you want to change the theme for a certain component from the library, you can directly pass the `theme` prop to the component. The theme passed as the prop is merged with the theme from the `PaperProvider`: diff --git a/docs/docs/guides/10-migration-guide-to-5.0.md b/docs/docs/guides/10-migration-guide-to-5.0.md deleted file mode 100644 index 178900d2e3..0000000000 --- a/docs/docs/guides/10-migration-guide-to-5.0.md +++ /dev/null @@ -1,629 +0,0 @@ ---- -title: Introducing v5 with Material You ---- - -React Native Paper v5 is all about adopting the new Material Design 3 aka Material You. It was released in October 2021 after intense work and effort to make Material You follow a more expressive approach to design. - -Current releases use Material Design 3 only; the historical `version: 2` / MD2 theme APIs described below were removed after v5. Use this guide when upgrading older apps to MD3. - -# Migration guide to Material You 5.0 - -Version 5.0 brings support for the next Material Design iteration branded as Material You (in fact being Material Design v3 or in short MD3) into the `react-native-paper` library. All the components were refined according to the official [design kit on figma](https://www.figma.com/community/file/1035203688168086460) and adjusted in terms of visuals by changes in colors, typography and animations. - -Below you can find the most important information about the components whose API may have changed API has been changed due to supporting new props, renaming existing ones or some deprecations. Hopefully, based on the presented required changes, migration to the latest version should be smooth. Enjoy! - -### Installation - -* The `v5` has been oficially released as a stable version, which means it will be installed by default from the `npm`. In order to do that, open a Terminal in your project's folder and run: - -```bash npm2yarn -npm install react-native-paper -``` - -* From `v5` there is a need to install [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) for handling safe area. - -```bash npm2yarn -npm install react-native-safe-area-context -``` - -Additionaly for `iOS` platform there is a requirement to link the native parts of the library: - -```bash -npx pod-install -``` - -## Theming - -### Versioning - -In v5, Material You (MD3) became the default theme. Earlier releases also exposed `version: 2` for Material Design 2; that option and MD2 theme exports have since been removed—use MD3 themes (`MD3LightTheme` / `MD3DarkTheme`) only. - -The theme object no longer includes `version` or `isV3`. Material Design 3 is the only supported mode; use `MD3LightTheme` / `MD3DarkTheme` (or a custom object matching the `MD3Theme` shape) without setting a version flag. - -Read more about theming in the [theming guide](https://callstack.github.io/react-native-paper/docs/guides/theming). - -### Colors - -New theme introduces a new color palette along with new namings reflecting design color tokens, but written in camel case. Palette contains a set of five key colors, where primary, secondary and tertiary are classified into accent colors. The second group of colors is neutral and neutral variant colors used for defining surface or background roles as well as specifying high and medium emphasis text and icons. Additionally, the color system includes a semantic color role for error. - -Each accent and error colors has a group of related tones. The tones are mapped to roles that create contrast and visual interest when applied to elements in the UI. - -:::note -Dynamic colors are not supported yet -::: - -![color-palette](../../static/migration/color-palette.png) - -Colors theme structure should follow the default palette and contain the following properties: - -```js -theme: { - /* ... */ - colors: { - primary, - primaryContainer, - secondary, - secondaryContainer, - tertiary, - tertiaryContainer, - surface, - surfaceVariant, - surfaceDisabled, - background, - error, - errorContainer, - onPrimary, - onPrimaryContainer, - onSecondary, - onSecondaryContainer, - onTertiary, - onTertiaryContainer, - onSurface, - onSurfaceVariant, - onSurfaceDisabled, - onError, - onErrorContainer, - onBackground, - outline, - shadow, - inverseOnSurface, - inverseSurface, - inversePrimary, - backdrop, - elevation: { - level0, - level1, - level2, - level3, - level4, - level5 - } - } -} -``` - -👉 You can find more about color on the [Material You website](https://m3.material.io/styles/color/the-color-system/key-colors-tones) - -## Typography - -A new way of approaching typography introduces one component `` which accepts prop `variant`. Variant defines appropriate text styles for type role and its size. The updated type scale organizes styles into five roles that are named to describe their purposes: Display, Headline, Title, Label and Body along with three display styles large, medium, and small. In total, there are fifteen variants that are MD3 compliant and are reflecting design typography tokens written in camel case. - -:::info -If any component uses Paper's `Text` component, without specified variant, then `default` variant is applied. -::: - -```js -Display Large -Display Medium -Display small - -Headline Large -Headline Medium -Headline Small - -Title Large -Title Medium -Title Small - -Body Large -Body Medium -Body Small - -Label Large -Label Medium -Label Small - ``` - -Take a look at the suggested replacement diff: - - ```diff -- Headline -+ Headline - -- Title -+ Title - -- Subheading -+ Subheading - -- Paragraph -+ Paragraph - -- Caption -+ Caption - ``` - -:::caution -The standalone components `Caption`, `Headline`, `Paragraph`, `Subheading`, and `Title` are no longer exported from `react-native-paper`. Use `` as in the diff above. -::: - -👉 You can find more about typography on the [Material You website](https://m3.material.io/styles/typography/overview) - -### Configure fonts - -The `configureFonts` helper configures the Material Design 3 typescale. Pass overrides via the `config` object (see [Fonts](https://callstack.github.io/react-native-paper/docs/guides/fonts.html)). - -```ts -configureFonts({ config: { bodyLarge: { fontFamily: 'System' } } }) -``` - -Older direct calls like `configureFonts(fontConfig)` and MD2 platform-split configs should be replaced with MD3 variant keys (`displayLarge`, `bodyMedium`, and so on). - -## Components - -### Appbar (Top app bar) - -`Appbar` and `Appbar.Header` in the latest version can be used in four various modes due to new prop `mode`: - -* `small` - Appbar with default height (64) (default), -* `medium` - Appbar with medium height (112), -* `large` - Appbar with large height (152), -* `center-aligned` - Appbar with default height (64) and center-aligned title. - -```js - - /* ... */ - -``` - -To make it easier for users to build the `BottomBar`, formed on the `Appbar` components, we have added a property `safeAreaInsets`: - -```js - - /* ... */ - -``` - -It's worth noting that by default the theme version 3 `Appbar` and `Appbar.Header` don't have a shadow. However, it can be added by passing prop `elevated` into the component: - -```js - - /* ... */ - -``` - -#### Appbar.Action - -`Appbar.Action` received new prop `isLeading`, which defines whether it's the leading button and should be placed at the beginning of the `Appbar`. - -```js - {}} /> -``` - -#### Appbar.Content - -New design guidelines indicate there is no subtitle in `Appbar.Content`, that's why there are two deprecations and the following props won't be supported anymore: `subtitle` and `subtitleStyle`. - -```diff -- -+ -``` - -### Banner, Searchbar and Snackbar - -According to the updates in `Surface` on the top of which `Banner`, `Searchbar` and `Snackbar` are implemented, all three component received `elevation` prop to adjust its value. - -```diff -- Hello -+ Hello -``` - -### BottomNavigation (Navigation bar) - -For the sake of new animation of pill shape, indicating active destination, and assisting icon change from outlined to filled, there are three changes within `navigationState.routes` property items: - -* `color` is deprecated since color is constant and the same for all routes, -* `icon` is renamed to `focusedIcon`, as the name implies, with theme version 3 it's the outline icon used as focused tab icon and with theme version 2 it's a default icon, -* `unfocusedIcon` (optional) is the filled icon used as the unfocused tab icon, compatible with theme version 3. - -:::info -`unfocusedIcon` is optional, if you can't find outline icon equivalent, omit that prop, so `focusedIcon` will be displayed in both active and inactive state. -::: - -```diff -routes: [ -- { key: "album", title: "Album", icon: "image-album", color: "#3F51B5" }, -+ { key: "album", title: "Album", focusedIcon: "image-album" }, -- { key: "library", title: "Library", icon: "inbox", color: "#009688" }, -+ { key: "library", title: "Library", focusedIcon: "inbox", unfocusedIcon: "inbox-outline" }, -- { key: "favorites", title: "Favorites", icon: "heart", color: "#795548" }, -+ { key: "favorites", title: "Favorites", focusedIcon: "heart", unfocusedIcon: "heart-outline; }, -- { key: "purchased", title: "Purchased", icon: "shopping-cart", color: "#607D8B" }, -+ { key: "purchased", title: "Purchased", focusedIcon: "shopping-cart" }, -] -``` - -The `compact` prop was also introduced, working with both themes. It indicates whether tabs should be spread across the entire width, especially in a horizontal mode. - -```js - -``` - -It's worth to mention that default value of prop `shifting` depends on the theme version: -* 3 - it's `false`, -* 2 - it's `true` when there are more than 3 tabs. - -Two additional props that control the scene animation were introduced that control the animation of the tabs when `sceneAnimationEnabled` is `true`: -* `sceneAnimationType: "opacity" | "shifting" | undefined` - defines the animation type for the scene. `shifting` enables a new animation where navigating to a scene will shift it horizontally into view. Both `opacity` and `undefined` have the same effect, fading the scene into view. -* `sceneAnimationEasing` allows specifying a custom easing function for the scene animation. - -![shiftingAnimation](../../static/screenshots/bottom-navigation-shifting.gif) - -On a final note, please be aware that `BottomNavigation` with theme version 3 doesn't have a shadow. - -### Button - -`Button`'s property `mode` has been expanded with two additional options: -* `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background, -* `container-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. - -```js -<> - - - -``` - -The property `color` is deprecated, but in its place two new props called `buttonColor` and `textColor` are introduced: -* `buttonColor` - custom button's background color, -* `textColor` - custom button's text color. - -```diff -- -+ -``` - -```diff -- -+ -``` - -Please be aware that along with theme version 3, by default text in the `Button` component isn't uppercased and `contained` button doesn't have any shadow (use then `elevated`). - -### Card - -The `Card` component's property `mode` has been expanded with one additional option called `contained`, which applies to the card's specified background color without any elevation and border. - -```js - -``` - -#### Card.Title - -Since there is no one right way to make a card, there is also no one right way for specifying a title and subtitle variant. Therefore two new props come in handy: - -* `titleVariant` - title text variant defines appropriate text styles for type role and its size. -* `subtitleVariant` - subtitle text variant defines appropriate text styles for type role and its size. - -```js - -``` - -### Checkbox -#### Checkbox.Item - -`Checkbox.Item` similarly to `RadioButton.Item` has been expanded with the prop called `labelVariant`, which defines appropriate text styles for type role and its size. - -```js - -``` - -### Chip - -To properly compose `Chip` component and adjust into required type, there are three new props that will come in handy: - -* `compact` - sets smaller horizontal paddings around the label, useful for `Chip` containing only the label, -* `elevated` - indicating whether `Chip` should be elevated, -* `showSelectedOverlay` - defining whether to display an overlay on a selected button. - -```js -<> - Compact Chip - Elevated Chip - Chip with selected overlay - -``` -### Dialog -#### Dialog.Icon - -`Dialog.Icon` is another freshly added component presenting an icon within a `Dialog`, placed at the top of the content. - -:::caution -It's working only with theme version 3. -::: - -```js - - - - - -``` -### Divider - -`Divider` component received two new props: - -* `bold` - divider is bolded, -* `horizontalInset` - divider has horizontal insets on both sides. - -Additionally prop `inset` was renamed to `leftInset`. - -```diff -- -+ -``` - -### Drawer -#### Drawer.CollapsedItem (Navigation rail) - -`Drawer.CollapsedItem` is a newly created side navigation component that can be used within `Drawer`, representing a destination in the form of an action item with an icon and optionally label. - -:::caution -It's working only with theme version 3. -::: - -```js - - - - -``` - -#### Drawer.Section - -With the latest version, there is a possibility to specify whether `Drawer.Section` should have a separator, in form of `Divider` component, displayed at the end of the section. To adjust it, a new property called `showDivider` was introduced, which by default is `true`: - -```js - - - - -``` - -### FAB - -`FAB`, `AnimatedFAB` and `FAB.Group` in the latest version can be used with four variants and two modes, thanks to two new props: - -* `variant` defines color mappings variant for combinations of container and icon colors. Can be one of: primary (default), secondary, tertiary or surface. - -```js - -``` - -* `mode` specifies whether a button should be flat or elevated: - - `flat` - button without a shadow, - - `elevated` - button with a shadow. - -```js - -``` - -#### FAB - -Additionaly `FAB` may be applied in one of three available sizes, thanks to new prop `size`: - -* `small` - FAB with small height (40), -* `medium` - Appbar with default medium height (56), -* `large` - Appbar with large height (96). - -```js - -``` - -However, if you would like to have your own size of `FAB`, there is a new prop called `customSize`: - -```js - -``` - -Accordingly to introducing `size="small"`, prop `small` was deprecated. - -```diff -- -+ -``` - -#### FAB.Group - -There is also deprecation in one of the `actions` properties, namely `small` prop is deprecated and replaced in favour of the default `size="small"`. - -```diff -- -+ -``` - -Additionally, the action item property previously known as `labelStyle` was renamed to `containerStyle` since it's tied mostly with the container styles. At the same time, `labelStyle` is still available with the new role related to styling item label. - -```diff -- -+ -``` - -### IconButton - -`IconButton` received two new props: - -* `selected` sets alternative combination of icon and container color, - -```js - -``` - -* `containerColor` custom background color of the icon container. - -```js - -``` - -At the same time, the `color` prop was renamed to `iconColor`. - -```diff -- -+ -``` - -### Menu -#### Menu.Item - -`Menu.Item` received two new props: - -* `dense` sets smaller item height for more condensed layout, -* `trailingIcon` which handles displaying an icon at the end of the item row. - -```js - -``` - -At the same time, by analogy to the second new prop, the `icon` prop was renamed to `leadingIcon`. - -```diff -- {}} title="Redo" /> -+ {}} title="Redo" /> -``` - -### RadioButton -##### RadioButton.Item - -`RadioButton.Item` has been expanded with the prop called `labelVariant`, which defines appropriate text styles for type role and its size. - -```js - -``` - -### SegmentedButtons - -`SegmentedButtons` is a completely new component introduced in the latest version. It allows people to select options, switch views, or sort elements. It supports single and multiselect select variant and provide a lot -of customization options. - -![segmentedButtons](../../static/screenshots/segmentedbuttons.gif) - -```js -const MyComponent = () => { - const [value, setValue] = React.useState(''); - - return ( - - ); -}; -``` - -### Snackbar - -`Snackbar` due to the optional close affordance, in form of `IconButton` (located on the right side of action button), received three new props: - -* `icon` - icon to display when `onIconPress` is defined. Default will be `close` icon. -* `onIconPress` - function to execute on icon button press. The icon button appears only when this prop is specified. -* `iconAccessibilityLabel` - accessibility label for the icon button. - -### Surface - -`Surface` component received one new prop: -* `elevation` - accepts values from `0` to `5` and applies background color and shadows to the `Surface` component. Supports both iOS and Android. - -Previously `elevation` was passed inside the `style` prop. Since it supported not only Android, but also iOS, we decided to extract it from `style` and create a separate `elevation` prop for that. - -```diff -- -+ -``` - -### TextInput -#### TextInput.Icon - -The property `name` was renamed to `icon`, since the scope and type of that prop is much wider than just the icon name – it accepts also the function which receives an object with color and size properties and - -```diff -- -+ -``` - -## Tooltip - -Component displayed upon tapping and holding a screen element or component used to present an informative text label identifying an element, such as a description of its function. - -![tooltip](../../static/screenshots/tooltips.gif) - - -```js - - {}} /> - -``` - -## Credits - -With this, that’s a wrap. - -The update wouldn't happen without a group of great React Native experts I'm happy to work with. -From this place I would like to thank: -- [Daniel Szczepanik](https://github.com/Drakeoon) for his commitment, effort and collaborative work on adjusting components, -- [Olimpia Zurek](https://github.com/OlimpiaZurek) for her contribution and help, -- [Aleksandra Desmurs-Linczewska](https://github.com/p-syche), [Jan Jaworski](https://github.com/jaworek) and [Kewin Wereszczyński](https://github.com/kwereszczynski) for checking and testing changes as well as providing valuable feedback, -- [Bruno Castro](https://github.com/brunohkbx) for creating a long-awaited `Tooltip` component, -- [Muhammad Hur Ali](https://github.com/hurali97) for various bug fixes and `List` subcomponents adjustments, - -and, last but not least, [Satya Sahoo](https://github.com/satya164) for his mentoring during the process. diff --git a/docs/static/llms.txt b/docs/static/llms.txt index 4029ace3ea..3244326f67 100644 --- a/docs/static/llms.txt +++ b/docs/static/llms.txt @@ -17,7 +17,6 @@ - Icons: https://callstack.github.io/react-native-paper/docs/guides/icons - Fonts: https://callstack.github.io/react-native-paper/docs/guides/fonts - React Native Web: https://callstack.github.io/react-native-paper/docs/guides/react-native-web -- Migration to v5: https://callstack.github.io/react-native-paper/docs/guides/migration-guide-to-5.0 - Recommended Libraries: https://callstack.github.io/react-native-paper/docs/guides/recommended-libraries - Contributing: https://callstack.github.io/react-native-paper/docs/guides/contributing - Bottom Navigation: https://callstack.github.io/react-native-paper/docs/guides/bottom-navigation diff --git a/example/src/Examples/IconButtonExample.tsx b/example/src/Examples/IconButtonExample.tsx index a2ac7e72db..2b90a4ebd4 100644 --- a/example/src/Examples/IconButtonExample.tsx +++ b/example/src/Examples/IconButtonExample.tsx @@ -137,7 +137,7 @@ const ButtonExample = () => { mode="contained" style={styles.slightlyRounded} size={24} - contentStyle={{ padding: 8 }} + contentStyle={styles.contentPadding} iconColor={MD3Colors.tertiary50} onPress={() => {}} /> @@ -190,6 +190,9 @@ const styles = StyleSheet.create({ width: 48, height: 48, }, + contentPadding: { + padding: 8, + }, differentBorderRadius: { borderTopLeftRadius: 2, borderTopRightRadius: 4, diff --git a/example/src/index.native.tsx b/example/src/index.native.tsx index c6bba2b1bd..880fc4f04c 100644 --- a/example/src/index.native.tsx +++ b/example/src/index.native.tsx @@ -13,9 +13,9 @@ import { PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper'; import { SafeAreaInsetsContext } from 'react-native-safe-area-context'; import DrawerItems from './DrawerItems'; +import { PreferencesContext } from './PreferencesContext'; import App from './RootNavigator'; import { deviceColorsSupported } from '../utils'; -import { PreferencesContext } from './PreferencesContext'; import { CombinedDefaultTheme, CombinedDarkTheme, diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index a15cdd0110..d0cd447295 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -418,13 +418,11 @@ const BottomNavigation = ({ }); }, [ - shifting, navigationState.routes, offsetsAnims, scale, tabsPositionAnims, sceneAnimationEasing, - theme, ] ); diff --git a/src/components/TouchableRipple/TouchableRipple.native.tsx b/src/components/TouchableRipple/TouchableRipple.native.tsx index 1cb17b4068..3f8c791c33 100644 --- a/src/components/TouchableRipple/TouchableRipple.native.tsx +++ b/src/components/TouchableRipple/TouchableRipple.native.tsx @@ -73,12 +73,13 @@ const TouchableRipple = ( underlayColor, }); - // A workaround for ripple on Android P is to use useForeground + overflow: 'hidden' + // Use foreground ripple on Android P+ to ensure visibility. + // Background ripple requires the view to have a background drawable, + // which isn't always present. Foreground ripple needs overflow: 'hidden' + // to stay within bounds. // https://github.com/facebook/react-native/issues/6480 const useForeground = - Platform.OS === 'android' && - Platform.Version >= ANDROID_VERSION_PIE && - borderless; + Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_PIE; if (TouchableRipple.supported) { const androidRipple = rippleEffectEnabled @@ -94,7 +95,7 @@ const TouchableRipple = ( {...rest} ref={ref} disabled={disabled} - style={[borderless && styles.overflowHidden, style]} + style={[useForeground && styles.overflowHidden, style]} android_ripple={androidRipple} > {React.Children.only(children)} From 1369526813386d1385096f64e2abe198a11596ba Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Wed, 29 Apr 2026 13:48:43 +0300 Subject: [PATCH 03/94] feat: remove customRippleColor from components (#4899) * fixes #4894 --- src/components/Appbar/AppbarAction.tsx | 14 +------ src/components/Button/Button.tsx | 12 ------ src/components/Checkbox/CheckboxItem.tsx | 7 ---- src/components/Chip/Chip.tsx | 9 ---- src/components/Chip/helpers.tsx | 39 ----------------- .../DataTable/DataTablePagination.tsx | 26 ------------ src/components/Drawer/DrawerItem.tsx | 10 ----- src/components/FAB/AnimatedFAB.tsx | 12 ------ src/components/FAB/FAB.tsx | 10 +---- src/components/FAB/FABGroup.tsx | 10 ----- src/components/FAB/utils.ts | 4 -- src/components/IconButton/IconButton.tsx | 26 ++++-------- src/components/IconButton/utils.ts | 20 --------- src/components/List/ListAccordion.tsx | 10 +---- src/components/List/utils.ts | 9 +--- src/components/Menu/MenuItem.tsx | 10 +---- src/components/Menu/utils.ts | 26 +----------- .../RadioButton/RadioButtonItem.tsx | 7 ---- src/components/Searchbar.tsx | 31 ++------------ .../SegmentedButtons/SegmentedButtonItem.tsx | 11 ----- src/components/Snackbar.tsx | 13 +----- .../TextInput/Adornment/TextInputIcon.tsx | 7 ---- src/components/ToggleButton/ToggleButton.tsx | 7 ---- src/components/TouchableRipple/utils.ts | 2 +- src/components/__tests__/Chip.test.tsx | 42 ------------------- src/components/__tests__/FABGroup.test.tsx | 31 -------------- src/components/__tests__/IconButton.test.tsx | 16 ------- .../__tests__/ListAccordion.test.tsx | 27 ------------ src/components/__tests__/MenuItem.test.tsx | 16 ------- src/components/__tests__/Searchbar.test.tsx | 36 ---------------- src/components/__tests__/Snackbar.test.tsx | 17 -------- .../__tests__/ToggleButton.test.tsx | 16 ------- 32 files changed, 20 insertions(+), 513 deletions(-) diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index 14982cbecb..212fcca952 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -1,11 +1,5 @@ import * as React from 'react'; -import type { - Animated, - ColorValue, - StyleProp, - View, - ViewStyle, -} from 'react-native'; +import type { Animated, StyleProp, View, ViewStyle } from 'react-native'; import type { ThemeProp } from 'src/types'; @@ -20,10 +14,6 @@ export type Props = React.ComponentPropsWithoutRef & { * Custom color for action icon. */ color?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Name of the icon to show. */ @@ -91,7 +81,6 @@ const AppbarAction = forwardRef( accessibilityLabel, isLeading, theme: themeOverrides, - rippleColor, ...rest }: Props, ref @@ -115,7 +104,6 @@ const AppbarAction = forwardRef( accessibilityLabel={accessibilityLabel} animated ref={ref} - rippleColor={rippleColor} {...rest} /> ); diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 1d1d0d3002..516ff915f7 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { AccessibilityRole, Animated, - ColorValue, GestureResponderEvent, Platform, PressableAndroidRippleConfig, @@ -13,8 +12,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; - import { ButtonMode, getButtonColors, @@ -66,10 +63,6 @@ export type Props = $Omit, 'mode'> & { * Custom button's text color. */ textColor?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Whether to show a loading indicator. */ @@ -186,7 +179,6 @@ const Button = ( icon, buttonColor: customButtonColor, textColor: customTextColor, - rippleColor: customRippleColor, children, accessibilityLabel, accessibilityHint, @@ -292,9 +284,6 @@ const Button = ( dark, }); - const rippleColor = - customRippleColor || color(textColor).alpha(0.12).rgb().string(); - const touchableStyle = { ...borderRadiusStyles, borderRadius: borderRadiusStyles.borderRadius ?? borderRadius, @@ -363,7 +352,6 @@ const Button = ( accessible={accessible} hitSlop={hitSlop} disabled={disabled} - rippleColor={rippleColor} style={getButtonTouchableRippleStyle(touchableStyle, borderWidth)} testID={testID} theme={theme} diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 869edd674a..45e86bcced 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { - ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, @@ -58,10 +57,6 @@ export type Props = { * Custom color for checkbox. */ color?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Additional styles for container View. */ @@ -148,7 +143,6 @@ const CheckboxItem = ({ disabled, labelVariant = 'bodyLarge', labelMaxFontSizeMultiplier = 1.5, - rippleColor, background, hitSlop, ...props @@ -187,7 +181,6 @@ const CheckboxItem = ({ onLongPress={onLongPress} testID={testID} disabled={disabled} - rippleColor={rippleColor} theme={theme} background={background} hitSlop={hitSlop} diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 3706239b71..012cac4fc1 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { AccessibilityState, Animated, - ColorValue, GestureResponderEvent, Platform, PressableAndroidRippleConfig, @@ -73,10 +72,6 @@ export type Props = $Omit, 'mode'> & { * Note: Check will not be shown if `icon` is specified. If specified, `icon` will be shown regardless of `selected`. */ showSelectedCheck?: boolean; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Whether the chip is disabled. A disabled chip is greyed out and `onPress` is not called on touch. */ @@ -201,7 +196,6 @@ const Chip = ({ theme: themeOverrides, testID = 'chip', selectedColor, - rippleColor: customRippleColor, showSelectedOverlay = false, showSelectedCheck = true, ellipsizeMode, @@ -263,7 +257,6 @@ const Chip = ({ borderColor, textColor, iconColor, - rippleColor, selectedBackgroundColor, backgroundColor, } = getChipColors({ @@ -273,7 +266,6 @@ const Chip = ({ showSelectedOverlay, customBackgroundColor, disabled, - customRippleColor, }); const accessibilityState: AccessibilityState = { @@ -324,7 +316,6 @@ const Chip = ({ onPressIn={hasPassedTouchHandler ? handlePressIn : undefined} onPressOut={hasPassedTouchHandler ? handlePressOut : undefined} delayLongPress={delayLongPress} - rippleColor={rippleColor} disabled={disabled} accessibilityLabel={accessibilityLabel} accessibilityRole={accessibilityRole} diff --git a/src/components/Chip/helpers.tsx b/src/components/Chip/helpers.tsx index 7c654a22f6..0f191b2ac0 100644 --- a/src/components/Chip/helpers.tsx +++ b/src/components/Chip/helpers.tsx @@ -171,37 +171,6 @@ const getIconColor = ({ return colors.onSecondaryContainer; }; -const getRippleColor = ({ - theme, - isOutlined, - disabled, - selectedColor, - selectedBackgroundColor: _selectedBackgroundColor, - customRippleColor, -}: BaseProps & { - selectedBackgroundColor: string; - selectedColor?: string; - customRippleColor?: ColorValue; -}) => { - if (customRippleColor) { - return customRippleColor; - } - - const isSelectedColor = selectedColor !== undefined; - const textColor = getTextColor({ - theme, - disabled, - selectedColor, - isOutlined, - }); - - if (isSelectedColor) { - return color(selectedColor).alpha(0.12).rgb().string(); - } - - return color(textColor).alpha(0.12).rgb().string(); -}; - export const getChipColors = ({ isOutlined, theme, @@ -209,13 +178,11 @@ export const getChipColors = ({ showSelectedOverlay, customBackgroundColor, disabled, - customRippleColor, }: BaseProps & { customBackgroundColor?: ColorValue; disabled?: boolean; showSelectedOverlay?: boolean; selectedColor?: string; - customRippleColor?: ColorValue; }) => { const baseChipColorProps = { theme, isOutlined, disabled }; @@ -244,12 +211,6 @@ export const getChipColors = ({ ...baseChipColorProps, selectedColor, }), - rippleColor: getRippleColor({ - ...baseChipColorProps, - selectedColor, - selectedBackgroundColor, - customRippleColor, - }), backgroundColor, selectedBackgroundColor, }; diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index c7a5375f82..6e2612973e 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { - ColorValue, I18nManager, StyleProp, StyleSheet, @@ -57,14 +56,6 @@ type PaginationDropdownProps = { * The function to set the number of rows per page. */ onItemsPerPageChange?: (numberOfItemsPerPage: number) => void; - /** - * Color of the dropdown item ripple effect. - */ - dropdownItemRippleColor?: ColorValue; - /** - * Color of the select page dropdown ripple effect. - */ - selectPageDropdownRippleColor?: ColorValue; /** * @optional */ @@ -88,10 +79,6 @@ type PaginationControlsProps = { * Whether to show fast forward and fast rewind buttons in pagination. False by default. */ showFastPaginationControls?: boolean; - /** - * Color of the pagination control ripple effect. - */ - paginationControlRippleColor?: ColorValue; /** * @optional */ @@ -104,7 +91,6 @@ const PaginationControls = ({ onPageChange, showFastPaginationControls, theme: themeOverrides, - paginationControlRippleColor, }: PaginationControlsProps) => { const theme = useInternalTheme(themeOverrides); @@ -123,7 +109,6 @@ const PaginationControls = ({ /> )} iconColor={textColor} - rippleColor={paginationControlRippleColor} disabled={page === 0} onPress={() => onPageChange(0)} accessibilityLabel="page-first" @@ -140,7 +125,6 @@ const PaginationControls = ({ /> )} iconColor={textColor} - rippleColor={paginationControlRippleColor} disabled={page === 0} onPress={() => onPageChange(page - 1)} accessibilityLabel="chevron-left" @@ -156,7 +140,6 @@ const PaginationControls = ({ /> )} iconColor={textColor} - rippleColor={paginationControlRippleColor} disabled={numberOfPages === 0 || page === numberOfPages - 1} onPress={() => onPageChange(page + 1)} accessibilityLabel="chevron-right" @@ -173,7 +156,6 @@ const PaginationControls = ({ /> )} iconColor={textColor} - rippleColor={paginationControlRippleColor} disabled={numberOfPages === 0 || page === numberOfPages - 1} onPress={() => onPageChange(numberOfPages - 1)} accessibilityLabel="page-last" @@ -189,8 +171,6 @@ const PaginationDropdown = ({ numberOfItemsPerPage, onItemsPerPageChange, theme: themeOverrides, - selectPageDropdownRippleColor, - dropdownItemRippleColor, }: PaginationDropdownProps) => { const theme = useInternalTheme(themeOverrides); const { colors } = theme; @@ -209,7 +189,6 @@ const PaginationDropdown = ({ icon="menu-down" contentStyle={styles.contentStyle} theme={theme} - rippleColor={selectPageDropdownRippleColor} > {`${numberOfItemsPerPage}`} @@ -227,7 +206,6 @@ const PaginationDropdown = ({ onItemsPerPageChange?.(option); toggleSelect(false); }} - rippleColor={dropdownItemRippleColor} title={option} theme={theme} /> @@ -304,8 +282,6 @@ const DataTablePagination = ({ onItemsPerPageChange, selectPageDropdownLabel, selectPageDropdownAccessibilityLabel, - selectPageDropdownRippleColor, - dropdownItemRippleColor, theme: themeOverrides, ...rest }: Props) => { @@ -339,8 +315,6 @@ const DataTablePagination = ({ numberOfItemsPerPageList={numberOfItemsPerPageList} numberOfItemsPerPage={numberOfItemsPerPage} onItemsPerPageChange={onItemsPerPageChange} - selectPageDropdownRippleColor={selectPageDropdownRippleColor} - dropdownItemRippleColor={dropdownItemRippleColor} theme={theme} /> diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 7af3d9fcf4..6902b4b8cb 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { - ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, @@ -9,8 +8,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; - import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import Icon, { IconSource } from '../Icon'; @@ -57,10 +54,6 @@ export type Props = React.ComponentPropsWithRef & { * Specifies the largest possible scale a label font can reach. */ labelMaxFontSizeMultiplier?: number; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Sets additional distance outside of element in which a press can be detected. */ @@ -97,7 +90,6 @@ const DrawerItem = ({ active, disabled, theme: themeOverrides, - rippleColor: customRippleColor, style, onPress, background, @@ -117,7 +109,6 @@ const DrawerItem = ({ const labelMargin = icon ? 12 : 0; const borderRadius = 7 * roundness; - const rippleColor = color(contentColor).alpha(0.12).rgb().string(); const font = theme.fonts.labelLarge; return ( @@ -136,7 +127,6 @@ const DrawerItem = ({ accessibilityRole="button" accessibilityState={{ selected: active }} accessibilityLabel={accessibilityLabel} - rippleColor={customRippleColor || rippleColor} theme={theme} hitSlop={hitSlop} > diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 6b6964a4a9..c39144705c 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import type { AccessibilityState, - ColorValue, NativeSyntheticEvent, PressableAndroidRippleConfig, TextLayoutEventData, @@ -20,8 +19,6 @@ import { Text, } from 'react-native'; -import color from 'color'; - import { getCombinedStyles, getFABColors, getLabelSizeWeb } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $Omit, $RemoveChildren, ThemeProp } from '../../types'; @@ -67,10 +64,6 @@ export type Props = $Omit<$RemoveChildren, 'mode'> & { * Custom color for the icon and label of the `FAB`. */ color?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch. */ @@ -213,7 +206,6 @@ const AnimatedFAB = ({ accessibilityLabel = label, accessibilityState, color: customColor, - rippleColor: customRippleColor, disabled, onPress, onLongPress, @@ -313,9 +305,6 @@ const AnimatedFAB = ({ customBackgroundColor, }); - const rippleColor = - customRippleColor || color(foregroundColor).alpha(0.12).rgb().string(); - const extendedWidth = textWidth + SIZE + borderRadius; const distance = isAnimatedFromRight @@ -462,7 +451,6 @@ const AnimatedFAB = ({ onPress={onPress} onLongPress={onLongPress} delayLongPress={delayLongPress} - rippleColor={rippleColor} disabled={disabled} accessibilityLabel={accessibilityLabel} accessibilityRole="button" diff --git a/src/components/FAB/FAB.tsx b/src/components/FAB/FAB.tsx index 9ac9cf3435..ae8c3f193d 100644 --- a/src/components/FAB/FAB.tsx +++ b/src/components/FAB/FAB.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { AccessibilityState, Animated, - ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, @@ -79,10 +78,6 @@ export type Props = $Omit<$RemoveChildren, 'mode'> & { * Custom color for the icon and label of the `FAB`. */ color?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch. */ @@ -189,7 +184,6 @@ const FAB = forwardRef( accessibilityState, animated = true, color: customColor, - rippleColor: customRippleColor, disabled, onPress, onLongPress, @@ -242,13 +236,12 @@ const FAB = forwardRef( backgroundColor: customBackgroundColor, } = (StyleSheet.flatten(style) || {}) as ViewStyle; - const { backgroundColor, foregroundColor, rippleColor } = getFABColors({ + const { backgroundColor, foregroundColor } = getFABColors({ theme, variant, disabled, customColor, customBackgroundColor, - customRippleColor, }); const isLargeSize = size === 'large'; @@ -295,7 +288,6 @@ const FAB = forwardRef( onPress={onPress} onLongPress={onLongPress} delayLongPress={delayLongPress} - rippleColor={rippleColor} disabled={disabled} accessibilityLabel={accessibilityLabel} accessibilityRole="button" diff --git a/src/components/FAB/FABGroup.tsx b/src/components/FAB/FABGroup.tsx index ec2cc85f68..4b9d9f1cd4 100644 --- a/src/components/FAB/FABGroup.tsx +++ b/src/components/FAB/FABGroup.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Animated, - ColorValue, GestureResponderEvent, Pressable, StyleProp, @@ -43,7 +42,6 @@ export type Props = { * - `toggleStackOnLongPress`: callback that is called when `FAB` is long pressed * - `size`: size of action item. Defaults to `small`. @supported Available in v5.x * - `testID`: testID to be used on tests - * - `rippleColor`: color of the ripple effect. */ actions: Array<{ icon: IconSource; @@ -60,7 +58,6 @@ export type Props = { onPress: (e: GestureResponderEvent) => void; size?: 'small' | 'medium'; testID?: string; - rippleColor?: ColorValue; }>; /** * Icon to display for the `FAB`. @@ -79,10 +76,6 @@ export type Props = { * Custom backdrop color for opened speed dial background. */ backdropColor?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Function to execute on pressing the `FAB`. */ @@ -221,7 +214,6 @@ const FABGroup = ({ variant = 'primary', enableLongPressWhenStackOpened = false, backdropColor: customBackdropColor, - rippleColor, }: Props) => { const theme = useInternalTheme(themeOverrides); const { top, bottom, right, left } = useSafeAreaInsets(); @@ -466,7 +458,6 @@ const FABGroup = ({ importantForAccessibility="no-hide-descendants" testID={it.testID} visible={open} - rippleColor={it.rippleColor} /> ); @@ -487,7 +478,6 @@ const FABGroup = ({ label={label} testID={testID} variant={variant} - rippleColor={rippleColor} /> diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index 42dd05ff7e..384fcc6c45 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -234,14 +234,12 @@ export const getFABColors = ({ disabled, customColor, customBackgroundColor, - customRippleColor, }: { theme: InternalTheme; variant: string; disabled?: boolean; customColor?: string; customBackgroundColor?: ColorValue; - customRippleColor?: ColorValue; }) => { const isVariant = (variantToCompare: Variant) => { return variant === variantToCompare; @@ -262,8 +260,6 @@ export const getFABColors = ({ return { backgroundColor, foregroundColor, - rippleColor: - customRippleColor || color(foregroundColor).alpha(0.12).rgb().string(), }; }; diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 2da6e12fcf..dbe7de4841 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -6,7 +6,6 @@ import { ViewStyle, View, Animated, - ColorValue, } from 'react-native'; import { getIconButtonColor } from './utils'; @@ -43,11 +42,6 @@ export type Props = Omit<$RemoveChildren, 'style'> & { */ containerColor?: string; /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; - /** - * @supported Available in v5.x with theme version 3 * Whether icon button is selected. A selected button receives alternative combination of icon and container colors. */ selected?: boolean; @@ -120,7 +114,6 @@ const IconButton = forwardRef( icon, iconColor: customIconColor, containerColor: customContainerColor, - rippleColor: customRippleColor, size = 24, accessibilityLabel, disabled, @@ -141,16 +134,14 @@ const IconButton = forwardRef( const IconComponent = animated ? CrossFadeIcon : Icon; - const { iconColor, rippleColor, backgroundColor, borderColor } = - getIconButtonColor({ - theme, - disabled, - selected, - mode, - customIconColor, - customContainerColor, - customRippleColor, - }); + const { iconColor, backgroundColor, borderColor } = getIconButtonColor({ + theme, + disabled, + selected, + mode, + customIconColor, + customContainerColor, + }); const buttonSize = size + 2 * PADDING; @@ -186,7 +177,6 @@ const IconButton = forwardRef( borderless centered onPress={onPress} - rippleColor={rippleColor} accessibilityLabel={accessibilityLabel} style={[styles.touchable, contentStyle]} // @ts-expect-error We keep old a11y props for backwards compat with old RN versions diff --git a/src/components/IconButton/utils.ts b/src/components/IconButton/utils.ts index f808ec6970..fd985b04aa 100644 --- a/src/components/IconButton/utils.ts +++ b/src/components/IconButton/utils.ts @@ -1,7 +1,3 @@ -import type { ColorValue } from 'react-native'; - -import color from 'color'; - import type { InternalTheme } from '../../types'; type IconButtonMode = 'outlined' | 'contained' | 'contained-tonal'; @@ -109,19 +105,6 @@ const getIconColor = ({ return theme.colors.onSurfaceVariant; }; -const getRippleColor = ({ - iconColor, - customRippleColor, -}: { - iconColor: string; - customRippleColor?: ColorValue; -}) => { - if (customRippleColor) { - return customRippleColor; - } - return color(iconColor).alpha(0.12).rgb().string(); -}; - export const getIconButtonColor = ({ theme, disabled, @@ -129,7 +112,6 @@ export const getIconButtonColor = ({ selected, customIconColor, customContainerColor, - customRippleColor, }: { theme: InternalTheme; disabled?: boolean; @@ -137,7 +119,6 @@ export const getIconButtonColor = ({ mode?: IconButtonMode; customIconColor?: string; customContainerColor?: string; - customRippleColor?: ColorValue; }) => { const isMode = (modeToCompare: IconButtonMode) => { return mode === modeToCompare; @@ -161,7 +142,6 @@ export const getIconButtonColor = ({ ...baseIconColorProps, customContainerColor, }), - rippleColor: getRippleColor({ iconColor, customRippleColor }), borderColor: getBorderColor({ theme, disabled }), }; }; diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index ace8810a71..3eaee97d61 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { - ColorValue, GestureResponderEvent, I18nManager, NativeSyntheticEvent, @@ -93,10 +92,6 @@ export type Props = { * Style that is passed to Description element. */ descriptionStyle?: StyleProp; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Truncate Title text such that the total number of lines does not * exceed this number. @@ -186,7 +181,6 @@ const ListAccordion = ({ descriptionStyle, titleNumberOfLines = 1, descriptionNumberOfLines = 2, - rippleColor: customRippleColor, style, containerStyle, contentStyle, @@ -238,10 +232,9 @@ const ListAccordion = ({ ? groupContext.expandedId === id : expandedInternal; - const { descriptionColor, titleTextColor, rippleColor } = getAccordionColors({ + const { descriptionColor, titleTextColor } = getAccordionColors({ theme, isExpanded, - customRippleColor, }); const handlePress = @@ -256,7 +249,6 @@ const ListAccordion = ({ onPress={handlePress} onLongPress={onLongPress} delayLongPress={delayLongPress} - rippleColor={rippleColor} accessibilityRole="button" accessibilityState={{ expanded: isExpanded }} accessibilityLabel={accessibilityLabel} diff --git a/src/components/List/utils.ts b/src/components/List/utils.ts index 05186efe8e..11a1618fbc 100644 --- a/src/components/List/utils.ts +++ b/src/components/List/utils.ts @@ -1,6 +1,5 @@ -import { ColorValue, StyleSheet, StyleProp, ViewStyle } from 'react-native'; +import { StyleSheet, StyleProp, ViewStyle } from 'react-native'; -import color from 'color'; import type { EllipsizeProp, InternalTheme, ThemeProp } from 'src/types'; type Description = @@ -90,11 +89,9 @@ const styles = StyleSheet.create({ export const getAccordionColors = ({ theme, isExpanded, - customRippleColor, }: { theme: InternalTheme; isExpanded?: boolean; - customRippleColor?: ColorValue; }) => { const titleColor = theme.colors.onSurface; @@ -102,12 +99,8 @@ export const getAccordionColors = ({ const titleTextColor = isExpanded ? theme.colors?.primary : titleColor; - const rippleColor = - customRippleColor || color(titleTextColor).alpha(0.12).rgb().string(); - return { descriptionColor, titleTextColor, - rippleColor, }; }; diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 652ffc9a4f..725e38cd5b 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { AccessibilityState, - ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, @@ -82,10 +81,6 @@ export type Props = { * Style that is passed to the Title element. */ titleStyle?: StyleProp; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * @optional */ @@ -142,7 +137,6 @@ const MenuItem = ({ containerStyle, contentStyle, titleStyle, - rippleColor: customRippleColor, testID = 'menu-item', accessibilityLabel, accessibilityState, @@ -151,10 +145,9 @@ const MenuItem = ({ hitSlop, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { titleColor, iconColor, rippleColor } = getMenuItemColor({ + const { titleColor, iconColor } = getMenuItemColor({ theme, disabled, - customRippleColor, }); const containerPadding = 12; @@ -190,7 +183,6 @@ const MenuItem = ({ accessibilityLabel={accessibilityLabel} accessibilityRole="menuitem" accessibilityState={newAccessibilityState} - rippleColor={rippleColor} hitSlop={hitSlop} > diff --git a/src/components/Menu/utils.ts b/src/components/Menu/utils.ts index 71c503bcc2..8fc73ca082 100644 --- a/src/components/Menu/utils.ts +++ b/src/components/Menu/utils.ts @@ -1,7 +1,3 @@ -import type { ColorValue } from 'react-native'; - -import color from 'color'; - import type { InternalTheme, MD3Theme } from '../../types'; import type { IconSource } from '../Icon'; @@ -17,7 +13,6 @@ type ContentProps = { type ColorProps = { theme: InternalTheme; disabled?: boolean; - customRippleColor?: ColorValue; }; const getDisabledColor = (theme: InternalTheme) => { @@ -42,29 +37,10 @@ const getIconColor = ({ theme, disabled }: ColorProps) => { return colors.onSurfaceVariant; }; -const getRippleColor = ({ - theme, - customRippleColor, -}: Omit) => { - if (customRippleColor) { - return customRippleColor; - } - - return color((theme as MD3Theme).colors.onSurfaceVariant) - .alpha(0.12) - .rgb() - .string(); -}; - -export const getMenuItemColor = ({ - theme, - disabled, - customRippleColor, -}: ColorProps) => { +export const getMenuItemColor = ({ theme, disabled }: ColorProps) => { return { titleColor: getTitleColor({ theme, disabled }), iconColor: getIconColor({ theme, disabled }), - rippleColor: getRippleColor({ theme, customRippleColor }), }; }; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index eeff5c3d26..aa6c401c1f 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { - ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, @@ -60,10 +59,6 @@ export type Props = { * Custom color for radio. */ color?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Status of radio button. */ @@ -152,7 +147,6 @@ const RadioButtonItem = ({ disabled, color, uncheckedColor, - rippleColor, status, theme: themeOverrides, background, @@ -223,7 +217,6 @@ const RadioButtonItem = ({ disabled={disabled} background={background} theme={theme} - rippleColor={rippleColor} hitSlop={hitSlop} > diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 6377899280..1f49d7925c 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Animated, - ColorValue, GestureResponderEvent, I18nManager, Platform, @@ -14,8 +13,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; - import ActivityIndicator from './ActivityIndicator'; import Divider from './Divider'; import type { IconSource } from './Icon'; @@ -56,10 +53,6 @@ export type Props = React.ComponentPropsWithRef & { * Custom color for icon, default will be derived from theme */ iconColor?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Callback to execute if we want the left icon to act as button. */ @@ -94,12 +87,6 @@ export type Props = React.ComponentPropsWithRef & { */ traileringIconColor?: string; /** - * @supported Available in v5.x with theme version 3 - * Color of the trailering icon ripple effect. - */ - traileringRippleColor?: ColorValue; - /** - * @supported Available in v5.x with theme version 3 * Callback to execute on the right trailering icon button press. */ onTraileringIconPress?: (e: GestureResponderEvent) => void; @@ -181,7 +168,6 @@ const Searchbar = forwardRef( { icon, iconColor: customIconColor, - rippleColor: customRippleColor, onIconPress, searchAccessibilityLabel = 'search', clearIcon, @@ -190,7 +176,6 @@ const Searchbar = forwardRef( traileringIcon, traileringIconColor, traileringIconAccessibilityLabel, - traileringRippleColor: customTraileringRippleColor, onTraileringIconPress, right, mode = 'bar', @@ -230,14 +215,9 @@ const Searchbar = forwardRef( const { roundness, dark } = theme; - const placeholderTextColor = colors.onSurface; - const textColor = colors.onSurfaceVariant; - const iconColor = customIconColor || colors.onSurfaceVariant; - const rippleColor = - customRippleColor || color(textColor).alpha(0.32).rgb().string(); - const traileringRippleColor = - customTraileringRippleColor || - color(textColor).alpha(0.32).rgb().string(); + const placeholderTextColor = theme.colors.onSurface; + const textColor = theme.colors.onSurfaceVariant; + const iconColor = customIconColor || theme.colors.onSurfaceVariant; const font = { ...fonts.bodyLarge, @@ -273,7 +253,6 @@ const Searchbar = forwardRef( ( borderless accessibilityLabel={clearAccessibilityLabel} iconColor={value ? iconColor : 'rgba(255, 255, 255, 0)'} - rippleColor={rippleColor} onPress={handleClearPress} icon={ clearIcon || @@ -360,8 +338,7 @@ const Searchbar = forwardRef( accessibilityRole="button" borderless onPress={onTraileringIconPress} - iconColor={traileringIconColor || colors.onSurfaceVariant} - rippleColor={traileringRippleColor} + iconColor={traileringIconColor || theme.colors.onSurfaceVariant} icon={traileringIcon} accessibilityLabel={traileringIconAccessibilityLabel} testID={`${testID}-trailering-icon`} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 64471f59fd..6f04b6aeb7 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Animated, - ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, @@ -11,7 +10,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; import type { ThemeProp } from 'src/types'; import { @@ -46,10 +44,6 @@ export type Props = { * Custom color for checked Text and Icon. */ checkedColor?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Whether the button is disabled. */ @@ -119,7 +113,6 @@ const SegmentedButtonItem = ({ showSelectedCheck, checkedColor, uncheckedColor, - rippleColor: customRippleColor, background, icon, testID, @@ -167,9 +160,6 @@ const SegmentedButtonItem = ({ theme, segment, }); - const rippleColor = - customRippleColor || color(textColor).alpha(0.12).rgb().string(); - const showIcon = !icon ? false : label && checked ? !showSelectedCheck : true; const showCheckedIcon = checked && showSelectedCheck; @@ -214,7 +204,6 @@ const SegmentedButtonItem = ({ accessibilityState={{ disabled, checked }} accessibilityRole="button" disabled={disabled} - rippleColor={rippleColor} testID={testID} style={rippleStyle} background={background} diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index 22b55bb02a..6151e23ae2 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Animated, - ColorValue, Easing, I18nManager, StyleProp, @@ -41,12 +40,6 @@ export type Props = $Omit, 'mode'> & { */ icon?: IconSource; /** - * @supported Available in v5.x with theme version 3 - * Color of the ripple effect. - */ - rippleColor?: ColorValue; - /** - * @supported Available in v5.x with theme version 3 * Function to execute on icon button press. The icon button appears only when this prop is specified. */ onIconPress?: () => void; @@ -161,7 +154,6 @@ const Snackbar = ({ contentStyle, theme: themeOverrides, maxFontSizeMultiplier, - rippleColor, testID, ...rest }: Props) => { @@ -253,7 +245,6 @@ const Snackbar = ({ style: actionStyle, label: actionLabel, onPress: onPressAction, - rippleColor: actionRippleColor, ...actionProps } = action || {}; @@ -338,7 +329,6 @@ const Snackbar = ({ compact={false} mode="text" theme={theme} - rippleColor={actionRippleColor} {...actionProps} > {actionLabel} @@ -349,8 +339,7 @@ const Snackbar = ({ accessibilityRole="button" borderless onPress={onIconPress} - iconColor={colors.inverseOnSurface} - rippleColor={rippleColor} + iconColor={theme.colors.inverseOnSurface} theme={theme} icon={ icon || diff --git a/src/components/TextInput/Adornment/TextInputIcon.tsx b/src/components/TextInput/Adornment/TextInputIcon.tsx index 8f4ce7c4df..0005071fe3 100644 --- a/src/components/TextInput/Adornment/TextInputIcon.tsx +++ b/src/components/TextInput/Adornment/TextInputIcon.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { - ColorValue, GestureResponderEvent, StyleProp, StyleSheet, @@ -37,10 +36,6 @@ export type Props = $Omit< * Color of the icon or a function receiving a boolean indicating whether the TextInput is focused and returning the color. */ color?: ((isTextInputFocused: boolean) => string | undefined) | string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; style?: StyleProp; /** * @optional @@ -129,7 +124,6 @@ const TextInputIcon = ({ forceTextInputFocus = true, color: customColor, theme: themeOverrides, - rippleColor, ...rest }: Props) => { const { style, isTextInputFocused, forceFocus, testID, disabled } = @@ -165,7 +159,6 @@ const TextInputIcon = ({ iconColor={iconColor} testID={testID} theme={themeOverrides} - rippleColor={rippleColor} {...rest} /> diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index e115dec7ec..ba4bd138a3 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -6,7 +6,6 @@ import { ViewStyle, View, Animated, - ColorValue, } from 'react-native'; import { ToggleButtonGroupContext } from './ToggleButtonGroup'; @@ -30,10 +29,6 @@ export type Props = { * Custom text color for button. */ iconColor?: string; - /** - * Color of the ripple effect. - */ - rippleColor?: ColorValue; /** * Whether the button is disabled. */ @@ -108,7 +103,6 @@ const ToggleButton = forwardRef( value, status, onPress, - rippleColor, ...rest }: Props, ref @@ -155,7 +149,6 @@ const ToggleButton = forwardRef( ]} ref={ref} theme={theme} - rippleColor={rippleColor} {...rest} /> ); diff --git a/src/components/TouchableRipple/utils.ts b/src/components/TouchableRipple/utils.ts index e3a1e9726f..83308eb228 100644 --- a/src/components/TouchableRipple/utils.ts +++ b/src/components/TouchableRipple/utils.ts @@ -29,7 +29,7 @@ const getRippleColor = ({ return rippleColor; } - return color(theme.colors.onSurface).alpha(0.12).rgb().string(); + return color(theme.colors.onSurface).alpha(0.1).rgb().string(); }; export const getTouchableRippleColors = ({ diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 6c0e851f22..5505a3726e 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -186,48 +186,6 @@ describe('getChipColors - icon color', () => { }); }); -describe('getChipColors - ripple color', () => { - it('should return theme color, for theme version 3, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(), - isOutlined: false, - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.onSecondaryContainer) - .alpha(0.12) - .rgb() - .string(), - }); - }); - - it('should return theme color, for theme version 3, outline mode', () => { - expect( - getChipColors({ - theme: getTheme(), - isOutlined: true, - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.onSurfaceVariant) - .alpha(0.12) - .rgb() - .string(), - }); - }); - - it('should return custom color, for theme version 3', () => { - expect( - getChipColors({ - theme: getTheme(), - selectedColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - rippleColor: color('purple').alpha(0.12).rgb().string(), - }); - }); -}); - describe('getChipColor - selected background color', () => { it('should return custom color, for theme version 3, outlined mode', () => { expect( diff --git a/src/components/__tests__/FABGroup.test.tsx b/src/components/__tests__/FABGroup.test.tsx index 3eb51c78d6..7009526e42 100644 --- a/src/components/__tests__/FABGroup.test.tsx +++ b/src/components/__tests__/FABGroup.test.tsx @@ -137,37 +137,6 @@ it('correctly adds label prop', () => { expect(getByText('Label test')).toBeTruthy(); }); -it('correct renders custom ripple color passed to FAB.Group and its item', () => { - const { getByTestId } = render( - {}} - actions={[ - { - label: 'testing', - onPress() {}, - icon: '', - rippleColor: 'yellow', - testID: 'fab-group-item', - }, - ]} - /> - ); - - expect( - getByTestId('fab-group-container').props.children.props.rippleColor - ).toBe('orange'); - - expect( - getByTestId('fab-group-item-container').props.children.props.rippleColor - ).toBe('yellow'); -}); - it('animated value changes correctly', () => { const value = new Animated.Value(1); const { getByTestId } = render( diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index 11f47cb49c..39d1eb8663 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; import { act, render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; import { pink500 } from '../../styles/themes/v2/colors'; @@ -308,21 +307,6 @@ describe('getIconButtonColor - border color', () => { }); }); -describe('getIconButtonColor - ripple color', () => { - it('should return theme color, for theme version 3', () => { - expect( - getIconButtonColor({ - theme: getTheme(), - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.onSurfaceVariant) - .alpha(0.12) - .rgb() - .string(), - }); - }); -}); - it('action animated value changes correctly', () => { const value = new Animated.Value(1); const { getByTestId } = render( diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index 9695b3c186..65a6a9d4de 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import { render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; import { red500 } from '../../styles/themes/v2/colors'; @@ -149,29 +148,3 @@ describe('getAccordionColors - title text color', () => { }); }); }); - -describe('getAccordionColors - ripple color', () => { - it('should return theme color, for theme version 3', () => { - expect( - getAccordionColors({ - theme: getTheme(), - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.onSurface) - .alpha(0.12) - .rgb() - .string(), - }); - }); - - it('should return primary color if it is expanded', () => { - expect( - getAccordionColors({ - theme: getTheme(), - isExpanded: true, - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.primary).alpha(0.12).rgb().string(), - }); - }); -}); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index d482800582..10fb008b5e 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; import Menu from '../Menu/Menu'; @@ -105,18 +104,3 @@ describe('getMenuItemColor - icon color', () => { }); }); }); - -describe('getMenuItemColor - ripple color', () => { - it('should return correct theme color, for theme version 3', () => { - expect( - getMenuItemColor({ - theme: getTheme(), - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.onSurfaceVariant) - .alpha(0.12) - .rgb() - .string(), - }); - }); -}); diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 59ade128e4..eb44e85481 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -38,15 +38,6 @@ it('renders without ActivityIndicator', () => { expect(() => getByTestId('activity-indicator')).toThrow(); }); -it('render icon with custom ripple color', () => { - const { getByTestId } = render( - - ); - - const iconContainer = getByTestId('search-bar-icon-container').props.children; - expect(iconContainer.props.rippleColor).toBe('purple'); -}); - it('renders clear icon with custom color', () => { const { getByTestId } = render( @@ -141,16 +132,6 @@ it('renders clear icon wrapper, with appropriate style for v3', () => { }); }); -it('render clear icon with custom ripple color', () => { - const { getByTestId } = render( - - ); - - const clearIconContainer = getByTestId('search-bar-clear-icon-container') - .props.children; - expect(clearIconContainer.props.rippleColor).toBe('purple'); -}); - it('renders trailering icon when mode is set to "bar"', () => { const { getByTestId } = render( { expect(onTraileringIconPressMock).toHaveBeenCalledTimes(1); }); -it('renders trailering icon with custom ripple colors', () => { - const { getByTestId } = render( - - ); - - const traileringIconContainer = getByTestId( - 'search-bar-trailering-icon-container' - ).props.children; - expect(traileringIconContainer.props.rippleColor).toBe('purple'); -}); - it('renders clear icon instead of trailering icon', () => { const { getByTestId, update, queryByTestId } = render( { expect(tree).toMatchSnapshot(); }); -it('renders with custom ripple color', () => { - const { getByTestId } = render( - {}} - onIconPress={() => {}} - rippleColor="purple" - testID="snackbar" - > - Snackbar content - - ); - - const iconContainer = getByTestId('snackbar-icon-container').props.children; - expect(iconContainer.props.rippleColor).toBe('purple'); -}); - it('animated value changes correctly', () => { const value = new Animated.Value(1); const { getByTestId } = render( diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx index a0e2bbafcb..61936f4909 100644 --- a/src/components/__tests__/ToggleButton.test.tsx +++ b/src/components/__tests__/ToggleButton.test.tsx @@ -33,22 +33,6 @@ it('renders unchecked toggle button', () => { expect(tree).toMatchSnapshot(); }); -it('render toggle button with custom ripple color', () => { - const { getByTestId } = render( - - ); - - const iconContainer = getByTestId('toggle-button-container').props.children; - expect(iconContainer.props.rippleColor).toBe('purple'); -}); - describe('getToggleButtonColor', () => { it('should return correct color when checked and theme version 3', () => { expect(getToggleButtonColor({ theme: getTheme(), checked: true })).toBe( From 709325ea91a17b74a9bcd414a4dca2efffb318ef Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Wed, 29 Apr 2026 14:20:36 +0300 Subject: [PATCH 04/94] fix(MD3): update color schema and tokens (#4900) * fix: update colors as per material-components color tokens * see https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/color/res/values/tokens.xml * fix: update the color schema for LightTheme and DarkTheme * see: https://m3.material.io/styles/color/roles --- example/src/index.native.tsx | 7 +- example/utils/index.ts | 28 +-- .../__tests__/Appbar/Appbar.test.tsx | 4 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 20 +-- .../Card/__snapshots__/Card.test.tsx.snap | 4 +- .../__snapshots__/CheckboxItem.test.tsx.snap | 16 +- .../RadioButtonItem.test.tsx.snap | 16 +- .../__snapshots__/Text.test.tsx.snap | 32 ++-- .../__snapshots__/Avatar.test.tsx.snap | 8 +- .../__snapshots__/Banner.test.tsx.snap | 54 +++--- .../BottomNavigation.test.tsx.snap | 144 ++++++++-------- .../__snapshots__/Button.test.tsx.snap | 28 +-- .../__snapshots__/Chip.test.tsx.snap | 14 +- .../__snapshots__/DataTable.test.tsx.snap | 70 ++++---- .../__snapshots__/DrawerItem.test.tsx.snap | 6 +- .../__tests__/__snapshots__/FAB.test.tsx.snap | 10 +- .../__snapshots__/IconButton.test.tsx.snap | 4 +- .../__snapshots__/ListAccordion.test.tsx.snap | 36 ++-- .../__snapshots__/ListItem.test.tsx.snap | 38 ++-- .../__snapshots__/ListSection.test.tsx.snap | 163 ++++++++++++------ .../__snapshots__/Menu.test.tsx.snap | 30 ++-- .../__snapshots__/MenuItem.test.tsx.snap | 24 +-- .../__snapshots__/Searchbar.test.tsx.snap | 18 +- .../SegmentedButton.test.tsx.snap | 6 +- .../__snapshots__/Snackbar.test.tsx.snap | 26 +-- .../__snapshots__/TextInput.test.tsx.snap | 22 +-- .../__snapshots__/ToggleButton.test.tsx.snap | 4 +- src/styles/themes/v3/DarkTheme.tsx | 36 ++-- src/styles/themes/v3/LightTheme.tsx | 36 ++-- src/styles/themes/v3/tokens.tsx | 38 ++-- src/types.tsx | 19 ++ 31 files changed, 544 insertions(+), 417 deletions(-) diff --git a/example/src/index.native.tsx b/example/src/index.native.tsx index 880fc4f04c..0d5be506e7 100644 --- a/example/src/index.native.tsx +++ b/example/src/index.native.tsx @@ -57,8 +57,11 @@ export default function PaperExample() { } return isDarkMode - ? { ...MD3DarkTheme, colors: mdTheme.dark } - : { ...MD3LightTheme, colors: mdTheme.light }; + ? { ...MD3DarkTheme, colors: { ...MD3DarkTheme.colors, ...mdTheme.dark } } + : { + ...MD3LightTheme, + colors: { ...MD3LightTheme.colors, ...mdTheme.light }, + }; }, [isDarkMode, mdTheme, shouldUseDeviceColors]); React.useEffect(() => { diff --git a/example/utils/index.ts b/example/utils/index.ts index 5c15917644..f86f3a0307 100644 --- a/example/utils/index.ts +++ b/example/utils/index.ts @@ -1162,71 +1162,71 @@ export const colorThemes = { pink: { light: { ...MD3LightTheme, - ...lightPinkColors, + colors: { ...MD3LightTheme.colors, ...lightPinkColors.colors }, }, dark: { ...MD3DarkTheme, - ...darkPinkColors, + colors: { ...MD3DarkTheme.colors, ...darkPinkColors.colors }, }, }, green: { light: { ...MD3LightTheme, - ...lightGreenColors, + colors: { ...MD3LightTheme.colors, ...lightGreenColors.colors }, }, dark: { ...MD3DarkTheme, - ...darkGreenColors, + colors: { ...MD3DarkTheme.colors, ...darkGreenColors.colors }, }, }, blue: { light: { ...MD3LightTheme, - ...lightBlueColors, + colors: { ...MD3LightTheme.colors, ...lightBlueColors.colors }, }, dark: { ...MD3DarkTheme, - ...darkBlueColors, + colors: { ...MD3DarkTheme.colors, ...darkBlueColors.colors }, }, }, orange: { light: { ...MD3LightTheme, - ...lightOrangeColors, + colors: { ...MD3LightTheme.colors, ...lightOrangeColors.colors }, }, dark: { ...MD3DarkTheme, - ...darkOrangeColors, + colors: { ...MD3DarkTheme.colors, ...darkOrangeColors.colors }, }, }, red: { light: { ...MD3LightTheme, - ...lightRedColors, + colors: { ...MD3LightTheme.colors, ...lightRedColors.colors }, }, dark: { ...MD3DarkTheme, - ...darkRedColors, + colors: { ...MD3DarkTheme.colors, ...darkRedColors.colors }, }, }, yellow: { light: { ...MD3LightTheme, - ...lightYellowColors, + colors: { ...MD3LightTheme.colors, ...lightYellowColors.colors }, }, dark: { ...MD3DarkTheme, - ...darkYellowColors, + colors: { ...MD3DarkTheme.colors, ...darkYellowColors.colors }, }, }, cyan: { light: { ...MD3LightTheme, - ...lightCyanColors, + colors: { ...MD3LightTheme.colors, ...lightCyanColors.colors }, }, dark: { ...MD3DarkTheme, - ...darkCyanColors, + colors: { ...MD3DarkTheme.colors, ...darkCyanColors.colors }, }, }, } as { [key: string]: { light: MD3Theme; dark: MD3Theme } }; diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 60a7827b6e..6c80269a0f 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -303,13 +303,13 @@ describe('getAppbarColors', () => { it('should return v3 light color if theme version is 3', () => { expect(getAppbarBackgroundColor(getTheme(), elevation)).toBe( - tokens.md.ref.palette.neutral99 + tokens.md.ref.palette.neutral98 ); }); it('should return v3 dark color if theme version is 3', () => { expect(getAppbarBackgroundColor(getTheme(true), elevation)).toBe( - tokens.md.ref.palette.neutral10 + tokens.md.ref.palette.neutral6 ); }); }); diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 14afac63f3..50ab8e3cd6 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -5,7 +5,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "height": 64, "shadowColor": "#000", "shadowOffset": { @@ -23,7 +23,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "flex": 1, "flexDirection": "row", "paddingBottom": undefined, @@ -46,7 +46,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -64,7 +64,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -217,7 +217,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` accessibilityRole="search" keyboardAppearance="light" placeholder="Search" - placeholderTextColor="rgba(28, 27, 31, 1)" + placeholderTextColor="rgba(29, 27, 32, 1)" returnKeyType="search" selectionColor="rgba(103, 80, 164, 1)" style={ @@ -409,7 +409,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A collapsable={false} style={ { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "height": 64, "shadowColor": "#000", "shadowOffset": { @@ -427,7 +427,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A style={ { "alignItems": "center", - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "flex": 1, "flexDirection": "row", "paddingBottom": undefined, @@ -614,7 +614,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A }, { "height": 21, - "tintColor": "rgba(28, 27, 31, 1)", + "tintColor": "rgba(29, 27, 32, 1)", "width": 21, }, ] @@ -659,7 +659,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -672,7 +672,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 22, "fontWeight": "400", diff --git a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap index 301eea942c..a5a8abd1ac 100644 --- a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap +++ b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap @@ -5,7 +5,7 @@ exports[`Card renders an outlined card 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "borderRadius": 12, "shadowColor": "#000", "shadowOffset": { @@ -22,7 +22,7 @@ exports[`Card renders an outlined card 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "borderRadius": 12, "flex": undefined, "shadowColor": "#000", diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index dbd992a134..3325829520 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -147,7 +147,7 @@ exports[`can render leading checkbox control 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -164,7 +164,7 @@ exports[`can render leading checkbox control 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "right", }, undefined, @@ -244,7 +244,7 @@ exports[`can render the Android checkbox on different platforms 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -261,7 +261,7 @@ exports[`can render the Android checkbox on different platforms 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "left", }, undefined, @@ -460,7 +460,7 @@ exports[`can render the iOS checkbox on different platforms 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -477,7 +477,7 @@ exports[`can render the iOS checkbox on different platforms 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "left", }, undefined, @@ -640,7 +640,7 @@ exports[`renders unchecked 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -657,7 +657,7 @@ exports[`renders unchecked 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "left", }, undefined, diff --git a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap index e74650efd2..de667249ae 100644 --- a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap +++ b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap @@ -145,7 +145,7 @@ exports[`can render leading radio button control 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -162,7 +162,7 @@ exports[`can render leading radio button control 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "right", }, undefined, @@ -239,7 +239,7 @@ exports[`can render the Android radio button on different platforms 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -256,7 +256,7 @@ exports[`can render the Android radio button on different platforms 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "left", }, undefined, @@ -390,7 +390,7 @@ exports[`can render the iOS radio button on different platforms 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -407,7 +407,7 @@ exports[`can render the iOS radio button on different platforms 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "left", }, undefined, @@ -567,7 +567,7 @@ exports[`renders unchecked 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -584,7 +584,7 @@ exports[`renders unchecked 1`] = ` "flexShrink": 1, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "textAlign": "left", }, undefined, diff --git a/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap b/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap index a47cd14611..5b87989124 100644 --- a/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap +++ b/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap @@ -9,7 +9,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -34,7 +34,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -59,7 +59,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -84,7 +84,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -109,7 +109,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -134,7 +134,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -159,7 +159,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -184,7 +184,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -209,7 +209,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -234,7 +234,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -259,7 +259,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -284,7 +284,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -309,7 +309,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -334,7 +334,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -359,7 +359,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -386,7 +386,7 @@ exports[`renders v3 Text component with custom variant correctly 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ diff --git a/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap b/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap index c73f2ae180..9e55666cef 100644 --- a/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Avatar.test.tsx.snap @@ -158,7 +158,7 @@ exports[`renders avatar with text 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -212,7 +212,7 @@ exports[`renders avatar with text and custom background color 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -266,7 +266,7 @@ exports[`renders avatar with text and custom colors 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -320,7 +320,7 @@ exports[`renders avatar with text and custom size 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, diff --git a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap index 175383e4ad..0a61a1a3de 100644 --- a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap @@ -5,7 +5,7 @@ exports[`render visible banner, with custom theme 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -22,7 +22,7 @@ exports[`render visible banner, with custom theme 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -81,7 +81,7 @@ exports[`render visible banner, with custom theme 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -221,7 +221,7 @@ exports[`render visible banner, with custom theme 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -278,7 +278,7 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "opacity": 0, "shadowColor": "#000", "shadowOffset": { @@ -295,7 +295,7 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -366,7 +366,7 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -383,7 +383,7 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` "margin": 8, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, ], ], @@ -413,7 +413,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -430,7 +430,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -511,7 +511,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -528,7 +528,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "margin": 8, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, ], ], @@ -651,7 +651,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -708,7 +708,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -725,7 +725,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -784,7 +784,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -801,7 +801,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "margin": 8, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, ], ], @@ -924,7 +924,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1073,7 +1073,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1130,7 +1130,7 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -1147,7 +1147,7 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -1228,7 +1228,7 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1245,7 +1245,7 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` "margin": 8, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, ], ], @@ -1275,7 +1275,7 @@ exports[`renders visible banner, without action buttons and without image 1`] = collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -1292,7 +1292,7 @@ exports[`renders visible banner, without action buttons and without image 1`] = collapsable={false} style={ { - "backgroundColor": "rgb(247, 243, 249)", + "backgroundColor": "rgba(255, 255, 255, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -1351,7 +1351,7 @@ exports[`renders visible banner, without action buttons and without image 1`] = "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1368,7 +1368,7 @@ exports[`renders visible banner, without action buttons and without image 1`] = "margin": 8, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, ], ], diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index cc788ce0b9..6bc02934ba 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -20,7 +20,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -113,7 +113,7 @@ exports[`allows customizing Route's type via generics 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -313,7 +313,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -332,7 +332,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -369,7 +369,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -388,7 +388,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -567,7 +567,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -623,7 +623,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -687,7 +687,7 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -780,7 +780,7 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -1428,7 +1428,7 @@ exports[`hides labels in shifting bottom navigation 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -1521,7 +1521,7 @@ exports[`hides labels in shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -2169,7 +2169,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -2394,7 +2394,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -2654,7 +2654,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -2673,7 +2673,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -2710,7 +2710,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -2729,7 +2729,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -2968,7 +2968,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -3024,7 +3024,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -3282,7 +3282,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -3338,7 +3338,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -3596,7 +3596,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -3652,7 +3652,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -3910,7 +3910,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -3966,7 +3966,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -4030,7 +4030,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -4123,7 +4123,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -4388,7 +4388,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -4407,7 +4407,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -4651,7 +4651,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -4914,7 +4914,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -5177,7 +5177,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -5440,7 +5440,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -5504,7 +5504,7 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -5597,7 +5597,7 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -5790,7 +5790,7 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` } > Route: 0 @@ -5809,7 +5809,7 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` } > Route: 0 @@ -6206,7 +6206,7 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -6299,7 +6299,7 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -6497,7 +6497,7 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` } > Route: 0 @@ -7200,7 +7200,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -7293,7 +7293,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -7553,7 +7553,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -7609,7 +7609,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -7867,7 +7867,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -7923,7 +7923,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -8181,7 +8181,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -8237,7 +8237,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -8301,7 +8301,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -8394,7 +8394,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -8659,7 +8659,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -8922,7 +8922,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -9185,7 +9185,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -9249,7 +9249,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -9342,7 +9342,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -9602,7 +9602,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -9621,7 +9621,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -9658,7 +9658,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -9677,7 +9677,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -9916,7 +9916,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -9972,7 +9972,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -10230,7 +10230,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -10286,7 +10286,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -10350,7 +10350,7 @@ exports[`renders shifting bottom navigation 1`] = ` "flex": 1, }, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", }, ] } @@ -10443,7 +10443,7 @@ exports[`renders shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "overflow": "hidden", } } @@ -10708,7 +10708,7 @@ exports[`renders shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -10727,7 +10727,7 @@ exports[`renders shifting bottom navigation 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 12, "fontWeight": "500", @@ -10971,7 +10971,7 @@ exports[`renders shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -11234,7 +11234,7 @@ exports[`renders shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -11497,7 +11497,7 @@ exports[`renders shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -11760,7 +11760,7 @@ exports[`renders shifting bottom navigation 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ diff --git a/src/components/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/__tests__/__snapshots__/Button.test.tsx.snap index d5f335c3f1..fddcd2bc10 100644 --- a/src/components/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Button.test.tsx.snap @@ -105,7 +105,7 @@ exports[`renders button with an accessibility hint 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -255,7 +255,7 @@ exports[`renders button with an accessibility label 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -404,7 +404,7 @@ exports[`renders button with button color 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -553,7 +553,7 @@ exports[`renders button with color 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -702,7 +702,7 @@ exports[`renders button with custom testID 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -900,7 +900,7 @@ exports[`renders button with icon 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1100,7 +1100,7 @@ exports[`renders button with icon in reverse order 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1249,7 +1249,7 @@ exports[`renders contained contained with mode 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1399,7 +1399,7 @@ exports[`renders disabled button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1422,7 +1422,7 @@ exports[`renders disabled button 1`] = ` undefined, false, { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500", @@ -1756,7 +1756,7 @@ exports[`renders loading button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1905,7 +1905,7 @@ exports[`renders outlined button with mode 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -2055,7 +2055,7 @@ exports[`renders text button by default 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -2204,7 +2204,7 @@ exports[`renders text button with mode 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ diff --git a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap index 2994761722..f6d6b1379f 100644 --- a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap @@ -160,7 +160,7 @@ exports[`renders chip with close button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -455,7 +455,7 @@ exports[`renders chip with custom close button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -750,7 +750,7 @@ exports[`renders chip with icon 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -907,7 +907,7 @@ exports[`renders chip with onPress 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1064,7 +1064,7 @@ exports[`renders outlined disabled chip 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1081,7 +1081,7 @@ exports[`renders outlined disabled chip 1`] = ` "textAlignVertical": "center", }, { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500", @@ -1266,7 +1266,7 @@ exports[`renders selected chip 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index 59e80d0aef..7fde640799 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -54,7 +54,7 @@ exports[`DataTable.Cell renders data table cell 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -128,7 +128,7 @@ exports[`DataTable.Cell renders right aligned data table cell 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -213,7 +213,7 @@ exports[`DataTable.Header renders data table header 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -233,7 +233,7 @@ exports[`DataTable.Header renders data table header 1`] = ` }, {}, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, undefined, ], @@ -294,7 +294,7 @@ exports[`DataTable.Header renders data table header 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -314,7 +314,7 @@ exports[`DataTable.Header renders data table header 1`] = ` }, {}, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, undefined, ], @@ -352,7 +352,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -366,7 +366,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` "marginRight": 16, }, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, ], ] @@ -489,7 +489,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -623,7 +623,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -676,7 +676,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -690,7 +690,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "marginRight": 16, }, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, ], ] @@ -815,7 +815,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -949,7 +949,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -1083,7 +1083,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -1217,7 +1217,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -1270,7 +1270,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1284,7 +1284,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` "marginRight": 16, }, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, ], ] @@ -1409,7 +1409,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -1543,7 +1543,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -1606,7 +1606,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1620,7 +1620,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "marginRight": 16, }, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, ], ] @@ -1784,7 +1784,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1839,7 +1839,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1853,7 +1853,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "marginRight": 16, }, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, ], ] @@ -1978,7 +1978,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -2112,7 +2112,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -2246,7 +2246,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -2380,7 +2380,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 24, }, [ @@ -2474,7 +2474,7 @@ exports[`DataTable.Title renders data table title with press handler 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 16, }, [ @@ -2504,7 +2504,7 @@ exports[`DataTable.Title renders data table title with press handler 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -2602,7 +2602,7 @@ exports[`DataTable.Title renders data table title with sort icon 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontSize": 16, }, [ @@ -2632,7 +2632,7 @@ exports[`DataTable.Title renders data table title with sort icon 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -2718,7 +2718,7 @@ exports[`DataTable.Title renders right aligned data table title 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -2738,7 +2738,7 @@ exports[`DataTable.Title renders right aligned data table title 1`] = ` }, {}, { - "color": "rgba(28, 27, 31, 0.6)", + "color": "rgba(29, 27, 32, 0.6)", }, undefined, ], diff --git a/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap b/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap index e3d5e8ca8b..5746cc089b 100644 --- a/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DrawerItem.test.tsx.snap @@ -122,7 +122,7 @@ exports[`renders DrawerItem with icon 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -281,7 +281,7 @@ exports[`renders active DrawerItem 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -411,7 +411,7 @@ exports[`renders basic DrawerItem 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ diff --git a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap index b4a42c20fa..3eb29a581a 100644 --- a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap @@ -527,7 +527,7 @@ exports[`renders disabled FAB 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(28, 27, 31, 0.12)", + "backgroundColor": "rgba(29, 27, 32, 0.12)", "borderRadius": 16, "opacity": 1, "shadowColor": "#000", @@ -551,7 +551,7 @@ exports[`renders disabled FAB 1`] = ` pointerEvents="auto" style={ { - "backgroundColor": "rgba(28, 27, 31, 0.12)", + "backgroundColor": "rgba(29, 27, 32, 0.12)", "borderRadius": 16, "flex": undefined, "shadowColor": "#000", @@ -667,7 +667,7 @@ exports[`renders disabled FAB 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontSize": 24, }, [ @@ -873,7 +873,7 @@ exports[`renders extended FAB 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1087,7 +1087,7 @@ exports[`renders extended FAB with custom size prop 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ diff --git a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap index 1fe2b72a3d..82bcdbac24 100644 --- a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap @@ -26,7 +26,7 @@ exports[`renders disabled icon button 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(28, 27, 31, 0.12)", + "borderColor": "rgba(29, 27, 32, 0.12)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -115,7 +115,7 @@ exports[`renders disabled icon button 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontSize": 24, }, [ diff --git a/src/components/__tests__/__snapshots__/ListAccordion.test.tsx.snap b/src/components/__tests__/__snapshots__/ListAccordion.test.tsx.snap index 0857e59773..ec3393cb29 100644 --- a/src/components/__tests__/__snapshots__/ListAccordion.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListAccordion.test.tsx.snap @@ -5,7 +5,7 @@ exports[`renders expanded accordion 1`] = ` @@ -90,7 +90,7 @@ exports[`renders expanded accordion 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -237,7 +237,7 @@ exports[`renders expanded accordion 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -250,7 +250,7 @@ exports[`renders expanded accordion 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -270,7 +270,7 @@ exports[`renders list accordion with children 1`] = ` @@ -402,7 +402,7 @@ exports[`renders list accordion with children 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -415,7 +415,7 @@ exports[`renders list accordion with children 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -477,7 +477,7 @@ exports[`renders list accordion with custom title and description styles 1`] = ` @@ -562,7 +562,7 @@ exports[`renders list accordion with custom title and description styles 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -575,7 +575,7 @@ exports[`renders list accordion with custom title and description styles 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, { "color": "#f44336", @@ -596,7 +596,7 @@ exports[`renders list accordion with custom title and description styles 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -677,7 +677,7 @@ exports[`renders list accordion with left items 1`] = ` @@ -809,7 +809,7 @@ exports[`renders list accordion with left items 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -822,7 +822,7 @@ exports[`renders list accordion with left items 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -884,7 +884,7 @@ exports[`renders multiline list accordion 1`] = ` @@ -969,7 +969,7 @@ exports[`renders multiline list accordion 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -982,7 +982,7 @@ exports[`renders multiline list accordion 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -1001,7 +1001,7 @@ exports[`renders multiline list accordion 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, diff --git a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap index 1f8c683d3a..2ae673d6d3 100644 --- a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap @@ -82,7 +82,7 @@ exports[`renders list item with custom description 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -95,7 +95,7 @@ exports[`renders list item with custom description 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -276,7 +276,7 @@ exports[`renders list item with custom description 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -405,7 +405,7 @@ exports[`renders list item with custom title and description styles 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -418,7 +418,7 @@ exports[`renders list item with custom title and description styles 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, { "fontSize": 20, @@ -439,7 +439,7 @@ exports[`renders list item with custom title and description styles 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -553,7 +553,7 @@ exports[`renders list item with left and right items 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -566,7 +566,7 @@ exports[`renders list item with left and right items 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -585,7 +585,7 @@ exports[`renders list item with left and right items 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -787,7 +787,7 @@ exports[`renders list item with left item 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -800,7 +800,7 @@ exports[`renders list item with left item 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -896,7 +896,7 @@ exports[`renders list item with right item 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -909,7 +909,7 @@ exports[`renders list item with right item 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -1008,7 +1008,7 @@ exports[`renders list item with title and description 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1021,7 +1021,7 @@ exports[`renders list item with title and description 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -1040,7 +1040,7 @@ exports[`renders list item with title and description 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1149,7 +1149,7 @@ exports[`renders with a description with typeof number 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1162,7 +1162,7 @@ exports[`renders with a description with typeof number 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, { "fontSize": 20, @@ -1183,7 +1183,7 @@ exports[`renders with a description with typeof number 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index a2ed0f8c8e..875cfd0516 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -17,45 +17,64 @@ exports[`renders list section with custom title style 1`] = ` }, "colors": { "backdrop": "rgba(50, 47, 55, 0.4)", - "background": "rgba(255, 251, 254, 1)", + "background": "rgba(254, 247, 255, 1)", "elevation": { "level0": "transparent", - "level1": "rgb(247, 243, 249)", - "level2": "rgb(243, 237, 246)", - "level3": "rgb(238, 232, 244)", - "level4": "rgb(236, 230, 243)", - "level5": "rgb(233, 227, 241)", + "level1": "rgba(255, 255, 255, 1)", + "level2": "rgba(247, 242, 250, 1)", + "level3": "rgba(243, 237, 247, 1)", + "level4": "rgba(236, 230, 240, 1)", + "level5": "rgba(230, 224, 233, 1)", }, "error": "rgba(179, 38, 30, 1)", "errorContainer": "rgba(249, 222, 220, 1)", - "inverseOnSurface": "rgba(244, 239, 244, 1)", + "inverseOnSurface": "rgba(245, 239, 247, 1)", "inversePrimary": "rgba(208, 188, 255, 1)", - "inverseSurface": "rgba(49, 48, 51, 1)", - "onBackground": "rgba(28, 27, 31, 1)", + "inverseSurface": "rgba(50, 47, 53, 1)", + "onBackground": "rgba(29, 27, 32, 1)", "onError": "rgba(255, 255, 255, 1)", "onErrorContainer": "rgba(65, 14, 11, 1)", "onPrimary": "rgba(255, 255, 255, 1)", "onPrimaryContainer": "rgba(33, 0, 93, 1)", + "onPrimaryFixed": "rgba(33, 0, 93, 1)", + "onPrimaryFixedVariant": "rgba(79, 55, 139, 1)", "onSecondary": "rgba(255, 255, 255, 1)", "onSecondaryContainer": "rgba(29, 25, 43, 1)", - "onSurface": "rgba(28, 27, 31, 1)", - "onSurfaceDisabled": "rgba(28, 27, 31, 0.38)", + "onSecondaryFixed": "rgba(29, 25, 43, 1)", + "onSecondaryFixedVariant": "rgba(74, 68, 88, 1)", + "onSurface": "rgba(29, 27, 32, 1)", + "onSurfaceDisabled": "rgba(29, 27, 32, 0.38)", "onSurfaceVariant": "rgba(73, 69, 79, 1)", "onTertiary": "rgba(255, 255, 255, 1)", "onTertiaryContainer": "rgba(49, 17, 29, 1)", + "onTertiaryFixed": "rgba(49, 17, 29, 1)", + "onTertiaryFixedVariant": "rgba(99, 59, 72, 1)", "outline": "rgba(121, 116, 126, 1)", "outlineVariant": "rgba(202, 196, 208, 1)", "primary": "rgba(103, 80, 164, 1)", "primaryContainer": "rgba(234, 221, 255, 1)", + "primaryFixed": "rgba(234, 221, 255, 1)", + "primaryFixedDim": "rgba(208, 188, 255, 1)", "scrim": "rgba(0, 0, 0, 1)", "secondary": "rgba(98, 91, 113, 1)", "secondaryContainer": "rgba(232, 222, 248, 1)", + "secondaryFixed": "rgba(232, 222, 248, 1)", + "secondaryFixedDim": "rgba(204, 194, 220, 1)", "shadow": "rgba(0, 0, 0, 1)", - "surface": "rgba(255, 251, 254, 1)", - "surfaceDisabled": "rgba(28, 27, 31, 0.12)", + "surface": "rgba(254, 247, 255, 1)", + "surfaceBright": "rgba(254, 247, 255, 1)", + "surfaceContainer": "rgba(243, 237, 247, 1)", + "surfaceContainerHigh": "rgba(236, 230, 240, 1)", + "surfaceContainerHighest": "rgba(230, 224, 233, 1)", + "surfaceContainerLow": "rgba(247, 242, 250, 1)", + "surfaceContainerLowest": "rgba(255, 255, 255, 1)", + "surfaceDim": "rgba(222, 216, 225, 1)", + "surfaceDisabled": "rgba(29, 27, 32, 0.12)", "surfaceVariant": "rgba(231, 224, 236, 1)", "tertiary": "rgba(125, 82, 96, 1)", "tertiaryContainer": "rgba(255, 216, 228, 1)", + "tertiaryFixed": "rgba(255, 216, 228, 1)", + "tertiaryFixedDim": "rgba(239, 184, 200, 1)", }, "dark": false, "fonts": { @@ -182,7 +201,7 @@ exports[`renders list section with custom title style 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -344,7 +363,7 @@ exports[`renders list section with custom title style 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -357,7 +376,7 @@ exports[`renders list section with custom title style 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -497,7 +516,7 @@ exports[`renders list section with custom title style 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -510,7 +529,7 @@ exports[`renders list section with custom title style 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -542,45 +561,64 @@ exports[`renders list section with subheader 1`] = ` }, "colors": { "backdrop": "rgba(50, 47, 55, 0.4)", - "background": "rgba(255, 251, 254, 1)", + "background": "rgba(254, 247, 255, 1)", "elevation": { "level0": "transparent", - "level1": "rgb(247, 243, 249)", - "level2": "rgb(243, 237, 246)", - "level3": "rgb(238, 232, 244)", - "level4": "rgb(236, 230, 243)", - "level5": "rgb(233, 227, 241)", + "level1": "rgba(255, 255, 255, 1)", + "level2": "rgba(247, 242, 250, 1)", + "level3": "rgba(243, 237, 247, 1)", + "level4": "rgba(236, 230, 240, 1)", + "level5": "rgba(230, 224, 233, 1)", }, "error": "rgba(179, 38, 30, 1)", "errorContainer": "rgba(249, 222, 220, 1)", - "inverseOnSurface": "rgba(244, 239, 244, 1)", + "inverseOnSurface": "rgba(245, 239, 247, 1)", "inversePrimary": "rgba(208, 188, 255, 1)", - "inverseSurface": "rgba(49, 48, 51, 1)", - "onBackground": "rgba(28, 27, 31, 1)", + "inverseSurface": "rgba(50, 47, 53, 1)", + "onBackground": "rgba(29, 27, 32, 1)", "onError": "rgba(255, 255, 255, 1)", "onErrorContainer": "rgba(65, 14, 11, 1)", "onPrimary": "rgba(255, 255, 255, 1)", "onPrimaryContainer": "rgba(33, 0, 93, 1)", + "onPrimaryFixed": "rgba(33, 0, 93, 1)", + "onPrimaryFixedVariant": "rgba(79, 55, 139, 1)", "onSecondary": "rgba(255, 255, 255, 1)", "onSecondaryContainer": "rgba(29, 25, 43, 1)", - "onSurface": "rgba(28, 27, 31, 1)", - "onSurfaceDisabled": "rgba(28, 27, 31, 0.38)", + "onSecondaryFixed": "rgba(29, 25, 43, 1)", + "onSecondaryFixedVariant": "rgba(74, 68, 88, 1)", + "onSurface": "rgba(29, 27, 32, 1)", + "onSurfaceDisabled": "rgba(29, 27, 32, 0.38)", "onSurfaceVariant": "rgba(73, 69, 79, 1)", "onTertiary": "rgba(255, 255, 255, 1)", "onTertiaryContainer": "rgba(49, 17, 29, 1)", + "onTertiaryFixed": "rgba(49, 17, 29, 1)", + "onTertiaryFixedVariant": "rgba(99, 59, 72, 1)", "outline": "rgba(121, 116, 126, 1)", "outlineVariant": "rgba(202, 196, 208, 1)", "primary": "rgba(103, 80, 164, 1)", "primaryContainer": "rgba(234, 221, 255, 1)", + "primaryFixed": "rgba(234, 221, 255, 1)", + "primaryFixedDim": "rgba(208, 188, 255, 1)", "scrim": "rgba(0, 0, 0, 1)", "secondary": "rgba(98, 91, 113, 1)", "secondaryContainer": "rgba(232, 222, 248, 1)", + "secondaryFixed": "rgba(232, 222, 248, 1)", + "secondaryFixedDim": "rgba(204, 194, 220, 1)", "shadow": "rgba(0, 0, 0, 1)", - "surface": "rgba(255, 251, 254, 1)", - "surfaceDisabled": "rgba(28, 27, 31, 0.12)", + "surface": "rgba(254, 247, 255, 1)", + "surfaceBright": "rgba(254, 247, 255, 1)", + "surfaceContainer": "rgba(243, 237, 247, 1)", + "surfaceContainerHigh": "rgba(236, 230, 240, 1)", + "surfaceContainerHighest": "rgba(230, 224, 233, 1)", + "surfaceContainerLow": "rgba(247, 242, 250, 1)", + "surfaceContainerLowest": "rgba(255, 255, 255, 1)", + "surfaceDim": "rgba(222, 216, 225, 1)", + "surfaceDisabled": "rgba(29, 27, 32, 0.12)", "surfaceVariant": "rgba(231, 224, 236, 1)", "tertiary": "rgba(125, 82, 96, 1)", "tertiaryContainer": "rgba(255, 216, 228, 1)", + "tertiaryFixed": "rgba(255, 216, 228, 1)", + "tertiaryFixedDim": "rgba(239, 184, 200, 1)", }, "dark": false, "fonts": { @@ -707,7 +745,7 @@ exports[`renders list section with subheader 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -867,7 +905,7 @@ exports[`renders list section with subheader 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -880,7 +918,7 @@ exports[`renders list section with subheader 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -1020,7 +1058,7 @@ exports[`renders list section with subheader 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1033,7 +1071,7 @@ exports[`renders list section with subheader 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -1065,45 +1103,64 @@ exports[`renders list section without subheader 1`] = ` }, "colors": { "backdrop": "rgba(50, 47, 55, 0.4)", - "background": "rgba(255, 251, 254, 1)", + "background": "rgba(254, 247, 255, 1)", "elevation": { "level0": "transparent", - "level1": "rgb(247, 243, 249)", - "level2": "rgb(243, 237, 246)", - "level3": "rgb(238, 232, 244)", - "level4": "rgb(236, 230, 243)", - "level5": "rgb(233, 227, 241)", + "level1": "rgba(255, 255, 255, 1)", + "level2": "rgba(247, 242, 250, 1)", + "level3": "rgba(243, 237, 247, 1)", + "level4": "rgba(236, 230, 240, 1)", + "level5": "rgba(230, 224, 233, 1)", }, "error": "rgba(179, 38, 30, 1)", "errorContainer": "rgba(249, 222, 220, 1)", - "inverseOnSurface": "rgba(244, 239, 244, 1)", + "inverseOnSurface": "rgba(245, 239, 247, 1)", "inversePrimary": "rgba(208, 188, 255, 1)", - "inverseSurface": "rgba(49, 48, 51, 1)", - "onBackground": "rgba(28, 27, 31, 1)", + "inverseSurface": "rgba(50, 47, 53, 1)", + "onBackground": "rgba(29, 27, 32, 1)", "onError": "rgba(255, 255, 255, 1)", "onErrorContainer": "rgba(65, 14, 11, 1)", "onPrimary": "rgba(255, 255, 255, 1)", "onPrimaryContainer": "rgba(33, 0, 93, 1)", + "onPrimaryFixed": "rgba(33, 0, 93, 1)", + "onPrimaryFixedVariant": "rgba(79, 55, 139, 1)", "onSecondary": "rgba(255, 255, 255, 1)", "onSecondaryContainer": "rgba(29, 25, 43, 1)", - "onSurface": "rgba(28, 27, 31, 1)", - "onSurfaceDisabled": "rgba(28, 27, 31, 0.38)", + "onSecondaryFixed": "rgba(29, 25, 43, 1)", + "onSecondaryFixedVariant": "rgba(74, 68, 88, 1)", + "onSurface": "rgba(29, 27, 32, 1)", + "onSurfaceDisabled": "rgba(29, 27, 32, 0.38)", "onSurfaceVariant": "rgba(73, 69, 79, 1)", "onTertiary": "rgba(255, 255, 255, 1)", "onTertiaryContainer": "rgba(49, 17, 29, 1)", + "onTertiaryFixed": "rgba(49, 17, 29, 1)", + "onTertiaryFixedVariant": "rgba(99, 59, 72, 1)", "outline": "rgba(121, 116, 126, 1)", "outlineVariant": "rgba(202, 196, 208, 1)", "primary": "rgba(103, 80, 164, 1)", "primaryContainer": "rgba(234, 221, 255, 1)", + "primaryFixed": "rgba(234, 221, 255, 1)", + "primaryFixedDim": "rgba(208, 188, 255, 1)", "scrim": "rgba(0, 0, 0, 1)", "secondary": "rgba(98, 91, 113, 1)", "secondaryContainer": "rgba(232, 222, 248, 1)", + "secondaryFixed": "rgba(232, 222, 248, 1)", + "secondaryFixedDim": "rgba(204, 194, 220, 1)", "shadow": "rgba(0, 0, 0, 1)", - "surface": "rgba(255, 251, 254, 1)", - "surfaceDisabled": "rgba(28, 27, 31, 0.12)", + "surface": "rgba(254, 247, 255, 1)", + "surfaceBright": "rgba(254, 247, 255, 1)", + "surfaceContainer": "rgba(243, 237, 247, 1)", + "surfaceContainerHigh": "rgba(236, 230, 240, 1)", + "surfaceContainerHighest": "rgba(230, 224, 233, 1)", + "surfaceContainerLow": "rgba(247, 242, 250, 1)", + "surfaceContainerLowest": "rgba(255, 255, 255, 1)", + "surfaceDim": "rgba(222, 216, 225, 1)", + "surfaceDisabled": "rgba(29, 27, 32, 0.12)", "surfaceVariant": "rgba(231, 224, 236, 1)", "tertiary": "rgba(125, 82, 96, 1)", "tertiaryContainer": "rgba(255, 216, 228, 1)", + "tertiaryFixed": "rgba(255, 216, 228, 1)", + "tertiaryFixedDim": "rgba(239, 184, 200, 1)", }, "dark": false, "fonts": { @@ -1350,7 +1407,7 @@ exports[`renders list section without subheader 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1363,7 +1420,7 @@ exports[`renders list section without subheader 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], @@ -1503,7 +1560,7 @@ exports[`renders list section without subheader 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontWeight": "400", "letterSpacing": 0, @@ -1516,7 +1573,7 @@ exports[`renders list section without subheader 1`] = ` "fontSize": 16, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", }, undefined, ], diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index f2a20c8047..d3a7c1b1a1 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -117,7 +117,7 @@ exports[`renders menu with content styles 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -259,7 +259,7 @@ exports[`renders menu with content styles 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "borderRadius": 4, "borderTopLeftRadius": 0, "borderTopRightRadius": 0, @@ -288,7 +288,7 @@ exports[`renders menu with content styles 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "borderRadius": 4, "borderTopLeftRadius": 0, "borderTopRightRadius": 0, @@ -394,7 +394,7 @@ exports[`renders menu with content styles 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -407,7 +407,7 @@ exports[`renders menu with content styles 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -515,7 +515,7 @@ exports[`renders menu with content styles 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -528,7 +528,7 @@ exports[`renders menu with content styles 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -671,7 +671,7 @@ exports[`renders not visible menu 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -836,7 +836,7 @@ exports[`renders visible menu 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -978,7 +978,7 @@ exports[`renders visible menu 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "borderRadius": 4, "opacity": 0, "shadowColor": "#000", @@ -1005,7 +1005,7 @@ exports[`renders visible menu 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgb(243, 237, 246)", + "backgroundColor": "rgba(247, 242, 250, 1)", "borderRadius": 4, "flex": undefined, "paddingVertical": 8, @@ -1109,7 +1109,7 @@ exports[`renders visible menu 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1122,7 +1122,7 @@ exports[`renders visible menu 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -1230,7 +1230,7 @@ exports[`renders visible menu 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -1243,7 +1243,7 @@ exports[`renders visible menu 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", diff --git a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap index a34067f576..069b8c854d 100644 --- a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap @@ -131,7 +131,7 @@ exports[`Menu Item renders menu item 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -144,7 +144,7 @@ exports[`Menu Item renders menu item 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -292,7 +292,7 @@ exports[`Menu Item renders menu item 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -305,7 +305,7 @@ exports[`Menu Item renders menu item 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -403,7 +403,7 @@ exports[`Menu Item renders menu item 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontSize": 24, }, [ @@ -453,7 +453,7 @@ exports[`Menu Item renders menu item 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -466,7 +466,7 @@ exports[`Menu Item renders menu item 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -564,7 +564,7 @@ exports[`Menu Item renders menu item 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontSize": 24, }, [ @@ -614,7 +614,7 @@ exports[`Menu Item renders menu item 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -627,7 +627,7 @@ exports[`Menu Item renders menu item 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -735,7 +735,7 @@ exports[`Menu Item renders menu item 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -748,7 +748,7 @@ exports[`Menu Item renders menu item 1`] = ` }, [ { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap index f30e19f3f8..bbf1c420f7 100644 --- a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap @@ -5,7 +5,7 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -23,7 +23,7 @@ exports[`activity indicator snapshot test 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -176,7 +176,7 @@ exports[`activity indicator snapshot test 1`] = ` accessibilityRole="search" keyboardAppearance="light" placeholder="" - placeholderTextColor="rgba(28, 27, 31, 1)" + placeholderTextColor="rgba(29, 27, 32, 1)" returnKeyType="search" selectionColor="rgba(103, 80, 164, 1)" style={ @@ -415,7 +415,7 @@ exports[`renders with placeholder 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -433,7 +433,7 @@ exports[`renders with placeholder 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -586,7 +586,7 @@ exports[`renders with placeholder 1`] = ` accessibilityRole="search" keyboardAppearance="light" placeholder="Search" - placeholderTextColor="rgba(28, 27, 31, 1)" + placeholderTextColor="rgba(29, 27, 32, 1)" returnKeyType="search" selectionColor="rgba(103, 80, 164, 1)" style={ @@ -776,7 +776,7 @@ exports[`renders with text 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -794,7 +794,7 @@ exports[`renders with text 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgb(238, 232, 244)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -947,7 +947,7 @@ exports[`renders with text 1`] = ` accessibilityRole="search" keyboardAppearance="light" placeholder="Search" - placeholderTextColor="rgba(28, 27, 31, 1)" + placeholderTextColor="rgba(29, 27, 32, 1)" returnKeyType="search" selectionColor="rgba(103, 80, 164, 1)" style={ diff --git a/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap b/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap index 83f1541397..914c9d8c8a 100644 --- a/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap @@ -105,7 +105,7 @@ exports[`renders segmented button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -230,7 +230,7 @@ exports[`renders segmented button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -246,7 +246,7 @@ exports[`renders segmented button 1`] = ` "textAlign": "center", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500", diff --git a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap index b859166348..401868fabf 100644 --- a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap @@ -24,7 +24,7 @@ exports[`renders snackbar with Text as a child 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "margin": 8, "opacity": 0, @@ -50,7 +50,7 @@ exports[`renders snackbar with Text as a child 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "flex": undefined, "flexDirection": "row", @@ -112,7 +112,7 @@ exports[`renders snackbar with View & Text as a child 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "margin": 8, "opacity": 0, @@ -138,7 +138,7 @@ exports[`renders snackbar with View & Text as a child 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "flex": undefined, "flexDirection": "row", @@ -226,7 +226,7 @@ exports[`renders snackbar with action button 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "margin": 8, "opacity": 0, @@ -252,7 +252,7 @@ exports[`renders snackbar with action button 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "flex": undefined, "flexDirection": "row", @@ -276,7 +276,7 @@ exports[`renders snackbar with action button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -294,7 +294,7 @@ exports[`renders snackbar with action button 1`] = ` "marginVertical": 14, }, { - "color": "rgba(244, 239, 244, 1)", + "color": "rgba(245, 239, 247, 1)", }, ], ], @@ -423,7 +423,7 @@ exports[`renders snackbar with action button 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -494,7 +494,7 @@ exports[`renders snackbar with content 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "margin": 8, "opacity": 0, @@ -520,7 +520,7 @@ exports[`renders snackbar with content 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgba(49, 48, 51, 1)", + "backgroundColor": "rgba(50, 47, 53, 1)", "borderRadius": 4, "flex": undefined, "flexDirection": "row", @@ -544,7 +544,7 @@ exports[`renders snackbar with content 1`] = ` "textAlign": "left", }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "writingDirection": "ltr", }, [ @@ -562,7 +562,7 @@ exports[`renders snackbar with content 1`] = ` "marginVertical": 14, }, { - "color": "rgba(244, 239, 244, 1)", + "color": "rgba(245, 239, 247, 1)", }, ], ], diff --git a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap index 76ad8d4e54..090fdaec66 100644 --- a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap @@ -199,7 +199,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -513,7 +513,7 @@ exports[`correctly applies a component as the text label 1`] = ` "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -739,7 +739,7 @@ exports[`correctly applies cursorColor prop 1`] = ` "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -965,7 +965,7 @@ exports[`correctly applies default textAlign based on default RTL 1`] = ` "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -1009,7 +1009,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` }, false, { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "borderColor": "rgba(121, 116, 126, 1)", "borderRadius": 4, "borderWidth": 1, @@ -1086,7 +1086,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` numberOfLines={1} style={ { - "backgroundColor": "rgba(255, 251, 254, 1)", + "backgroundColor": "rgba(254, 247, 255, 1)", "color": "transparent", "fontFamily": "System", "fontSize": 16, @@ -1225,7 +1225,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -1450,7 +1450,7 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -1678,7 +1678,7 @@ exports[`correctly applies textAlign center 1`] = ` "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -1904,7 +1904,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, @@ -2323,7 +2323,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "paddingTop": 24, }, { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, diff --git a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap index 456c0402fa..0e475cd073 100644 --- a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap @@ -113,7 +113,7 @@ exports[`renders disabled toggle button 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontSize": 24, }, [ @@ -386,7 +386,7 @@ exports[`renders unchecked toggle button 1`] = ` style={ [ { - "color": "rgba(28, 27, 31, 0.38)", + "color": "rgba(29, 27, 32, 0.38)", "fontSize": 24, }, [ diff --git a/src/styles/themes/v3/DarkTheme.tsx b/src/styles/themes/v3/DarkTheme.tsx index 246925859c..d87404942a 100644 --- a/src/styles/themes/v3/DarkTheme.tsx +++ b/src/styles/themes/v3/DarkTheme.tsx @@ -17,13 +17,20 @@ export const MD3DarkTheme: MD3Theme = { secondaryContainer: palette.secondary30, tertiary: palette.tertiary80, tertiaryContainer: palette.tertiary30, - surface: palette.neutral10, + surface: palette.neutral6, + surfaceDim: palette.neutral6, + surfaceBright: palette.neutral24, + surfaceContainerLowest: palette.neutral4, + surfaceContainerLow: palette.neutral10, + surfaceContainer: palette.neutral12, + surfaceContainerHigh: palette.neutral17, + surfaceContainerHighest: palette.neutral22, surfaceVariant: palette.neutralVariant30, surfaceDisabled: color(palette.neutral90) .alpha(opacity.level2) .rgb() .string(), - background: palette.neutral10, + background: palette.neutral6, error: palette.error80, errorContainer: palette.error30, onPrimary: palette.primary20, @@ -46,19 +53,28 @@ export const MD3DarkTheme: MD3Theme = { inverseSurface: palette.neutral90, inverseOnSurface: palette.neutral20, inversePrimary: palette.primary40, + primaryFixed: palette.primary90, + primaryFixedDim: palette.primary80, + onPrimaryFixed: palette.primary10, + onPrimaryFixedVariant: palette.primary30, + secondaryFixed: palette.secondary90, + secondaryFixedDim: palette.secondary80, + onSecondaryFixed: palette.secondary10, + onSecondaryFixedVariant: palette.secondary30, + tertiaryFixed: palette.tertiary90, + tertiaryFixedDim: palette.tertiary80, + onTertiaryFixed: palette.tertiary10, + onTertiaryFixedVariant: palette.tertiary30, shadow: palette.neutral0, scrim: palette.neutral0, backdrop: color(MD3Colors.neutralVariant20).alpha(0.4).rgb().string(), elevation: { level0: 'transparent', - // Note: Color values with transparency cause RN to transfer shadows to children nodes - // instead of View component in Surface. Providing solid background fixes the issue. - // Opaque color values generated with `palette.primary80` used as background - level1: 'rgb(37, 35, 42)', // palette.primary80, alpha 0.05 - level2: 'rgb(44, 40, 49)', // palette.primary80, alpha 0.08 - level3: 'rgb(49, 44, 56)', // palette.primary80, alpha 0.11 - level4: 'rgb(51, 46, 58)', // palette.primary80, alpha 0.12 - level5: 'rgb(52, 49, 63)', // palette.primary80, alpha 0.14 + level1: palette.neutral4, + level2: palette.neutral10, + level3: palette.neutral12, + level4: palette.neutral17, + level5: palette.neutral22, }, }, }; diff --git a/src/styles/themes/v3/LightTheme.tsx b/src/styles/themes/v3/LightTheme.tsx index 19ec23fcfc..9782f3b90e 100644 --- a/src/styles/themes/v3/LightTheme.tsx +++ b/src/styles/themes/v3/LightTheme.tsx @@ -16,13 +16,20 @@ export const MD3LightTheme: MD3Theme = { secondaryContainer: palette.secondary90, tertiary: palette.tertiary40, tertiaryContainer: palette.tertiary90, - surface: palette.neutral99, + surface: palette.neutral98, + surfaceDim: palette.neutral87, + surfaceBright: palette.neutral98, + surfaceContainerLowest: palette.neutral100, + surfaceContainerLow: palette.neutral96, + surfaceContainer: palette.neutral94, + surfaceContainerHigh: palette.neutral92, + surfaceContainerHighest: palette.neutral90, surfaceVariant: palette.neutralVariant90, surfaceDisabled: color(palette.neutral10) .alpha(opacity.level2) .rgb() .string(), - background: palette.neutral99, + background: palette.neutral98, error: palette.error40, errorContainer: palette.error90, onPrimary: palette.primary100, @@ -45,19 +52,28 @@ export const MD3LightTheme: MD3Theme = { inverseSurface: palette.neutral20, inverseOnSurface: palette.neutral95, inversePrimary: palette.primary80, + primaryFixed: palette.primary90, + primaryFixedDim: palette.primary80, + onPrimaryFixed: palette.primary10, + onPrimaryFixedVariant: palette.primary30, + secondaryFixed: palette.secondary90, + secondaryFixedDim: palette.secondary80, + onSecondaryFixed: palette.secondary10, + onSecondaryFixedVariant: palette.secondary30, + tertiaryFixed: palette.tertiary90, + tertiaryFixedDim: palette.tertiary80, + onTertiaryFixed: palette.tertiary10, + onTertiaryFixedVariant: palette.tertiary30, shadow: palette.neutral0, scrim: palette.neutral0, backdrop: color(MD3Colors.neutralVariant20).alpha(0.4).rgb().string(), elevation: { level0: 'transparent', - // Note: Color values with transparency cause RN to transfer shadows to children nodes - // instead of View component in Surface. Providing solid background fixes the issue. - // Opaque color values generated with `palette.primary99` used as background - level1: 'rgb(247, 243, 249)', // palette.primary40, alpha 0.05 - level2: 'rgb(243, 237, 246)', // palette.primary40, alpha 0.08 - level3: 'rgb(238, 232, 244)', // palette.primary40, alpha 0.11 - level4: 'rgb(236, 230, 243)', // palette.primary40, alpha 0.12 - level5: 'rgb(233, 227, 241)', // palette.primary40, alpha 0.14 + level1: palette.neutral100, + level2: palette.neutral96, + level3: palette.neutral94, + level4: palette.neutral92, + level5: palette.neutral90, }, }, fonts: configureFonts(), diff --git a/src/styles/themes/v3/tokens.tsx b/src/styles/themes/v3/tokens.tsx index d008944d86..6eb27445fd 100644 --- a/src/styles/themes/v3/tokens.tsx +++ b/src/styles/themes/v3/tokens.tsx @@ -6,6 +6,7 @@ const ref = { palette: { primary100: 'rgba(255, 255, 255, 1)', primary99: 'rgba(255, 251, 254, 1)', + primary98: 'rgba(254, 247, 255, 1)', primary95: 'rgba(246, 237, 255, 1)', primary90: 'rgba(234, 221, 255, 1)', primary80: 'rgba(208, 188, 255, 1)', @@ -19,6 +20,7 @@ const ref = { primary0: 'rgba(0, 0, 0, 1)', secondary100: 'rgba(255, 255, 255, 1)', secondary99: 'rgba(255, 251, 254, 1)', + secondary98: 'rgba(254, 247, 255, 1)', secondary95: 'rgba(246, 237, 255, 1)', secondary90: 'rgba(232, 222, 248, 1)', secondary80: 'rgba(204, 194, 220, 1)', @@ -32,6 +34,7 @@ const ref = { secondary0: 'rgba(0, 0, 0, 1)', tertiary100: 'rgba(255, 255, 255, 1)', tertiary99: 'rgba(255, 251, 250, 1)', + tertiary98: 'rgba(255, 248, 248, 1)', tertiary95: 'rgba(255, 236, 241, 1)', tertiary90: 'rgba(255, 216, 228, 1)', tertiary80: 'rgba(239, 184, 200, 1)', @@ -44,20 +47,32 @@ const ref = { tertiary10: 'rgba(49, 17, 29, 1)', tertiary0: 'rgba(0, 0, 0, 1)', neutral100: 'rgba(255, 255, 255, 1)', - neutral99: 'rgba(255, 251, 254, 1)', - neutral95: 'rgba(244, 239, 244, 1)', - neutral90: 'rgba(230, 225, 229, 1)', - neutral80: 'rgba(201, 197, 202, 1)', - neutral70: 'rgba(174, 170, 174, 1)', - neutral60: 'rgba(147, 144, 148, 1)', - neutral50: 'rgba(120, 117, 121, 1)', - neutral40: 'rgba(96, 93, 98, 1)', - neutral30: 'rgba(72, 70, 73, 1)', - neutral20: 'rgba(49, 48, 51, 1)', - neutral10: 'rgba(28, 27, 31, 1)', + neutral99: 'rgba(255, 251, 255, 1)', + neutral98: 'rgba(254, 247, 255, 1)', + neutral96: 'rgba(247, 242, 250, 1)', + neutral95: 'rgba(245, 239, 247, 1)', + neutral94: 'rgba(243, 237, 247, 1)', + neutral92: 'rgba(236, 230, 240, 1)', + neutral90: 'rgba(230, 224, 233, 1)', + neutral87: 'rgba(222, 216, 225, 1)', + neutral80: 'rgba(202, 197, 205, 1)', + neutral70: 'rgba(174, 169, 177, 1)', + neutral60: 'rgba(147, 143, 150, 1)', + neutral50: 'rgba(121, 118, 125, 1)', + neutral40: 'rgba(96, 93, 100, 1)', + neutral30: 'rgba(72, 70, 76, 1)', + neutral24: 'rgba(59, 56, 62, 1)', + neutral22: 'rgba(54, 52, 59, 1)', + neutral20: 'rgba(50, 47, 53, 1)', + neutral17: 'rgba(43, 41, 48, 1)', + neutral12: 'rgba(33, 31, 38, 1)', + neutral10: 'rgba(29, 27, 32, 1)', + neutral6: 'rgba(20, 18, 24, 1)', + neutral4: 'rgba(15, 13, 19, 1)', neutral0: 'rgba(0, 0, 0, 1)', neutralVariant100: 'rgba(255, 255, 255, 1)', neutralVariant99: 'rgba(255, 251, 254, 1)', + neutralVariant98: 'rgba(253, 247, 255, 1)', neutralVariant95: 'rgba(245, 238, 250, 1)', neutralVariant90: 'rgba(231, 224, 236, 1)', neutralVariant80: 'rgba(202, 196, 208, 1)', @@ -71,6 +86,7 @@ const ref = { neutralVariant0: 'rgba(0, 0, 0, 1)', error100: 'rgba(255, 255, 255, 1)', error99: 'rgba(255, 251, 249, 1)', + error98: 'rgba(255, 248, 247, 1)', error95: 'rgba(252, 238, 238, 1)', error90: 'rgba(249, 222, 220, 1)', error80: 'rgba(242, 184, 181, 1)', diff --git a/src/types.tsx b/src/types.tsx index 25e8d4d5ba..b7a0317e80 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -36,6 +36,13 @@ export type MD3Colors = { tertiary: string; tertiaryContainer: string; surface: string; + surfaceDim: string; + surfaceBright: string; + surfaceContainerLowest: string; + surfaceContainerLow: string; + surfaceContainer: string; + surfaceContainerHigh: string; + surfaceContainerHighest: string; surfaceVariant: string; surfaceDisabled: string; background: string; @@ -58,6 +65,18 @@ export type MD3Colors = { inverseSurface: string; inverseOnSurface: string; inversePrimary: string; + primaryFixed: string; + primaryFixedDim: string; + onPrimaryFixed: string; + onPrimaryFixedVariant: string; + secondaryFixed: string; + secondaryFixedDim: string; + onSecondaryFixed: string; + onSecondaryFixedVariant: string; + tertiaryFixed: string; + tertiaryFixedDim: string; + onTertiaryFixed: string; + onTertiaryFixedVariant: string; shadow: string; scrim: string; backdrop: string; From ad9f3f00499ffc7b926069c7107909cebac6be31 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Wed, 29 Apr 2026 15:50:59 +0300 Subject: [PATCH 05/94] feat: add DynamicTheme using PlatformColor (#4901) * chore: remove md2 support * fix: update colors as per material-components color tokens * see https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/color/res/values/tokens.xml * fix: update the color schema for LightTheme and DarkTheme * see: https://m3.material.io/styles/color/roles * feat: add DynamicTheme * Known limitation: surface/container roles on API 31-33 use * @color/m3_ref_palette_dynamic_neutral_variant* (MCL resources that require a * native DynamicColors setup). No @android:color/ equivalent exists for those * slots. Reference palette values are used as fallback on API 31-33. * refactor: remove usage of expo-material3-theme * refactor: rename "device colors" to "Dynamic Theme" * feat: update Colors and stateOpacity tokens for PlatformColor compatibility * surfaceDisabled, onSurfaceDisabled and backdrop which were not part of the MD standard were removed * the reference theme elevation colors now use the correct neutral tones; see https://m3.material.io/blog/tone-based-surface-color-m3 * replace usage of "alpha" with opacity or surface colors * remove disabled FAB prop --- docs/docs/guides/02-theming.mdx | 23 +- docs/docs/guides/06-recommended-libraries.md | 6 +- example/package.json | 1 - example/src/DrawerItems.tsx | 16 +- example/src/Examples/AppbarExample.tsx | 2 +- example/src/Examples/FABExample.tsx | 8 - example/src/PreferencesContext.tsx | 4 +- example/src/index.native.tsx | 35 +- example/src/index.tsx | 4 +- example/utils/index.ts | 7 +- src/components/Appbar/Appbar.tsx | 2 +- src/components/Appbar/utils.ts | 2 +- .../BottomNavigation/BottomNavigationBar.tsx | 2 +- src/components/Button/Button.tsx | 41 +- src/components/Button/utils.tsx | 36 +- src/components/Checkbox/CheckboxAndroid.tsx | 10 +- src/components/Checkbox/CheckboxIOS.tsx | 3 +- src/components/Checkbox/CheckboxItem.tsx | 7 +- src/components/Checkbox/utils.ts | 56 +- src/components/Chip/Chip.tsx | 12 +- src/components/Chip/helpers.tsx | 52 +- .../DataTable/DataTablePagination.tsx | 3 +- src/components/DataTable/DataTableTitle.tsx | 4 +- src/components/Dialog/Dialog.tsx | 4 +- src/components/FAB/AnimatedFAB.tsx | 19 +- src/components/FAB/FAB.tsx | 13 +- src/components/FAB/FABGroup.tsx | 12 +- src/components/FAB/utils.ts | 41 +- src/components/HelperText/HelperText.tsx | 63 +- src/components/HelperText/utils.ts | 18 +- src/components/IconButton/IconButton.tsx | 32 +- src/components/IconButton/utils.ts | 42 +- src/components/Menu/MenuItem.tsx | 4 +- src/components/Menu/utils.ts | 32 +- src/components/Modal.tsx | 10 +- .../RadioButton/RadioButtonAndroid.tsx | 16 +- src/components/RadioButton/RadioButtonIOS.tsx | 3 +- .../RadioButton/RadioButtonItem.tsx | 7 +- src/components/Searchbar.tsx | 4 +- .../SegmentedButtons/SegmentedButtonItem.tsx | 6 +- src/components/SegmentedButtons/utils.ts | 11 +- src/components/Snackbar.tsx | 2 +- src/components/Switch/utils.ts | 2 +- src/components/TextInput/Addons/Underline.tsx | 3 + .../TextInput/Adornment/TextInputAffix.tsx | 11 +- .../TextInput/Adornment/TextInputIcon.tsx | 4 +- src/components/TextInput/Adornment/utils.ts | 29 +- src/components/TextInput/Label/InputLabel.tsx | 10 +- src/components/TextInput/TextInputFlat.tsx | 4 + .../TextInput/TextInputOutlined.tsx | 3 + src/components/TextInput/helpers.tsx | 47 +- src/components/TextInput/types.tsx | 1 + src/components/ToggleButton/utils.ts | 10 +- src/components/TouchableRipple/utils.ts | 2 +- .../__tests__/Appbar/Appbar.test.tsx | 15 - .../Appbar/__snapshots__/Appbar.test.tsx.snap | 344 ++++---- src/components/__tests__/Button.test.tsx | 27 +- .../__snapshots__/CheckboxItem.test.tsx.snap | 5 + .../__tests__/Checkbox/utils.test.tsx | 85 +- src/components/__tests__/Chip.test.tsx | 124 +-- src/components/__tests__/FAB.test.tsx | 36 +- src/components/__tests__/FABGroup.test.tsx | 31 +- src/components/__tests__/IconButton.test.tsx | 15 +- src/components/__tests__/MenuItem.test.tsx | 13 +- src/components/__tests__/Modal.test.tsx | 33 +- .../RadioButtonItem.test.tsx.snap | 4 + .../__tests__/SegmentedButton.test.tsx | 12 +- src/components/__tests__/Surface.test.tsx | 4 +- src/components/__tests__/Switch.test.tsx | 2 +- src/components/__tests__/TextInput.test.tsx | 40 +- .../__tests__/ToggleButton.test.tsx | 16 +- .../__snapshots__/Banner.test.tsx.snap | 36 +- .../BottomNavigation.test.tsx.snap | 22 +- .../__snapshots__/Button.test.tsx.snap | 43 +- .../__snapshots__/Chip.test.tsx.snap | 26 +- .../__snapshots__/DataTable.test.tsx.snap | 741 ++++++++++-------- .../__tests__/__snapshots__/FAB.test.tsx.snap | 174 ---- .../__snapshots__/IconButton.test.tsx.snap | 346 ++++---- .../__snapshots__/ListItem.test.tsx.snap | 3 + .../__snapshots__/ListSection.test.tsx.snap | 30 +- .../__snapshots__/Menu.test.tsx.snap | 35 +- .../__snapshots__/MenuItem.test.tsx.snap | 23 +- .../__snapshots__/Searchbar.test.tsx.snap | 312 ++++---- .../SegmentedButton.test.tsx.snap | 2 + .../__snapshots__/Snackbar.test.tsx.snap | 3 + .../__snapshots__/TextInput.test.tsx.snap | 173 ++-- .../__snapshots__/ToggleButton.test.tsx.snap | 186 +++-- src/core/__tests__/theming.test.tsx | 16 +- src/core/theming.tsx | 26 +- src/styles/themes/DynamicTheme.android.tsx | 502 ++++++++++++ src/styles/themes/DynamicTheme.tsx | 2 + src/styles/themes/index.ts | 1 + src/styles/themes/v3/DarkTheme.tsx | 23 +- src/styles/themes/v3/LightTheme.tsx | 23 +- src/styles/themes/v3/tokens.tsx | 17 +- src/types.tsx | 43 +- yarn.lock | 22 - 97 files changed, 2513 insertions(+), 1924 deletions(-) create mode 100644 src/styles/themes/DynamicTheme.android.tsx create mode 100644 src/styles/themes/DynamicTheme.tsx diff --git a/docs/docs/guides/02-theming.mdx b/docs/docs/guides/02-theming.mdx index fe1e445a83..3b24daf8d4 100644 --- a/docs/docs/guides/02-theming.mdx +++ b/docs/docs/guides/02-theming.mdx @@ -227,31 +227,26 @@ export default function Main() { ### Sync dynamic colors with system colors -Using [`pchmn/expo-material3-theme`](https://github.com/pchmn/expo-material3-theme) library you can easily access the Material 3 system colors from Android 12+ devices and seamlessly integrate them into your dynamic theme. Any changes made by the user to the system colors will be automatically reflected in the theme. +React Native Paper provides built-in support for Android dynamic colors through `DynamicLightTheme` and `DynamicDarkTheme`. These themes use React Native's `PlatformColor` API to map all Material Design 3 color roles to Android system colors, so any changes the user makes to their system palette are automatically reflected in the app. :::info -In case of incompatible devices, the library will revert to a default theme. +Dynamic colors require Android 12 (API 31+). On older Android versions and all other platforms, these themes fall back to the default `LightTheme` / `DarkTheme`. ::: -To get started, follow the [installation instructions](https://github.com/pchmn/expo-material3-theme#installation) and check the following code: - ```tsx -import { useMaterial3Theme } from '@pchmn/expo-material3-theme'; import { useColorScheme } from 'react-native'; -import { MD3DarkTheme, MD3LightTheme, PaperProvider } from 'react-native-paper'; +import { + DynamicDarkTheme, + DynamicLightTheme, + PaperProvider, +} from 'react-native-paper'; import App from './src/App'; export default function Main() { - const colorScheme = useColorScheme(); - const { theme } = useMaterial3Theme(); - - const paperTheme = - colorScheme === 'dark' - ? { ...MD3DarkTheme, colors: theme.dark } - : { ...MD3LightTheme, colors: theme.light }; + const isDarkMode = useColorScheme() === 'dark'; return ( - + ); diff --git a/docs/docs/guides/06-recommended-libraries.md b/docs/docs/guides/06-recommended-libraries.md index e70157ca1d..7be319cedf 100644 --- a/docs/docs/guides/06-recommended-libraries.md +++ b/docs/docs/guides/06-recommended-libraries.md @@ -33,8 +33,4 @@ Material Design themed [date picker](https://material.io/components/date-pickers ## Time Picker [web-ridge/react-native-paper-dates](https://github.com/web-ridge/react-native-paper-dates) -Material Design themed [time picker](https://material.io/components/time-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) - -## System Colors -[pchmn/expo-material3-theme](https://github.com/pchmn/expo-material3-theme) -Retrieve Material 3 system colors from Android 12+ devices \ No newline at end of file +Material Design themed [time picker](https://material.io/components/time-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) diff --git a/example/package.json b/example/package.json index 667c56cb19..3ae36fb1ec 100644 --- a/example/package.json +++ b/example/package.json @@ -16,7 +16,6 @@ "dependencies": { "@expo/vector-icons": "^15.0.2", "@expo/webpack-config": "~19.0.1", - "@pchmn/expo-material3-theme": "^1.3.2", "@react-native-async-storage/async-storage": "2.2.0", "@react-native-masked-view/masked-view": "0.3.2", "@react-navigation/bottom-tabs": "^7.3.10", diff --git a/example/src/DrawerItems.tsx b/example/src/DrawerItems.tsx index c4a37a43f3..7974fd6678 100644 --- a/example/src/DrawerItems.tsx +++ b/example/src/DrawerItems.tsx @@ -16,7 +16,7 @@ import { Portal, } from 'react-native-paper'; -import { deviceColorsSupported, isWeb } from '../utils'; +import { dynamicThemeSupported, isWeb } from '../utils'; import { useExampleTheme } from './hooks/useExampleTheme'; import { PreferencesContext } from './PreferencesContext'; @@ -103,7 +103,7 @@ function DrawerItems() { if (!preferences) throw new Error('PreferencesContext not provided'); const { - toggleShouldUseDeviceColors, + toggleShouldUseDynamicTheme, toggleTheme, toggleRtl: toggleRTL, toggleCollapsed, @@ -114,7 +114,7 @@ function DrawerItems() { collapsed, rtl: isRTL, theme: { dark: isDarkTheme }, - shouldUseDeviceColors, + shouldUseDynamicTheme, } = preferences; const _handleToggleRTL = () => { @@ -181,12 +181,12 @@ function DrawerItems() { - {deviceColorsSupported ? ( - - - Use device colors * + {dynamicThemeSupported ? ( + + + Use Dynamic Theme - + diff --git a/example/src/Examples/AppbarExample.tsx b/example/src/Examples/AppbarExample.tsx index 9bad92e536..3425254732 100644 --- a/example/src/Examples/AppbarExample.tsx +++ b/example/src/Examples/AppbarExample.tsx @@ -182,7 +182,7 @@ const AppbarExample = ({ navigation }: Props) => { height: height + bottom, }, { - backgroundColor: theme.colors.elevation.level2, + backgroundColor: theme.colors.surfaceContainerHigh, }, ]} safeAreaInsets={{ bottom, left, right }} diff --git a/example/src/Examples/FABExample.tsx b/example/src/Examples/FABExample.tsx index e7db1118a7..1ac542b371 100644 --- a/example/src/Examples/FABExample.tsx +++ b/example/src/Examples/FABExample.tsx @@ -95,14 +95,6 @@ const FABExample = () => { onPress={() => {}} visible={visible} /> - {}} - visible={visible} - disabled - /> void; toggleCustomFont: () => void; toggleRippleEffect: () => void; - toggleShouldUseDeviceColors?: () => void; + toggleShouldUseDynamicTheme?: () => void; theme: MD3Theme; rtl: boolean; collapsed: boolean; customFontLoaded: boolean; rippleEffectEnabled: boolean; - shouldUseDeviceColors?: boolean; + shouldUseDynamicTheme?: boolean; } | null>(null); diff --git a/example/src/index.native.tsx b/example/src/index.native.tsx index 0d5be506e7..821386cc83 100644 --- a/example/src/index.native.tsx +++ b/example/src/index.native.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { I18nManager } from 'react-native'; -import { useMaterial3Theme } from '@pchmn/expo-material3-theme'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { createDrawerNavigator } from '@react-navigation/drawer'; import { InitialState, NavigationContainer } from '@react-navigation/native'; @@ -9,13 +8,19 @@ import { useFonts } from 'expo-font'; import { useKeepAwake } from 'expo-keep-awake'; import { StatusBar } from 'expo-status-bar'; import * as Updates from 'expo-updates'; -import { PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper'; +import { + PaperProvider, + MD3DarkTheme, + MD3LightTheme, + DynamicLightTheme, + DynamicDarkTheme, +} from 'react-native-paper'; import { SafeAreaInsetsContext } from 'react-native-safe-area-context'; import DrawerItems from './DrawerItems'; import { PreferencesContext } from './PreferencesContext'; import App from './RootNavigator'; -import { deviceColorsSupported } from '../utils'; +import { dynamicThemeSupported } from '../utils'; import { CombinedDefaultTheme, CombinedDarkTheme, @@ -40,7 +45,7 @@ export default function PaperExample() { InitialState | undefined >(); - const [shouldUseDeviceColors, setShouldUseDeviceColors] = + const [shouldUseDynamicTheme, setShouldUseDynamicTheme] = React.useState(true); const [isDarkMode, setIsDarkMode] = React.useState(false); const [rtl, setRtl] = React.useState( @@ -50,19 +55,13 @@ export default function PaperExample() { const [customFontLoaded, setCustomFont] = React.useState(false); const [rippleEffectEnabled, setRippleEffectEnabled] = React.useState(true); - const { theme: mdTheme } = useMaterial3Theme(); const theme = React.useMemo(() => { - if (!deviceColorsSupported || !shouldUseDeviceColors) { - return isDarkMode ? MD3DarkTheme : MD3LightTheme; + if (dynamicThemeSupported && shouldUseDynamicTheme) { + return isDarkMode ? DynamicDarkTheme : DynamicLightTheme; } - return isDarkMode - ? { ...MD3DarkTheme, colors: { ...MD3DarkTheme.colors, ...mdTheme.dark } } - : { - ...MD3LightTheme, - colors: { ...MD3LightTheme.colors, ...mdTheme.light }, - }; - }, [isDarkMode, mdTheme, shouldUseDeviceColors]); + return isDarkMode ? MD3DarkTheme : MD3LightTheme; + }, [isDarkMode, shouldUseDynamicTheme]); React.useEffect(() => { const restoreState = async () => { @@ -129,8 +128,8 @@ export default function PaperExample() { const preferences = React.useMemo( () => ({ - toggleShouldUseDeviceColors: () => - setShouldUseDeviceColors((oldValue) => !oldValue), + toggleShouldUseDynamicTheme: () => + setShouldUseDynamicTheme((oldValue) => !oldValue), toggleTheme: () => setIsDarkMode((oldValue) => !oldValue), toggleRtl: () => setRtl((rtl) => !rtl), toggleCollapsed: () => setCollapsed(!collapsed), @@ -141,14 +140,14 @@ export default function PaperExample() { collapsed, rtl, theme, - shouldUseDeviceColors, + shouldUseDynamicTheme: shouldUseDynamicTheme, }), [ rtl, theme, collapsed, customFontLoaded, - shouldUseDeviceColors, + shouldUseDynamicTheme, rippleEffectEnabled, ] ); diff --git a/example/src/index.tsx b/example/src/index.tsx index 4395d2d55e..a6ee5ccc08 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -111,9 +111,9 @@ export default function PaperExample() { rippleEffectEnabled, // noop for web, specified to avoid type errors toggleRtl: noop, - toggleShouldUseDeviceColors: noop, + toggleShouldUseDynamicTheme: noop, rtl: false, - shouldUseDeviceColors: false, + shouldUseDynamicTheme: false, }), [theme, collapsed, customFontLoaded, rippleEffectEnabled] ); diff --git a/example/utils/index.ts b/example/utils/index.ts index f86f3a0307..d0856f19a5 100644 --- a/example/utils/index.ts +++ b/example/utils/index.ts @@ -1,6 +1,5 @@ import { Platform } from 'react-native'; -import ExpoMaterial3ThemeModule from '@pchmn/expo-material3-theme/build/ExpoMaterial3ThemeModule'; import { MD3DarkTheme, MD3LightTheme, MD3Theme } from 'react-native-paper'; type ReducerAction = { @@ -1420,7 +1419,5 @@ export const restaurantsData = [ }, ]; -export const deviceColorsSupported = - Boolean(ExpoMaterial3ThemeModule) && - Platform.OS === 'android' && - Platform.Version >= 31; +export const dynamicThemeSupported = + Platform.OS === 'android' && (Platform.Version as number) >= 31; diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index 4934ef7bf6..707f5c15c2 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -110,7 +110,7 @@ export type Props = Omit< * styles.bottom, * { * height: BOTTOM_APPBAR_HEIGHT + bottom, - * backgroundColor: theme.colors.elevation.level2, + * backgroundColor: theme.colors.surfaceContainer, * }, * ]} * safeAreaInsets={{ bottom }} diff --git a/src/components/Appbar/utils.ts b/src/components/Appbar/utils.ts index fbf4fe438e..893326dead 100644 --- a/src/components/Appbar/utils.ts +++ b/src/components/Appbar/utils.ts @@ -33,7 +33,7 @@ export const getAppbarBackgroundColor = ( } if (elevated) { - return colors.elevation.level2; + return colors.surfaceContainer; } return colors.surface; diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index 0791a8fa3b..696ec8fe83 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -445,7 +445,7 @@ const BottomNavigationBar = ({ backgroundColor?: ColorValue; }; - const backgroundColor = customBackground || colors.elevation.level2; + const backgroundColor = customBackground || colors.surfaceContainer; const activeTintColor = getActiveTintColor({ activeColor, diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 516ff915f7..9f0b3a98dc 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -274,15 +274,21 @@ const Button = ( const borderRadius = 5 * roundness; const iconSize = 18; - const { backgroundColor, borderColor, textColor, borderWidth } = - getButtonColors({ - customButtonColor, - customTextColor, - theme, - mode, - disabled, - dark, - }); + const { + backgroundColor, + borderColor, + textColor, + textOpacity, + borderWidth, + backgroundOpacity, + } = getButtonColors({ + customButtonColor, + customTextColor, + theme, + mode, + disabled, + dark, + }); const touchableStyle = { ...borderRadiusStyles, @@ -290,7 +296,7 @@ const Button = ( }; const buttonStyle = { - backgroundColor, + backgroundColor: backgroundOpacity < 1 ? 'transparent' : backgroundColor, borderColor, borderWidth, ...touchableStyle, @@ -337,6 +343,19 @@ const Button = ( elevation={elevation} container > + {backgroundOpacity < 1 && ( + + )} - + {icon && loading !== true ? ( { - const { colors } = theme as MD3Theme; - if (disabled && isMode('outlined')) { - return colors.surfaceDisabled; - } - +const getButtonBorderColor = ({ isMode, theme }: BaseProps) => { if (isMode('outlined')) { - return colors.outline; + return theme.colors.outlineVariant; } return 'transparent'; @@ -179,15 +170,24 @@ export const getButtonColors = ({ dark, }); - const borderColor = getButtonBorderColor({ isMode, theme, disabled }); + const borderColor = getButtonBorderColor({ isMode, theme }); const borderWidth = getButtonBorderWidth({ isMode, theme }); + const textOpacity = disabled ? stateOpacity.disabled : stateOpacity.enabled; + + const backgroundOpacity = + disabled && !isMode('outlined') && !isMode('text') + ? stateOpacity.pressed + : stateOpacity.enabled; + return { backgroundColor, borderColor, textColor, + textOpacity, borderWidth, + backgroundOpacity, }; }; diff --git a/src/components/Checkbox/CheckboxAndroid.tsx b/src/components/Checkbox/CheckboxAndroid.tsx index 642602b0fa..a4e6fb0c23 100644 --- a/src/components/Checkbox/CheckboxAndroid.tsx +++ b/src/components/Checkbox/CheckboxAndroid.tsx @@ -99,7 +99,7 @@ const CheckboxAndroid = ({ const checked = status === 'checked'; const indeterminate = status === 'indeterminate'; - const { rippleColor, selectionControlColor } = + const { selectionControlColor, selectionControlOpacity } = getAndroidSelectionControlColor({ theme, disabled, @@ -123,7 +123,6 @@ const CheckboxAndroid = ({ - + { - if (disabled) { - return color(theme.colors.onSurface).alpha(0.16).rgb().string(); - } - - return color(checkedColor).fade(0.32).rgb().string(); -}; - const getAndroidControlColor = ({ theme, checked, @@ -60,7 +45,7 @@ const getAndroidControlColor = ({ disabled?: boolean; }) => { if (disabled) { - return theme.colors.onSurfaceDisabled; + return theme.colors.onSurface; } if (checked) { @@ -87,8 +72,11 @@ export const getAndroidSelectionControlColor = ({ theme, customUncheckedColor, }); + const selectionControlOpacity = disabled + ? stateOpacity.disabled + : stateOpacity.enabled; + return { - rippleColor: getAndroidRippleColor({ theme, checkedColor, disabled }), selectionControlColor: getAndroidControlColor({ theme, disabled, @@ -96,6 +84,7 @@ export const getAndroidSelectionControlColor = ({ checkedColor, uncheckedColor, }), + selectionControlOpacity, }; }; @@ -109,7 +98,7 @@ const getIOSCheckedColor = ({ disabled?: boolean; }) => { if (disabled) { - return theme.colors.onSurfaceDisabled; + return theme.colors.primary; } if (customColor) { @@ -119,21 +108,6 @@ const getIOSCheckedColor = ({ return theme.colors.primary; }; -const getIOSRippleColor = ({ - theme, - checkedColor, - disabled, -}: { - theme: InternalTheme; - checkedColor: string; - disabled?: boolean; -}) => { - if (disabled) { - return color(theme.colors.onSurface).alpha(0.16).rgb().string(); - } - return color(checkedColor).fade(0.32).rgb().string(); -}; - export const getSelectionControlIOSColor = ({ theme, disabled, @@ -144,12 +118,12 @@ export const getSelectionControlIOSColor = ({ customColor?: string; }) => { const checkedColor = getIOSCheckedColor({ theme, disabled, customColor }); + const checkedColorOpacity = disabled + ? stateOpacity.disabled + : stateOpacity.enabled; + return { checkedColor, - rippleColor: getIOSRippleColor({ - theme, - checkedColor, - disabled, - }), + checkedColorOpacity, }; }; diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 012cac4fc1..2c79e8201d 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -196,7 +196,6 @@ const Chip = ({ theme: themeOverrides, testID = 'chip', selectedColor, - showSelectedOverlay = false, showSelectedCheck = true, ellipsizeMode, compact, @@ -257,13 +256,13 @@ const Chip = ({ borderColor, textColor, iconColor, + contentOpacity, selectedBackgroundColor, backgroundColor, } = getChipColors({ isOutlined, theme, selectedColor, - showSelectedOverlay, customBackgroundColor, disabled, }); @@ -324,7 +323,14 @@ const Chip = ({ theme={theme} hitSlop={hitSlop} > - + {avatar && !icon ? ( theme as MD3Theme; +const { stateOpacity } = tokens.md.ref; + export type ChipAvatarProps = { style?: StyleProp; }; @@ -21,7 +24,6 @@ const getBorderColor = ({ isOutlined, disabled, selectedColor, - backgroundColor: _backgroundColor, }: BaseProps & { backgroundColor: string; selectedColor?: string }) => { const isSelectedColor = selectedColor !== undefined; const { colors } = md3(theme); @@ -32,14 +34,14 @@ const getBorderColor = ({ } if (disabled) { - return color(colors.onSurfaceVariant).alpha(0.12).rgb().string(); + return colors.surfaceContainer; } if (isSelectedColor) { return color(selectedColor).alpha(0.29).rgb().string(); } - return colors.outline; + return colors.outlineVariant; }; const getTextColor = ({ @@ -53,7 +55,7 @@ const getTextColor = ({ const isSelectedColor = selectedColor !== undefined; const { colors } = md3(theme); if (disabled) { - return colors.onSurfaceDisabled; + return colors.onSurface; } if (isSelectedColor) { @@ -96,7 +98,7 @@ const getBackgroundColor = ({ if (isOutlined) { return 'transparent'; } - return color(colors.onSurfaceVariant).alpha(0.12).rgb().string(); + return colors.surfaceContainerLow; } return getDefaultBackgroundColor({ theme, isOutlined }); @@ -107,43 +109,15 @@ const getSelectedBackgroundColor = ({ isOutlined, disabled, customBackgroundColor, - showSelectedOverlay, }: BaseProps & { customBackgroundColor?: ColorValue; - showSelectedOverlay?: boolean; }) => { - const { colors } = md3(theme); - const backgroundColor = getBackgroundColor({ + return getBackgroundColor({ theme, disabled, isOutlined, customBackgroundColor, }); - - if (isOutlined) { - if (showSelectedOverlay) { - return color(backgroundColor) - .mix(color(colors.onSurfaceVariant), 0.12) - .rgb() - .string(); - } - return color(backgroundColor) - .mix(color(colors.onSurfaceVariant), 0) - .rgb() - .string(); - } - - if (showSelectedOverlay) { - return color(backgroundColor) - .mix(color(colors.onSecondaryContainer), 0.12) - .rgb() - .string(); - } - - return color(backgroundColor) - .mix(color(colors.onSecondaryContainer), 0) - .rgb() - .string(); }; const getIconColor = ({ @@ -157,7 +131,7 @@ const getIconColor = ({ const isSelectedColor = selectedColor !== undefined; const { colors } = md3(theme); if (disabled) { - return colors.onSurfaceDisabled; + return colors.onSurface; } if (isSelectedColor) { @@ -175,13 +149,11 @@ export const getChipColors = ({ isOutlined, theme, selectedColor, - showSelectedOverlay, customBackgroundColor, disabled, }: BaseProps & { customBackgroundColor?: ColorValue; disabled?: boolean; - showSelectedOverlay?: boolean; selectedColor?: string; }) => { const baseChipColorProps = { theme, isOutlined, disabled }; @@ -194,9 +166,12 @@ export const getChipColors = ({ const selectedBackgroundColor = getSelectedBackgroundColor({ ...baseChipColorProps, customBackgroundColor, - showSelectedOverlay, }); + const contentOpacity = disabled + ? stateOpacity.disabled + : stateOpacity.enabled; + return { borderColor: getBorderColor({ ...baseChipColorProps, @@ -211,6 +186,7 @@ export const getChipColors = ({ ...baseChipColorProps, selectedColor, }), + contentOpacity, backgroundColor, selectedBackgroundColor, }; diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 6e2612973e..6a3fc52b3d 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -7,7 +7,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; @@ -286,7 +285,7 @@ const DataTablePagination = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const labelColor = color(theme.colors.onSurface).alpha(0.6).rgb().string(); + const labelColor = theme.colors.onSurfaceVariant; return ( { const { right, left } = useSafeAreaInsets(); const theme = useInternalTheme(themeOverrides); - const { roundness, colors } = theme as MD3Theme; + const { roundness } = theme as MD3Theme; const borderRadius = 7 * roundness; - const backgroundColor = colors.elevation.level3; + const backgroundColor = theme.colors.surfaceContainerHigh; return ( , 'mode'> & { * Custom color for the icon and label of the `FAB`. */ color?: string; - /** - * Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch. - */ - disabled?: boolean; /** * Whether `FAB` is currently visible. */ @@ -206,7 +202,6 @@ const AnimatedFAB = ({ accessibilityLabel = label, accessibilityState, color: customColor, - disabled, onPress, onLongPress, delayLongPress, @@ -300,7 +295,6 @@ const AnimatedFAB = ({ const { backgroundColor, foregroundColor } = getFABColors({ theme, variant, - disabled, customColor, customBackgroundColor, }); @@ -359,15 +353,12 @@ const AnimatedFAB = ({ ...font, }; - const md3Elevation = disabled || !isIOS ? 0 : 3; + const md3Elevation = !isIOS ? 0 : 3; const shadowStyle = styles.v3Shadow; - const baseStyle = [ - StyleSheet.absoluteFill, - disabled ? styles.disabled : shadowStyle, - ]; + const baseStyle = [StyleSheet.absoluteFill, shadowStyle]; - const newAccessibilityState = { ...accessibilityState, disabled }; + const newAccessibilityState = { ...accessibilityState }; return ( , 'mode'> & { * Custom color for the icon and label of the `FAB`. */ color?: string; - /** - * Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch. - */ - disabled?: boolean; /** * Whether `FAB` is currently visible. */ @@ -184,7 +180,6 @@ const FAB = forwardRef( accessibilityState, animated = true, color: customColor, - disabled, onPress, onLongPress, delayLongPress, @@ -239,7 +234,6 @@ const FAB = forwardRef( const { backgroundColor, foregroundColor } = getFABColors({ theme, variant, - disabled, customColor, customBackgroundColor, }); @@ -256,9 +250,7 @@ const FAB = forwardRef( ...font, }; - const md3Elevation = isFlatMode || disabled ? 0 : 3; - - const newAccessibilityState = { ...accessibilityState, disabled }; + const md3Elevation = isFlatMode ? 0 : 3; return ( ( onPress={onPress} onLongPress={onLongPress} delayLongPress={delayLongPress} - disabled={disabled} accessibilityLabel={accessibilityLabel} accessibilityRole="button" - accessibilityState={newAccessibilityState} + accessibilityState={accessibilityState} testID={testID} style={{ borderRadius }} {...rest} diff --git a/src/components/FAB/FABGroup.tsx b/src/components/FAB/FABGroup.tsx index 4b9d9f1cd4..1123da16ae 100644 --- a/src/components/FAB/FABGroup.tsx +++ b/src/components/FAB/FABGroup.tsx @@ -306,15 +306,19 @@ const FABGroup = ({ } }; - const { labelColor, backdropColor, stackedFABBackgroundColor } = - getFABGroupColors({ theme, customBackdropColor }); + const { + labelColor, + backdropColor, + backdropOpacity: backdropMaxOpacity, + stackedFABBackgroundColor, + } = getFABGroupColors({ theme, customBackdropColor }); const backdropOpacity = open ? backdrop.interpolate({ inputRange: [0, 0.5, 1], - outputRange: [0, 1, 1], + outputRange: [0, backdropMaxOpacity, backdropMaxOpacity], }) - : backdrop; + : Animated.multiply(backdrop, backdropMaxOpacity); const opacities = animations.current; diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index 384fcc6c45..ad49fa13be 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -7,8 +7,6 @@ import { ViewStyle, } from 'react-native'; -import color from 'color'; - import type { InternalTheme } from '../../types'; type GetCombinedStylesProps = { @@ -29,7 +27,6 @@ type Variant = 'primary' | 'secondary' | 'tertiary' | 'surface'; type BaseProps = { isVariant: (variant: Variant) => boolean; theme: InternalTheme; - disabled?: boolean; }; export const getCombinedStyles = ({ @@ -165,17 +162,12 @@ export const getCombinedStyles = ({ const getBackgroundColor = ({ theme, isVariant, - disabled, customBackgroundColor, }: BaseProps & { customBackgroundColor?: ColorValue }) => { - if (customBackgroundColor && !disabled) { + if (customBackgroundColor) { return customBackgroundColor; } - if (disabled) { - return theme.colors.surfaceDisabled; - } - if (isVariant('primary')) { return theme.colors.primaryContainer; } @@ -189,7 +181,7 @@ const getBackgroundColor = ({ } if (isVariant('surface')) { - return theme.colors.elevation.level3; + return theme.colors.surfaceContainerHigh; } return theme.colors.primaryContainer; @@ -198,17 +190,12 @@ const getBackgroundColor = ({ const getForegroundColor = ({ theme, isVariant, - disabled, customColor, }: BaseProps & { customColor?: string }) => { - if (typeof customColor !== 'undefined' && !disabled) { + if (typeof customColor !== 'undefined') { return customColor; } - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - if (isVariant('primary')) { return theme.colors.onPrimaryContainer; } @@ -231,13 +218,11 @@ const getForegroundColor = ({ export const getFABColors = ({ theme, variant, - disabled, customColor, customBackgroundColor, }: { theme: InternalTheme; variant: string; - disabled?: boolean; customColor?: string; customBackgroundColor?: ColorValue; }) => { @@ -245,7 +230,7 @@ export const getFABColors = ({ return variant === variantToCompare; }; - const baseFABColorProps = { theme, isVariant, disabled }; + const baseFABColorProps = { theme, isVariant }; const backgroundColor = getBackgroundColor({ ...baseFABColorProps, @@ -267,21 +252,8 @@ const getLabelColor = ({ theme }: { theme: InternalTheme }) => { return theme.colors.onSurface; }; -const getBackdropColor = ({ - theme, - customBackdropColor, -}: { - theme: InternalTheme; - customBackdropColor?: string; -}) => { - if (customBackdropColor) { - return customBackdropColor; - } - return color(theme.colors.background).alpha(0.95).rgb().string(); -}; - const getStackedFABBackgroundColor = ({ theme }: { theme: InternalTheme }) => { - return theme.colors.elevation.level3; + return theme.colors.surfaceContainerHigh; }; export const getFABGroupColors = ({ @@ -293,7 +265,8 @@ export const getFABGroupColors = ({ }) => { return { labelColor: getLabelColor({ theme }), - backdropColor: getBackdropColor({ theme, customBackdropColor }), + backdropColor: customBackdropColor ?? theme.colors.background, + backdropOpacity: customBackdropColor ? 1 : 0.95, stackedFABBackgroundColor: getStackedFABBackgroundColor({ theme }), }; }; diff --git a/src/components/HelperText/HelperText.tsx b/src/components/HelperText/HelperText.tsx index 8043968cff..6fc44d58a5 100644 --- a/src/components/HelperText/HelperText.tsx +++ b/src/components/HelperText/HelperText.tsx @@ -5,6 +5,7 @@ import { StyleProp, StyleSheet, TextStyle, + View, } from 'react-native'; import { getTextColor } from './utils'; @@ -122,36 +123,42 @@ const HelperText = ({ textHeight = e.nativeEvent.layout.height; }; - const textColor = getTextColor({ theme, disabled, type }); + const { color: textColor, opacity: textOpacity } = getTextColor({ + theme, + disabled, + type, + }); return ( - - {rest.children} - + + + {rest.children} + + ); }; diff --git a/src/components/HelperText/utils.ts b/src/components/HelperText/utils.ts index ab9d2dde8e..767cb74ef0 100644 --- a/src/components/HelperText/utils.ts +++ b/src/components/HelperText/utils.ts @@ -1,5 +1,8 @@ +import { tokens } from '../../styles/themes/v3/tokens'; import type { InternalTheme } from '../../types'; +const { stateOpacity } = tokens.md.ref; + type BaseProps = { theme: InternalTheme; disabled?: boolean; @@ -7,14 +10,19 @@ type BaseProps = { }; export function getTextColor({ theme, disabled, type }: BaseProps) { - const { colors } = theme; - if (type === 'error') { - return colors?.error; + return { color: theme.colors.error, opacity: stateOpacity.enabled }; } if (disabled) { - return theme.colors.onSurfaceDisabled; + return { + color: theme.colors.onSurfaceVariant, + opacity: stateOpacity.disabled, + }; } - return theme.colors.onSurfaceVariant; + + return { + color: theme.colors.onSurfaceVariant, + opacity: stateOpacity.enabled, + }; } diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index dbe7de4841..8cf5bff8d5 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -134,7 +134,13 @@ const IconButton = forwardRef( const IconComponent = animated ? CrossFadeIcon : Icon; - const { iconColor, backgroundColor, borderColor } = getIconButtonColor({ + const { + iconColor, + iconOpacity, + backgroundColor, + borderColor, + backgroundOpacity, + } = getIconButtonColor({ theme, disabled, selected, @@ -162,7 +168,8 @@ const IconButton = forwardRef( testID={`${testID}-container`} style={[ { - backgroundColor, + backgroundColor: + backgroundOpacity < 1 ? undefined : backgroundColor, width: buttonSize, height: buttonSize, }, @@ -173,6 +180,15 @@ const IconButton = forwardRef( container elevation={0} > + {backgroundOpacity < 1 && ( + + )} ( testID={testID} {...rest} > - {loading ? ( - - ) : ( - - )} + + {loading ? ( + + ) : ( + + )} + ); diff --git a/src/components/IconButton/utils.ts b/src/components/IconButton/utils.ts index fd985b04aa..653a272750 100644 --- a/src/components/IconButton/utils.ts +++ b/src/components/IconButton/utils.ts @@ -1,5 +1,8 @@ +import { tokens } from '../../styles/themes/v3/tokens'; import type { InternalTheme } from '../../types'; +const { stateOpacity } = tokens.md.ref; + type IconButtonMode = 'outlined' | 'contained' | 'contained-tonal'; type BaseProps = { @@ -9,20 +12,6 @@ type BaseProps = { selected?: boolean; }; -const getBorderColor = ({ - theme, - disabled, -}: { - theme: InternalTheme; - disabled?: boolean; -}) => { - if (disabled) { - return theme.colors.surfaceDisabled; - } - - return theme.colors.outline; -}; - const getBackgroundColor = ({ theme, isMode, @@ -32,7 +21,7 @@ const getBackgroundColor = ({ }: BaseProps & { customContainerColor?: string }) => { if (disabled) { if (isMode('contained') || isMode('contained-tonal')) { - return theme.colors.surfaceDisabled; + return theme.colors.onSurface; } } @@ -71,7 +60,7 @@ const getIconColor = ({ customIconColor, }: BaseProps & { customIconColor?: string }) => { if (disabled) { - return theme.colors.onSurfaceDisabled; + return theme.colors.onSurface; } if (typeof customIconColor !== 'undefined') { @@ -136,12 +125,23 @@ export const getIconButtonColor = ({ customIconColor, }); + const iconOpacity = disabled ? stateOpacity.disabled : stateOpacity.enabled; + + const backgroundColor = getBackgroundColor({ + ...baseIconColorProps, + customContainerColor, + }); + + const backgroundOpacity = + disabled && (isMode('contained') || isMode('contained-tonal')) + ? stateOpacity.disabled + : stateOpacity.enabled; + return { iconColor, - backgroundColor: getBackgroundColor({ - ...baseIconColorProps, - customContainerColor, - }), - borderColor: getBorderColor({ theme, disabled }), + iconOpacity, + backgroundColor, + borderColor: theme.colors.outlineVariant, + backgroundOpacity, }; }; diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 725e38cd5b..ac77147031 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -145,7 +145,7 @@ const MenuItem = ({ hitSlop, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { titleColor, iconColor } = getMenuItemColor({ + const { titleColor, iconColor, contentOpacity } = getMenuItemColor({ theme, disabled, }); @@ -185,7 +185,7 @@ const MenuItem = ({ accessibilityState={newAccessibilityState} hitSlop={hitSlop} > - + {leadingIcon ? ( diff --git a/src/components/Menu/utils.ts b/src/components/Menu/utils.ts index 8fc73ca082..f9e3d5d9a1 100644 --- a/src/components/Menu/utils.ts +++ b/src/components/Menu/utils.ts @@ -1,6 +1,9 @@ -import type { InternalTheme, MD3Theme } from '../../types'; +import { tokens } from '../../styles/themes/v3/tokens'; +import type { InternalTheme } from '../../types'; import type { IconSource } from '../Icon'; +const { stateOpacity } = tokens.md.ref; + export const MIN_WIDTH = 112; export const MAX_WIDTH = 280; @@ -15,32 +18,23 @@ type ColorProps = { disabled?: boolean; }; -const getDisabledColor = (theme: InternalTheme) => { - return (theme as MD3Theme).colors.onSurfaceDisabled; -}; - -const getTitleColor = ({ theme, disabled }: ColorProps) => { - const { colors } = theme as MD3Theme; - if (disabled) { - return getDisabledColor(theme); - } - - return colors.onSurface; +const getTitleColor = ({ theme }: ColorProps) => { + return theme.colors.onSurface; }; -const getIconColor = ({ theme, disabled }: ColorProps) => { - const { colors } = theme as MD3Theme; - if (disabled) { - return getDisabledColor(theme); - } - - return colors.onSurfaceVariant; +const getIconColor = ({ theme }: ColorProps) => { + return theme.colors.onSurfaceVariant; }; export const getMenuItemColor = ({ theme, disabled }: ColorProps) => { + const contentOpacity = disabled + ? stateOpacity.disabled + : stateOpacity.enabled; + return { titleColor: getTitleColor({ theme, disabled }), iconColor: getIconColor({ theme, disabled }), + contentOpacity, }; }; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index dc55c3cf7a..21b1ffd39d 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -14,11 +14,14 @@ import useLatestCallback from 'use-latest-callback'; import Surface from './Surface'; import { useInternalTheme } from '../core/theming'; +import { tokens } from '../styles/themes/v3/tokens'; import type { ThemeProp } from '../types'; import { addEventListener } from '../utils/addEventListener'; import { BackHandler } from '../utils/BackHandler/BackHandler'; import useAnimatedValue from '../utils/useAnimatedValue'; +const { scrimAlpha } = tokens.md.ref; + export type Props = { /** * Determines whether clicking outside the modal dismisses it. @@ -201,8 +204,11 @@ function Modal({ style={[ styles.backdrop, { - backgroundColor: theme.colors?.backdrop, - opacity, + backgroundColor: theme.colors.scrim, + opacity: opacity.interpolate({ + inputRange: [0, 1], + outputRange: [0, scrimAlpha], + }), }, ]} testID={`${testID}-backdrop`} diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index 515efe5eff..4b68455101 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -110,20 +110,18 @@ const RadioButtonAndroid = ({ value, }) === 'checked'; - const { rippleColor, selectionControlColor } = - getAndroidSelectionControlColor({ - theme, - disabled, - checked, - customColor: rest.color, - customUncheckedColor: rest.uncheckedColor, - }); + const { selectionControlColor } = getAndroidSelectionControlColor({ + theme, + disabled, + checked, + customColor: rest.color, + customUncheckedColor: rest.uncheckedColor, + }); return ( ( style={[ { borderRadius: roundness }, { - backgroundColor: colors.elevation.level3, + backgroundColor: theme.colors.surfaceContainerHigh, borderRadius: roundness * (isBarMode ? 7 : 0), }, styles.container, @@ -338,7 +338,7 @@ const Searchbar = forwardRef( accessibilityRole="button" borderless onPress={onTraileringIconPress} - iconColor={traileringIconColor || theme.colors.onSurfaceVariant} + iconColor={traileringIconColor || colors.onSurfaceVariant} icon={traileringIcon} accessibilityLabel={traileringIconAccessibilityLabel} testID={`${testID}-trailering-icon`} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 6f04b6aeb7..c70ff68ae2 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -146,7 +146,7 @@ const SegmentedButtonItem = ({ }, [checked, checkScale, showSelectedCheck]); const { roundness } = theme; - const { borderColor, textColor, borderWidth, backgroundColor } = + const { borderColor, textColor, textOpacity, borderWidth, backgroundColor } = getSegmentedButtonColors({ checked, theme, @@ -210,7 +210,9 @@ const SegmentedButtonItem = ({ theme={theme} hitSlop={hitSlop} > - + {showCheckedIcon ? ( { const getSegmentedButtonBorderColor = ({ theme, disabled }: BaseProps) => { if (disabled) { - return theme.colors.surfaceDisabled; + return theme.colors.outlineVariant; } return theme.colors.outline; }; @@ -108,7 +111,7 @@ const getSegmentedButtonTextColor = ({ uncheckedColor, }: SegmentedButtonProps) => { if (disabled) { - return theme.colors.onSurfaceDisabled; + return theme.colors.onSurface; } if (checked) { return checkedColor ?? theme.colors.onSecondaryContainer; @@ -141,5 +144,7 @@ export const getSegmentedButtonColors = ({ }); const borderWidth = getSegmentedButtonBorderWidth({ theme }); - return { backgroundColor, borderColor, textColor, borderWidth }; + const textOpacity = disabled ? stateOpacity.disabled : stateOpacity.enabled; + + return { backgroundColor, borderColor, textColor, textOpacity, borderWidth }; }; diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index 6151e23ae2..9ee01c7cb2 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -339,7 +339,7 @@ const Snackbar = ({ accessibilityRole="button" borderless onPress={onIconPress} - iconColor={theme.colors.inverseOnSurface} + iconColor={colors.inverseOnSurface} theme={theme} icon={ icon || diff --git a/src/components/Switch/utils.ts b/src/components/Switch/utils.ts index 34ffc26eb5..c0afc7c142 100644 --- a/src/components/Switch/utils.ts +++ b/src/components/Switch/utils.ts @@ -81,7 +81,7 @@ const getOnTintColor = ({ } if (value) { - return setColor(checkedColor).alpha(0.5).rgb().string(); + return theme.colors.surfaceContainerHighest; } if (theme.dark) { diff --git a/src/components/TextInput/Addons/Underline.tsx b/src/components/TextInput/Addons/Underline.tsx index 20b49233dc..22a8e419cc 100644 --- a/src/components/TextInput/Addons/Underline.tsx +++ b/src/components/TextInput/Addons/Underline.tsx @@ -14,6 +14,7 @@ type UnderlineProps = { activeColor: string; underlineColorCustom?: string; hasActiveOutline?: boolean; + disabledOpacity?: number; style?: StyleProp; theme?: ThemeProp; }; @@ -25,6 +26,7 @@ export const Underline = ({ activeColor, underlineColorCustom, hasActiveOutline, + disabledOpacity, style, theme: _themeOverrides, }: UnderlineProps) => { @@ -44,6 +46,7 @@ export const Underline = ({ styles.md3Underline, { backgroundColor, + opacity: disabledOpacity, // Underlines is thinner when input is not focused transform: [ { diff --git a/src/components/TextInput/Adornment/TextInputAffix.tsx b/src/components/TextInput/Adornment/TextInputAffix.tsx index 71a2b78475..94a0c08662 100644 --- a/src/components/TextInput/Adornment/TextInputAffix.tsx +++ b/src/components/TextInput/Adornment/TextInputAffix.tsx @@ -152,12 +152,19 @@ const TextInputAffix = ({ [side]: offset, } as ViewStyle; - const textColor = getTextColor({ theme, disabled }); + const { color: textColor, opacity: textOpacity } = getTextColor({ + theme, + disabled, + }); const content = ( diff --git a/src/components/TextInput/Adornment/TextInputIcon.tsx b/src/components/TextInput/Adornment/TextInputIcon.tsx index 0005071fe3..0fc2c7c906 100644 --- a/src/components/TextInput/Adornment/TextInputIcon.tsx +++ b/src/components/TextInput/Adornment/TextInputIcon.tsx @@ -142,7 +142,7 @@ const TextInputIcon = ({ const theme = useInternalTheme(themeOverrides); - const iconColor = getIconColor({ + const { color: iconColor, opacity: iconOpacity } = getIconColor({ theme, disabled, isTextInputFocused, @@ -150,7 +150,7 @@ const TextInputIcon = ({ }); return ( - + string | undefined) | string; }) { - if (typeof customColor === 'function') { - return customColor(isTextInputFocused); - } - if (customColor) { - return customColor; - } + const color = + typeof customColor === 'function' + ? customColor(isTextInputFocused) + : customColor ?? theme.colors.onSurfaceVariant; - if (disabled) { - return theme.colors.onSurfaceDisabled; - } + const opacity = + disabled && !customColor ? stateOpacity.disabled : stateOpacity.enabled; - return theme.colors.onSurfaceVariant; + return { color, opacity }; } diff --git a/src/components/TextInput/Label/InputLabel.tsx b/src/components/TextInput/Label/InputLabel.tsx index 4877bc236e..6fd869a1c3 100644 --- a/src/components/TextInput/Label/InputLabel.tsx +++ b/src/components/TextInput/Label/InputLabel.tsx @@ -18,7 +18,6 @@ const InputLabel = (props: InputLabelProps) => { wiggle, error, focused, - opacity, labelLayoutWidth, labelLayoutHeight, labelBackground, @@ -44,6 +43,8 @@ const InputLabel = (props: InputLabelProps) => { backgroundColor, roundness, placeholderColor, + disabledOpacity, + opacity, errorColor, labelTranslationXOffset, maxFontSizeMultiplier, @@ -139,7 +140,12 @@ const InputLabel = (props: InputLabelProps) => { // This gives the effect of animating the color, but allows us to use native driver { if (textColor) { return textColor; } - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - return theme.colors.onSurface; }; const getActiveColor = ({ theme, - disabled, error, activeUnderlineColor, activeOutlineColor, @@ -340,18 +333,10 @@ const getActiveColor = ({ return modeColor; } - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - return theme.colors.primary; }; -const getPlaceholderColor = ({ theme, disabled }: BaseProps) => { - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - +const getPlaceholderColor = ({ theme }: BaseProps) => { return theme.colors.onSurfaceVariant; }; @@ -365,17 +350,12 @@ const getSelectionColor = ({ if (typeof customSelectionColor !== 'undefined') { return customSelectionColor; } - - if (Platform.OS === 'android') { - return color(activeColor).alpha(0.54).rgb().string(); - } - return activeColor; }; const getFlatBackgroundColor = ({ theme, disabled }: BaseProps) => { if (disabled) { - return color(theme.colors.onSurface).alpha(0.04).rgb().string(); + return theme.colors.surfaceContainerHighest; } return theme.colors.surfaceVariant; @@ -390,10 +370,6 @@ const getFlatUnderlineColor = ({ return underlineColor; } - if (disabled) { - return theme.colors.onSurfaceDisabled; - } - return theme.colors.onSurfaceVariant; }; @@ -410,8 +386,7 @@ const getOutlinedOutlineInputColor = ({ if (theme.dark) { return 'transparent'; } - - return theme.colors.surfaceDisabled; + return theme.colors.outlineVariant; } return theme.colors.outline; @@ -442,12 +417,17 @@ export const getFlatInputColors = ({ mode: 'flat', }); + const disabledOpacity = disabled + ? stateOpacity.disabled + : stateOpacity.enabled; + return { inputTextColor: getInputTextColor({ ...baseFlatColorProps, textColor, }), activeColor, + disabledOpacity, underlineColorCustom: getFlatUnderlineColor({ ...baseFlatColorProps, underlineColor, @@ -484,12 +464,17 @@ export const getOutlinedInputColors = ({ mode: 'outlined', }); + const disabledOpacity = disabled + ? stateOpacity.disabled + : stateOpacity.enabled; + return { inputTextColor: getInputTextColor({ ...baseOutlinedColorProps, textColor, }), activeColor, + disabledOpacity, outlineColor: getOutlinedOutlineInputColor({ ...baseOutlinedColorProps, customOutlineColor, diff --git a/src/components/TextInput/types.tsx b/src/components/TextInput/types.tsx index 1f710e4089..49bc46e121 100644 --- a/src/components/TextInput/types.tsx +++ b/src/components/TextInput/types.tsx @@ -115,6 +115,7 @@ export type LabelProps = { paddingRight?: number; labelTranslationXOffset?: number; placeholderColor: string | null; + disabledOpacity?: number; backgroundColor?: ColorValue; label?: TextInputLabelProp | null; hasActiveOutline?: boolean | null; diff --git a/src/components/ToggleButton/utils.ts b/src/components/ToggleButton/utils.ts index efb2857bed..e471a5b4b6 100644 --- a/src/components/ToggleButton/utils.ts +++ b/src/components/ToggleButton/utils.ts @@ -1,6 +1,3 @@ -import color from 'color'; - -import { tokens } from '../../styles/themes/v3/tokens'; import type { InternalTheme } from '../../types'; export const getToggleButtonColor = ({ @@ -11,10 +8,7 @@ export const getToggleButtonColor = ({ checked: boolean | null; }) => { if (checked) { - return color(theme.colors.onSecondaryContainer) - .alpha(tokens.md.ref.opacity.level2) - .rgb() - .string(); + return theme.colors.surfaceContainerHighest; } - return 'transparent'; + return theme.colors.surfaceContainer; }; diff --git a/src/components/TouchableRipple/utils.ts b/src/components/TouchableRipple/utils.ts index 83308eb228..10dcef6703 100644 --- a/src/components/TouchableRipple/utils.ts +++ b/src/components/TouchableRipple/utils.ts @@ -29,7 +29,7 @@ const getRippleColor = ({ return rippleColor; } - return color(theme.colors.onSurface).alpha(0.1).rgb().string(); + return theme.colors.stateLayerPressed; }; export const getTouchableRippleColors = ({ diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 6c80269a0f..e1c55fa8c8 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -232,21 +232,6 @@ describe('AppbarAction', () => { expect(appbarActionIcon.props.color).toBe('purple'); }); - it('should be rendered with custom ripple color', () => { - const { getByTestId } = render( - - - - ); - const appbarActionContainer = getByTestId('appbar-action-container').props - .children; - expect(appbarActionContainer.props.rippleColor).toBe('purple'); - }); - it('should render AppbarBackAction with custom color', () => { const { getByTestId } = render( diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 50ab8e3cd6..82c623d863 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -46,7 +46,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -64,7 +64,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -104,7 +104,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -181,35 +181,43 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` } testID="search-bar-icon" > - + - magnify - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + magnify + + @@ -288,7 +296,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -365,35 +373,43 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` } testID="search-bar-clear-icon" > - + - close - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + + @@ -471,7 +487,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -550,76 +566,84 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A > - + > + + @@ -714,7 +738,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -792,66 +816,74 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A > - - menu - + + menu + + diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 3d16801c15..b47be8c53e 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -5,9 +5,12 @@ import { act, fireEvent, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; import { pink500, white } from '../../styles/themes/v2/colors'; +import { tokens } from '../../styles/themes/v3/tokens'; import Button from '../Button/Button'; import { getButtonColors } from '../Button/utils'; +const { stateOpacity } = tokens.md.ref; + const styles = StyleSheet.create({ flexing: { flexDirection: 'row-reverse', @@ -349,7 +352,8 @@ describe('getButtonColors - background color', () => { disabled: true, }) ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceDisabled, + backgroundColor: getTheme().colors.onSurface, + backgroundOpacity: stateOpacity.pressed, }); }) ); @@ -364,7 +368,8 @@ describe('getButtonColors - background color', () => { disabled: true, }) ).toMatchObject({ - backgroundColor: getTheme(true).colors.surfaceDisabled, + backgroundColor: getTheme(true).colors.onSurface, + backgroundOpacity: stateOpacity.pressed, }); }) ); @@ -376,7 +381,7 @@ describe('getButtonColors - background color', () => { mode: 'elevated', }) ).toMatchObject({ - backgroundColor: getTheme().colors.elevation.level1, + backgroundColor: getTheme().colors.surfaceContainerLow, }); }); @@ -387,7 +392,7 @@ describe('getButtonColors - background color', () => { mode: 'elevated', }) ).toMatchObject({ - backgroundColor: getTheme(true).colors.elevation.level1, + backgroundColor: getTheme(true).colors.surfaceContainerLow, }); }); @@ -485,7 +490,8 @@ describe('getButtonColors - text color', () => { mode: 'text', }) ).toMatchObject({ - textColor: getTheme().colors.onSurfaceDisabled, + textColor: getTheme().colors.onSurface, + textOpacity: stateOpacity.disabled, }); }); @@ -498,7 +504,8 @@ describe('getButtonColors - text color', () => { mode: 'text', }) ).toMatchObject({ - textColor: getTheme(true).colors.onSurfaceDisabled, + textColor: getTheme(true).colors.onSurface, + textOpacity: stateOpacity.disabled, }); }); @@ -596,7 +603,7 @@ describe('getButtonColors - border color', () => { mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme().colors.surfaceDisabled, + borderColor: getTheme().colors.outlineVariant, }); }); @@ -608,7 +615,7 @@ describe('getButtonColors - border color', () => { mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme(true).colors.surfaceDisabled, + borderColor: getTheme(true).colors.outlineVariant, }); }); @@ -619,7 +626,7 @@ describe('getButtonColors - border color', () => { mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme().colors.outline, + borderColor: getTheme().colors.outlineVariant, }); }); @@ -630,7 +637,7 @@ describe('getButtonColors - border color', () => { mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme(true).colors.outline, + borderColor: getTheme(true).colors.outlineVariant, }); }); diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index 3325829520..5c416c9720 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -165,6 +165,7 @@ exports[`can render leading checkbox control 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "right", }, undefined, @@ -262,6 +263,7 @@ exports[`can render the Android checkbox on different platforms 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "left", }, undefined, @@ -323,6 +325,7 @@ exports[`can render the Android checkbox on different platforms 1`] = ` collapsable={false} style={ { + "opacity": 1, "transform": [ { "scale": 1, @@ -478,6 +481,7 @@ exports[`can render the iOS checkbox on different platforms 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "left", }, undefined, @@ -658,6 +662,7 @@ exports[`renders unchecked 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "left", }, undefined, diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index 3e551140c5..799a343768 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,50 +1,10 @@ -import color from 'color'; - import { getTheme } from '../../../core/theming'; +import { tokens } from '../../../styles/themes/v3/tokens'; import { getAndroidSelectionControlColor, getSelectionControlIOSColor, } from '../../Checkbox/utils'; - -describe('getAndroidSelectionControlColor - ripple color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(), - disabled: true, - checked: false, - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.onSurface) - .alpha(0.16) - .rgb() - .string(), - }); - }); - - it('should return custom color', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(), - customColor: 'purple', - checked: false, - }) - ).toMatchObject({ - rippleColor: color('purple').fade(0.32).rgb().string(), - }); - }); - - it('should return theme color, for theme version 3', () => { - expect( - getAndroidSelectionControlColor({ - theme: getTheme(), - checked: false, - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.primary).fade(0.32).rgb().string(), - }); - }); -}); +const { stateOpacity } = tokens.md.ref; describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return correct disabled color, for theme version 3', () => { @@ -55,7 +15,8 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { checked: false, }) ).toMatchObject({ - selectionControlColor: getTheme().colors.onSurfaceDisabled, + selectionControlColor: getTheme().colors.onSurface, + selectionControlOpacity: stateOpacity.disabled, }); }); @@ -104,41 +65,26 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { selectionControlColor: getTheme().colors.onSurfaceVariant, }); }); -}); - -describe('getSelectionControlIOSColor - ripple color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(), - disabled: true, - }) - ).toMatchObject({ - rippleColor: color(getTheme().colors.onSurface) - .alpha(0.16) - .rgb() - .string(), - }); - }); - it('should return custom color', () => { + it('should return theme color, unchecked, dark mode', () => { expect( - getSelectionControlIOSColor({ - theme: getTheme(), - customColor: 'purple', + getAndroidSelectionControlColor({ + theme: getTheme(true), + checked: false, }) ).toMatchObject({ - rippleColor: color('purple').fade(0.32).rgb().string(), + selectionControlColor: getTheme(true).colors.onSurfaceVariant, }); }); - it('should return theme color, for theme version 3', () => { + it('should return theme color, unchecked, light mode', () => { expect( - getSelectionControlIOSColor({ - theme: getTheme(), + getAndroidSelectionControlColor({ + theme: getTheme(false), + checked: false, }) ).toMatchObject({ - rippleColor: color(getTheme().colors.primary).fade(0.32).rgb().string(), + selectionControlColor: getTheme(false).colors.onSurfaceVariant, }); }); }); @@ -151,7 +97,8 @@ describe('getSelectionControlIOSColor - checked color', () => { disabled: true, }) ).toMatchObject({ - checkedColor: getTheme().colors.onSurfaceDisabled, + checkedColor: getTheme().colors.primary, + checkedColorOpacity: stateOpacity.disabled, }); }); diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 5505a3726e..76044715b2 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -5,9 +5,12 @@ import { act, render } from '@testing-library/react-native'; import color from 'color'; import { getTheme } from '../../core/theming'; +import { tokens } from '../../styles/themes/v3/tokens'; import Chip from '../Chip/Chip'; import { getChipColors } from '../Chip/helpers'; +const { stateOpacity } = tokens.md.ref; + it('renders chip with onPress', () => { const tree = render( {}}>Example Chip).toJSON(); @@ -99,7 +102,8 @@ describe('getChipColors - text color', () => { isOutlined: false, }) ).toMatchObject({ - textColor: getTheme().colors.onSurfaceDisabled, + textColor: getTheme().colors.onSurface, + contentOpacity: stateOpacity.disabled, }); }); @@ -147,7 +151,8 @@ describe('getChipColors - icon color', () => { isOutlined: false, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceDisabled, + iconColor: getTheme().colors.onSurface, + contentOpacity: stateOpacity.disabled, }); }); @@ -187,7 +192,7 @@ describe('getChipColors - icon color', () => { }); describe('getChipColor - selected background color', () => { - it('should return custom color, for theme version 3, outlined mode', () => { + it('should return custom color, outlined mode', () => { expect( getChipColors({ theme: getTheme(), @@ -195,14 +200,11 @@ describe('getChipColor - selected background color', () => { isOutlined: true, }) ).toMatchObject({ - selectedBackgroundColor: color('purple') - .mix(color(getTheme().colors.onSurfaceVariant), 0) - .rgb() - .string(), + selectedBackgroundColor: 'purple', }); }); - it('should return custom color, for theme version 3, flat mode', () => { + it('should return custom color, flat mode', () => { expect( getChipColors({ theme: getTheme(), @@ -210,42 +212,32 @@ describe('getChipColor - selected background color', () => { isOutlined: false, }) ).toMatchObject({ - selectedBackgroundColor: color('purple') - .mix(color(getTheme().colors.onSecondaryContainer), 0) - .rgb() - .string(), + selectedBackgroundColor: 'purple', }); }); - it('should return custom color, for theme version 3, outlined mode, show selected overlay', () => { + it('should return theme color, for theme version 3, flat mode', () => { expect( getChipColors({ theme: getTheme(), - customBackgroundColor: 'purple', - showSelectedOverlay: true, - isOutlined: true, + isOutlined: false, }) ).toMatchObject({ - selectedBackgroundColor: color('purple') - .mix(color(getTheme().colors.onSurfaceVariant), 0.12) - .rgb() - .string(), + selectedBackgroundColor: getTheme().colors.secondaryContainer, }); }); +}); - it('should return custom color, for theme version 3, flat mode, show selected overlay', () => { +describe('getChipColor - background color', () => { + it('should return custom color', () => { expect( getChipColors({ theme: getTheme(), customBackgroundColor: 'purple', - showSelectedOverlay: true, isOutlined: false, }) ).toMatchObject({ - selectedBackgroundColor: color('purple') - .mix(color(getTheme().colors.onSecondaryContainer), 0.12) - .rgb() - .string(), + backgroundColor: 'purple', }); }); @@ -256,10 +248,7 @@ describe('getChipColor - selected background color', () => { isOutlined: true, }) ).toMatchObject({ - selectedBackgroundColor: color(getTheme().colors.surface) - .mix(color(getTheme().colors.onSurfaceVariant), 0) - .rgb() - .string(), + backgroundColor: getTheme().colors.surface, }); }); @@ -270,56 +259,64 @@ describe('getChipColor - selected background color', () => { isOutlined: false, }) ).toMatchObject({ - selectedBackgroundColor: color(getTheme().colors.secondaryContainer) - .mix(color(getTheme().colors.onSecondaryContainer), 0) - .rgb() - .string(), + backgroundColor: getTheme().colors.secondaryContainer, }); }); }); -describe('getChipColor - background color', () => { - it('should return custom color', () => { +describe('getChipColor - border color', () => { + it('should return correct disabled color, for theme version 3', () => { expect( getChipColors({ theme: getTheme(), - customBackgroundColor: 'purple', + disabled: true, isOutlined: false, }) ).toMatchObject({ - backgroundColor: 'purple', + borderColor: 'transparent', }); }); - it('should return theme color, for theme version 3, outlined mode', () => { + it('should return custom color, for theme version 3', () => { expect( getChipColors({ theme: getTheme(), - isOutlined: true, + selectedColor: 'purple', + isOutlined: false, }) ).toMatchObject({ - backgroundColor: getTheme().colors.surface, + borderColor: 'transparent', }); }); - it('should return theme color, for theme version 3, flat mode', () => { + it('should return theme color, for theme version 3', () => { expect( getChipColors({ theme: getTheme(), isOutlined: false, }) ).toMatchObject({ - backgroundColor: getTheme().colors.secondaryContainer, + borderColor: 'transparent', }); }); -}); -describe('getChipColor - border color', () => { - it('should return correct disabled color, for theme version 3', () => { + it('should return custom color, outlined mode', () => { expect( getChipColors({ - theme: getTheme(), - disabled: true, + theme: getTheme(false), + selectedColor: 'purple', + isOutlined: true, + }) + ).toMatchObject({ + borderColor: color('purple').alpha(0.29).rgb().string(), + }); + }); + + it('should return custom color, flat mode', () => { + expect( + getChipColors({ + theme: getTheme(true), + customBackgroundColor: 'purple', isOutlined: false, }) ).toMatchObject({ @@ -327,11 +324,32 @@ describe('getChipColor - border color', () => { }); }); - it('should return custom color, for theme version 3', () => { + it('should return theme color, light mode, outlined mode', () => { expect( getChipColors({ - theme: getTheme(), - selectedColor: 'purple', + theme: getTheme(false), + isOutlined: true, + }) + ).toMatchObject({ + borderColor: getTheme(false).colors.outlineVariant, + }); + }); + + it('should return theme color, dark mode, outlined mode', () => { + expect( + getChipColors({ + theme: getTheme(true), + isOutlined: true, + }) + ).toMatchObject({ + borderColor: getTheme(true).colors.outlineVariant, + }); + }); + + it('should return theme background color, light mode, flat mode', () => { + expect( + getChipColors({ + theme: getTheme(false), isOutlined: false, }) ).toMatchObject({ @@ -339,10 +357,10 @@ describe('getChipColor - border color', () => { }); }); - it('should return theme color, for theme version 3', () => { + it('should return theme background color, dark mode, flat mode', () => { expect( getChipColors({ - theme: getTheme(), + theme: getTheme(true), isOutlined: false, }) ).toMatchObject({ diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index 74f636cf5c..9fc9c12e98 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -91,12 +91,6 @@ it('renders loading FAB with custom size prop', () => { expect(tree).toMatchSnapshot(); }); -it('renders disabled FAB', () => { - const tree = render( {}} icon="plus" disabled />).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - it('renders custom color for the icon and label of the FAB', () => { const tree = render( {}} icon="plus" color="#AA0114" /> @@ -196,19 +190,7 @@ describe('getFABColors - background color', () => { }); }); - it('should return correct disabled color, for theme version 3', () => { - expect( - getFABColors({ - theme: getTheme(), - disabled: true, - variant: 'primary', - }) - ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceDisabled, - }); - }); - - it('should return correct theme color, for theme version 3, primary variant', () => { + it('should return correct theme color, primary variant', () => { expect( getFABColors({ theme: getTheme(), @@ -248,7 +230,7 @@ describe('getFABColors - background color', () => { variant: 'surface', }) ).toMatchObject({ - backgroundColor: getTheme().colors.elevation.level3, + backgroundColor: getTheme().colors.surfaceContainerHigh, }); }); }); @@ -266,19 +248,7 @@ describe('getFABColors - foreground color', () => { }); }); - it('should return correct disabled color, for theme version 3', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'primary', - disabled: true, - }) - ).toMatchObject({ - foregroundColor: getTheme().colors.onSurfaceDisabled, - }); - }); - - it('should return correct theme color, for theme version 3, primary variant', () => { + it('should return correct theme color, primary variant', () => { expect( getFABColors({ theme: getTheme(), diff --git a/src/components/__tests__/FABGroup.test.tsx b/src/components/__tests__/FABGroup.test.tsx index 7009526e42..5f25e46c6f 100644 --- a/src/components/__tests__/FABGroup.test.tsx +++ b/src/components/__tests__/FABGroup.test.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Animated } from 'react-native'; import { act, fireEvent, render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; import FAB from '../FAB'; @@ -26,10 +25,30 @@ describe('getFABGroupColors - backdrop color', () => { theme: getTheme(), }) ).toMatchObject({ - backdropColor: color(getTheme().colors.background) - .alpha(0.95) - .rgb() - .string(), + backdropColor: getTheme().colors.background, + }); + }); +}); + +describe('getFABGroupColors - backdrop opacity', () => { + it('should return scrimAlpha when no custom backdrop color', () => { + expect( + getFABGroupColors({ + theme: getTheme(), + }) + ).toMatchObject({ + backdropOpacity: 0.95, + }); + }); + + it('should return 1 when custom backdrop color is provided', () => { + expect( + getFABGroupColors({ + theme: getTheme(), + customBackdropColor: 'transparent', + }) + ).toMatchObject({ + backdropOpacity: 1, }); }); }); @@ -53,7 +72,7 @@ describe('getFABGroupColors - stacked FAB background color', () => { theme: getTheme(), }) ).toMatchObject({ - stackedFABBackgroundColor: getTheme().colors.elevation.level3, + stackedFABBackgroundColor: getTheme().colors.surfaceContainerHigh, }); }); }); diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index 39d1eb8663..fdd3316850 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -5,9 +5,12 @@ import { act, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; import { pink500 } from '../../styles/themes/v2/colors'; +import { tokens } from '../../styles/themes/v3/tokens'; import IconButton from '../IconButton/IconButton'; import { getIconButtonColor } from '../IconButton/utils'; +const { stateOpacity } = tokens.md.ref; + const styles = StyleSheet.create({ square: { borderRadius: 0, @@ -96,7 +99,8 @@ describe('getIconButtonColor - icon color', () => { disabled: true, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceDisabled, + iconColor: getTheme().colors.onSurface, + iconOpacity: stateOpacity.disabled, }); }); @@ -211,7 +215,10 @@ describe('getIconButtonColor - background color', () => { mode, disabled: true, }) - ).toMatchObject({ backgroundColor: getTheme().colors.surfaceDisabled }); + ).toMatchObject({ + backgroundColor: getTheme().colors.onSurface, + backgroundOpacity: stateOpacity.disabled, + }); }) ); @@ -292,7 +299,7 @@ describe('getIconButtonColor - border color', () => { disabled: true, }) ).toMatchObject({ - borderColor: getTheme().colors.surfaceDisabled, + borderColor: getTheme().colors.outlineVariant, }); }); @@ -302,7 +309,7 @@ describe('getIconButtonColor - border color', () => { theme: getTheme(), }) ).toMatchObject({ - borderColor: getTheme().colors.outline, + borderColor: getTheme().colors.outlineVariant, }); }); }); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index 10fb008b5e..dead127da1 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -3,9 +3,12 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { tokens } from '../../styles/themes/v3/tokens'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; +const { stateOpacity } = tokens.md.ref; + describe('Menu Item', () => { it('renders menu item', () => { const tree = render( @@ -70,7 +73,10 @@ describe('getMenuItemColor - title color', () => { theme: getTheme(), disabled: true, }) - ).toMatchObject({ titleColor: getTheme().colors.onSurfaceDisabled }); + ).toMatchObject({ + titleColor: getTheme().colors.onSurface, + contentOpacity: stateOpacity.disabled, + }); }); it('should return correct theme color, for theme version 3', () => { @@ -91,7 +97,10 @@ describe('getMenuItemColor - icon color', () => { theme: getTheme(), disabled: true, }) - ).toMatchObject({ iconColor: getTheme().colors.onSurfaceDisabled }); + ).toMatchObject({ + iconColor: getTheme().colors.onSurfaceVariant, + contentOpacity: stateOpacity.disabled, + }); }); it('should return correct theme color, for theme version 3', () => { diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 32161d5b84..d040cc62de 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -9,8 +9,11 @@ import { import { act, fireEvent, render } from '@testing-library/react-native'; import { MD3LightTheme } from '../../styles/themes'; +import { tokens } from '../../styles/themes/v3/tokens'; import Modal from '../Modal'; +const { scrimAlpha } = tokens.md.ref; + jest.mock('react-native-safe-area-context', () => ({ useSafeAreaInsets: () => ({ bottom: 44, left: 0, right: 0, top: 37 }), })); @@ -55,7 +58,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - backgroundColor: MD3LightTheme.colors.backdrop, + backgroundColor: MD3LightTheme.colors.scrim, }); }); @@ -66,7 +69,7 @@ describe('Modal', () => { testID="modal" theme={{ colors: { - backdrop: 'transparent', + scrim: 'transparent', }, }} > @@ -119,7 +122,7 @@ describe('Modal', () => { }); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ @@ -156,7 +159,7 @@ describe('Modal', () => { }); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ @@ -198,7 +201,7 @@ describe('Modal', () => { }); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ @@ -241,7 +244,7 @@ describe('Modal', () => { }); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ @@ -308,7 +311,7 @@ describe('Modal', () => { }); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ @@ -376,7 +379,7 @@ describe('Modal', () => { }); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, @@ -393,7 +396,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, @@ -406,7 +409,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, @@ -453,7 +456,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, @@ -466,7 +469,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, @@ -491,7 +494,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, @@ -504,7 +507,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, @@ -527,7 +530,7 @@ describe('Modal', () => { }); expect(getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 1, + opacity: scrimAlpha, }); expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, diff --git a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap index de667249ae..f44e395c55 100644 --- a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap +++ b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap @@ -163,6 +163,7 @@ exports[`can render leading radio button control 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "right", }, undefined, @@ -257,6 +258,7 @@ exports[`can render the Android radio button on different platforms 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "left", }, undefined, @@ -408,6 +410,7 @@ exports[`can render the iOS radio button on different platforms 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "left", }, undefined, @@ -585,6 +588,7 @@ exports[`renders unchecked 1`] = ` }, { "color": "rgba(29, 27, 32, 1)", + "opacity": 1, "textAlign": "left", }, undefined, diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 8475751f41..10385d5172 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -3,12 +3,15 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { tokens } from '../../styles/themes/v3/tokens'; import SegmentedButtons from '../SegmentedButtons/SegmentedButtons'; import { getDisabledSegmentedButtonStyle, getSegmentedButtonColors, } from '../SegmentedButtons/utils'; +const { stateOpacity } = tokens.md.ref; + it('renders segmented button', () => { const tree = render( { theme | disabled | checked | checkedColor | uncheckedColor | expected ${getTheme()} | ${false} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSecondaryContainer} ${getTheme()} | ${false} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} - ${getTheme()} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} - ${getTheme()} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} + ${getTheme()} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} + ${getTheme()} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} ${getTheme()} | ${false} | ${true} | ${'a125f5'} | ${undefined} | ${'a125f5'} ${getTheme()} | ${false} | ${false} | ${undefined} | ${'000'} | ${'000'} ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${'000'} | ${'000'} @@ -121,7 +124,7 @@ describe('getSegmentedButtonColors', () => { checked: false, }) ).toMatchObject({ - borderColor: getTheme().colors.surfaceDisabled, + borderColor: getTheme().colors.outlineVariant, }); }); @@ -145,7 +148,8 @@ describe('getSegmentedButtonColors', () => { checked: false, }) ).toMatchObject({ - textColor: getTheme().colors.onSurfaceDisabled, + textColor: getTheme().colors.onSurface, + textOpacity: stateOpacity.disabled, }); }); }); diff --git a/src/components/__tests__/Surface.test.tsx b/src/components/__tests__/Surface.test.tsx index 8d8e639779..045a63a3e6 100644 --- a/src/components/__tests__/Surface.test.tsx +++ b/src/components/__tests__/Surface.test.tsx @@ -65,7 +65,7 @@ describe('Surface', () => { shadowRadius: 12, }); expect(getByTestId('surface-test')).toHaveStyle({ - backgroundColor: getTheme().colors.elevation.level5, + backgroundColor: getTheme().colors.surfaceContainerHighest, }); }); @@ -256,7 +256,7 @@ describe('Surface', () => { expect(getByTestId(testID)).not.toHaveStyle({ elevation: 5 }); expect(getByTestId(testID)).toHaveStyle({ - backgroundColor: getTheme().colors.elevation.level5, + backgroundColor: getTheme().colors.surfaceContainerHighest, }); }); }); diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index dfaa028b43..ee7a82a15d 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -198,7 +198,7 @@ describe('getSwitchColor - on tint color', () => { color: 'purple', }) ).toMatchObject({ - onTintColor: color('purple').alpha(0.5).rgb().string(), + checkedColor: 'purple', }); }); diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index c92ed138c4..2fbebb326f 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -3,16 +3,18 @@ import * as React from 'react'; import { I18nManager, Platform, StyleSheet, Text, View } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; -import color from 'color'; import { DefaultTheme, getTheme, ThemeProvider } from '../../core/theming'; import { red500 } from '../../styles/themes/v2/colors'; +import { tokens } from '../../styles/themes/v3/tokens'; import { getFlatInputColors, getOutlinedInputColors, } from '../TextInput/helpers'; import TextInput, { Props } from '../TextInput/TextInput'; +const { stateOpacity } = tokens.md.ref; + const style = StyleSheet.create({ inputStyle: { color: red500, @@ -547,7 +549,7 @@ describe('getFlatInputColor - underline color', () => { theme: getTheme(), }) ).toMatchObject({ - underlineColorCustom: getTheme().colors.onSurfaceDisabled, + underlineColorCustom: getTheme().colors.onSurfaceVariant, }); }); @@ -610,7 +612,8 @@ describe('getFlatInputColor - input text color', () => { theme: getTheme(), }) ).toMatchObject({ - inputTextColor: getTheme().colors.onSurfaceDisabled, + inputTextColor: getTheme().colors.onSurface, + disabledOpacity: stateOpacity.disabled, }); }); @@ -626,18 +629,19 @@ describe('getFlatInputColor - input text color', () => { }); describe('getFlatInputColor - placeholder color', () => { - it('should return correct disabled color, for theme version 3', () => { + it('should return correct disabled color', () => { expect( getFlatInputColors({ disabled: true, theme: getTheme(), }) ).toMatchObject({ - placeholderColor: getTheme().colors.onSurfaceDisabled, + placeholderColor: getTheme().colors.onSurfaceVariant, + disabledOpacity: stateOpacity.disabled, }); }); - it('should return correct theme color, for theme version 3', () => { + it('should return correct theme color', () => { expect( getFlatInputColors({ theme: getTheme(), @@ -656,10 +660,7 @@ describe('getFlatInputColor - background color', () => { theme: getTheme(), }) ).toMatchObject({ - backgroundColor: color(getTheme().colors.onSurface) - .alpha(0.04) - .rgb() - .string(), + backgroundColor: getTheme().colors.surfaceContainerHighest, }); expect( getFlatInputColors({ @@ -667,10 +668,7 @@ describe('getFlatInputColor - background color', () => { theme: getTheme(true), }) ).toMatchObject({ - backgroundColor: color(getTheme(true).colors.onSurface) - .alpha(0.04) - .rgb() - .string(), + backgroundColor: getTheme(true).colors.surfaceContainerHighest, }); }); @@ -715,7 +713,8 @@ describe('getFlatInputColor - active color', () => { theme: getTheme(), }) ).toMatchObject({ - activeColor: getTheme().colors.onSurfaceDisabled, + activeColor: getTheme().colors.primary, + disabledOpacity: stateOpacity.disabled, }); }); @@ -778,7 +777,7 @@ describe('getOutlinedInputColors - outline color', () => { theme: getTheme(), }) ).toMatchObject({ - outlineColor: getTheme().colors.surfaceDisabled, + outlineColor: getTheme().colors.outlineVariant, }); }); @@ -832,7 +831,8 @@ describe('getOutlinedInputColors - input text color', () => { theme: getTheme(), }) ).toMatchObject({ - inputTextColor: getTheme().colors.onSurfaceDisabled, + inputTextColor: getTheme().colors.onSurface, + disabledOpacity: stateOpacity.disabled, }); }); @@ -855,7 +855,8 @@ describe('getOutlinedInputColors - placeholder color', () => { theme: getTheme(), }) ).toMatchObject({ - placeholderColor: getTheme().colors.onSurfaceDisabled, + placeholderColor: getTheme().colors.onSurfaceVariant, + disabledOpacity: stateOpacity.disabled, }); }); @@ -900,7 +901,8 @@ describe('getOutlinedInputColors - active color', () => { theme: getTheme(), }) ).toMatchObject({ - activeColor: getTheme().colors.onSurfaceDisabled, + activeColor: getTheme().colors.primary, + disabledOpacity: stateOpacity.disabled, }); }); diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx index 61936f4909..a830558681 100644 --- a/src/components/__tests__/ToggleButton.test.tsx +++ b/src/components/__tests__/ToggleButton.test.tsx @@ -2,10 +2,8 @@ import * as React from 'react'; import { Animated } from 'react-native'; import { act, render } from '@testing-library/react-native'; -import color from 'color'; import { getTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/v3/tokens'; import ToggleButton from '../ToggleButton'; import { getToggleButtonColor } from '../ToggleButton/utils'; @@ -36,25 +34,19 @@ it('renders unchecked toggle button', () => { describe('getToggleButtonColor', () => { it('should return correct color when checked and theme version 3', () => { expect(getToggleButtonColor({ theme: getTheme(), checked: true })).toBe( - color(getTheme().colors.onSecondaryContainer) - .alpha(tokens.md.ref.opacity.level2) - .rgb() - .string() + getTheme().colors.surfaceContainerHighest ); }); it('should return correct color when checked and theme version 3, dark theme', () => { expect(getToggleButtonColor({ theme: getTheme(true), checked: true })).toBe( - color(getTheme(true).colors.onSecondaryContainer) - .alpha(tokens.md.ref.opacity.level2) - .rgb() - .string() + getTheme(true).colors.surfaceContainerHighest ); }); - it('should return transparent color when not checked', () => { + it('should return correct color when not checked', () => { expect(getToggleButtonColor({ theme: getTheme(), checked: false })).toBe( - 'transparent' + getTheme().colors.surfaceContainer ); }); }); diff --git a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap index 0a61a1a3de..6be6a1afa3 100644 --- a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap @@ -5,7 +5,7 @@ exports[`render visible banner, with custom theme 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -22,7 +22,7 @@ exports[`render visible banner, with custom theme 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -208,6 +208,9 @@ exports[`render visible banner, with custom theme 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -278,7 +281,7 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 0, "shadowColor": "#000", "shadowOffset": { @@ -295,7 +298,7 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -413,7 +416,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -430,7 +433,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -638,6 +641,9 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -708,7 +714,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -725,7 +731,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -911,6 +917,9 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -1060,6 +1069,9 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -1130,7 +1142,7 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -1147,7 +1159,7 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { @@ -1275,7 +1287,7 @@ exports[`renders visible banner, without action buttons and without image 1`] = collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, "shadowColor": "#000", "shadowOffset": { @@ -1292,7 +1304,7 @@ exports[`renders visible banner, without action buttons and without image 1`] = collapsable={false} style={ { - "backgroundColor": "rgba(255, 255, 255, 1)", + "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, "shadowColor": "#000", "shadowOffset": { diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index 6bc02934ba..587006fa3d 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -113,7 +113,7 @@ exports[`allows customizing Route's type via generics 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -780,7 +780,7 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -1521,7 +1521,7 @@ exports[`hides labels in shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -2394,7 +2394,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -4123,7 +4123,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -5597,7 +5597,7 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -6299,7 +6299,7 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -7293,7 +7293,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -8394,7 +8394,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -9342,7 +9342,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } @@ -10443,7 +10443,7 @@ exports[`renders shifting bottom navigation 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "overflow": "hidden", } } diff --git a/src/components/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/__tests__/__snapshots__/Button.test.tsx.snap index fddcd2bc10..6c34864a74 100644 --- a/src/components/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Button.test.tsx.snap @@ -92,6 +92,9 @@ exports[`renders button with an accessibility hint 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -242,6 +245,9 @@ exports[`renders button with an accessibility label 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -391,6 +397,9 @@ exports[`renders button with button color 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -540,6 +549,9 @@ exports[`renders button with color 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -689,6 +701,9 @@ exports[`renders button with custom testID 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -838,6 +853,9 @@ exports[`renders button with icon 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -1036,6 +1054,9 @@ exports[`renders button with icon in reverse order 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, { "flexDirection": "row-reverse", }, @@ -1236,6 +1257,9 @@ exports[`renders contained contained with mode 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -1386,6 +1410,9 @@ exports[`renders disabled button 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 0.38, + }, undefined, ] } @@ -1422,7 +1449,7 @@ exports[`renders disabled button 1`] = ` undefined, false, { - "color": "rgba(29, 27, 32, 0.38)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500", @@ -1535,6 +1562,9 @@ exports[`renders loading button 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -1824,7 +1854,7 @@ exports[`renders outlined button with mode 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderStyle": "solid", "borderWidth": 1, @@ -1892,6 +1922,9 @@ exports[`renders outlined button with mode 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -2042,6 +2075,9 @@ exports[`renders text button by default 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -2191,6 +2227,9 @@ exports[`renders text button with mode 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } diff --git a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap index f6d6b1379f..4c6358aa1d 100644 --- a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap @@ -100,6 +100,9 @@ exports[`renders chip with close button 1`] = ` { "paddingLeft": 0, }, + { + "opacity": 1, + }, { "paddingRight": 34, }, @@ -395,6 +398,9 @@ exports[`renders chip with custom close button 1`] = ` { "paddingLeft": 0, }, + { + "opacity": 1, + }, { "paddingRight": 34, }, @@ -690,6 +696,9 @@ exports[`renders chip with icon 1`] = ` { "paddingLeft": 0, }, + { + "opacity": 1, + }, { "paddingRight": 0, }, @@ -892,6 +901,9 @@ exports[`renders chip with onPress 1`] = ` { "paddingLeft": 0, }, + { + "opacity": 1, + }, { "paddingRight": 0, }, @@ -972,7 +984,7 @@ exports[`renders outlined disabled chip 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(73, 69, 79, 0.12)", + "borderColor": "rgba(243, 237, 247, 1)", "borderRadius": 8, "borderStyle": "solid", "borderWidth": 1, @@ -1049,6 +1061,9 @@ exports[`renders outlined disabled chip 1`] = ` { "paddingLeft": 0, }, + { + "opacity": 0.38, + }, { "paddingRight": 0, }, @@ -1081,7 +1096,7 @@ exports[`renders outlined disabled chip 1`] = ` "textAlignVertical": "center", }, { - "color": "rgba(29, 27, 32, 0.38)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500", @@ -1111,7 +1126,7 @@ exports[`renders selected chip 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(232, 222, 248)", + "backgroundColor": "rgba(232, 222, 248, 1)", "borderRadius": 8, "shadowColor": "#000", "shadowOffset": { @@ -1128,7 +1143,7 @@ exports[`renders selected chip 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgb(232, 222, 248)", + "backgroundColor": "rgba(232, 222, 248, 1)", "borderColor": "transparent", "borderRadius": 8, "borderStyle": "solid", @@ -1206,6 +1221,9 @@ exports[`renders selected chip 1`] = ` { "paddingLeft": 0, }, + { + "opacity": 1, + }, { "paddingRight": 0, }, diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index 7fde640799..f6071c245c 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -233,7 +233,7 @@ exports[`DataTable.Header renders data table header 1`] = ` }, {}, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, undefined, ], @@ -314,7 +314,7 @@ exports[`DataTable.Header renders data table header 1`] = ` }, {}, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, undefined, ], @@ -366,7 +366,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` "marginRight": 16, }, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, ], ] @@ -404,7 +404,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -481,35 +481,43 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` } testID="icon-button" > - + - chevron-left - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + + @@ -538,7 +546,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -615,35 +623,43 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` } testID="icon-button" > - + - chevron-right - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + + @@ -690,7 +706,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "marginRight": 16, }, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, ], ] @@ -730,7 +746,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -807,35 +823,43 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu } testID="icon-button" > - + - page-first - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-first + + @@ -864,7 +888,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -941,35 +965,43 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu } testID="icon-button" > - + - chevron-left - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + + @@ -998,7 +1030,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -1075,35 +1107,43 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu } testID="icon-button" > - + - chevron-right - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + + @@ -1132,7 +1172,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -1209,35 +1249,43 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu } testID="icon-button" > - + - page-last - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-last + + @@ -1284,7 +1332,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` "marginRight": 16, }, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, ], ] @@ -1324,7 +1372,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -1401,35 +1449,43 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` } testID="icon-button" > - + - chevron-left - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + + @@ -1458,7 +1514,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -1535,35 +1591,43 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` } testID="icon-button" > - + - chevron-right - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + + @@ -1620,7 +1684,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "marginRight": 16, }, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, ], ] @@ -1654,7 +1718,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderStyle": "solid", "borderWidth": 1, @@ -1723,6 +1787,9 @@ exports[`DataTable.Pagination renders data table pagination with options select "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, { "flexDirection": "row-reverse", }, @@ -1853,7 +1920,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "marginRight": 16, }, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, ], ] @@ -1893,7 +1960,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -1970,35 +2037,43 @@ exports[`DataTable.Pagination renders data table pagination with options select } testID="icon-button" > - + - page-first - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-first + + @@ -2027,7 +2102,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -2104,35 +2179,43 @@ exports[`DataTable.Pagination renders data table pagination with options select } testID="icon-button" > - + - chevron-left - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + + @@ -2161,7 +2244,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -2238,35 +2321,43 @@ exports[`DataTable.Pagination renders data table pagination with options select } testID="icon-button" > - + - chevron-right - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + + @@ -2295,7 +2386,7 @@ exports[`DataTable.Pagination renders data table pagination with options select style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -2372,35 +2463,43 @@ exports[`DataTable.Pagination renders data table pagination with options select } testID="icon-button" > - + - page-last - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-last + + @@ -2738,7 +2837,7 @@ exports[`DataTable.Title renders right aligned data table title 1`] = ` }, {}, { - "color": "rgba(29, 27, 32, 0.6)", + "color": "rgba(73, 69, 79, 1)", }, undefined, ], diff --git a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap index 3eb29a581a..7dbef02469 100644 --- a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap @@ -522,180 +522,6 @@ exports[`renders default FAB 1`] = ` `; -exports[`renders disabled FAB 1`] = ` - - - - - - - - plus - - - - - - - -`; - exports[`renders extended FAB 1`] = ` - + - camera - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + camera + + @@ -167,7 +175,7 @@ exports[`renders icon button by default 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -243,35 +251,43 @@ exports[`renders icon button by default 1`] = ` } testID="icon-button" > - + - camera - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + camera + + @@ -303,7 +319,7 @@ exports[`renders icon button with color 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -379,35 +395,43 @@ exports[`renders icon button with color 1`] = ` } testID="icon-button" > - + - camera - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + camera + + @@ -439,7 +463,7 @@ exports[`renders icon button with size 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 23, "borderWidth": 0, "elevation": 0, @@ -515,35 +539,43 @@ exports[`renders icon button with size 1`] = ` } testID="icon-button" > - + - camera - + [ + { + "lineHeight": 30, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + camera + + @@ -575,7 +607,7 @@ exports[`renders icon change animated 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -653,66 +685,74 @@ exports[`renders icon change animated 1`] = ` > - - camera - + + camera + + diff --git a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap index 2ae673d6d3..d194b59877 100644 --- a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap @@ -216,6 +216,9 @@ exports[`renders list item with custom description 1`] = ` { "paddingLeft": 0, }, + { + "opacity": 1, + }, { "paddingRight": 0, }, diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index 875cfd0516..80b6cc6c20 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -16,13 +16,12 @@ exports[`renders list section with custom title style 1`] = ` "scale": 1, }, "colors": { - "backdrop": "rgba(50, 47, 55, 0.4)", "background": "rgba(254, 247, 255, 1)", "elevation": { "level0": "transparent", - "level1": "rgba(255, 255, 255, 1)", - "level2": "rgba(247, 242, 250, 1)", - "level3": "rgba(243, 237, 247, 1)", + "level1": "rgba(247, 242, 250, 1)", + "level2": "rgba(243, 237, 247, 1)", + "level3": "rgba(236, 230, 240, 1)", "level4": "rgba(236, 230, 240, 1)", "level5": "rgba(230, 224, 233, 1)", }, @@ -43,7 +42,6 @@ exports[`renders list section with custom title style 1`] = ` "onSecondaryFixed": "rgba(29, 25, 43, 1)", "onSecondaryFixedVariant": "rgba(74, 68, 88, 1)", "onSurface": "rgba(29, 27, 32, 1)", - "onSurfaceDisabled": "rgba(29, 27, 32, 0.38)", "onSurfaceVariant": "rgba(73, 69, 79, 1)", "onTertiary": "rgba(255, 255, 255, 1)", "onTertiaryContainer": "rgba(49, 17, 29, 1)", @@ -61,6 +59,7 @@ exports[`renders list section with custom title style 1`] = ` "secondaryFixed": "rgba(232, 222, 248, 1)", "secondaryFixedDim": "rgba(204, 194, 220, 1)", "shadow": "rgba(0, 0, 0, 1)", + "stateLayerPressed": "rgba(29, 27, 32, 0.1)", "surface": "rgba(254, 247, 255, 1)", "surfaceBright": "rgba(254, 247, 255, 1)", "surfaceContainer": "rgba(243, 237, 247, 1)", @@ -69,7 +68,6 @@ exports[`renders list section with custom title style 1`] = ` "surfaceContainerLow": "rgba(247, 242, 250, 1)", "surfaceContainerLowest": "rgba(255, 255, 255, 1)", "surfaceDim": "rgba(222, 216, 225, 1)", - "surfaceDisabled": "rgba(29, 27, 32, 0.12)", "surfaceVariant": "rgba(231, 224, 236, 1)", "tertiary": "rgba(125, 82, 96, 1)", "tertiaryContainer": "rgba(255, 216, 228, 1)", @@ -560,13 +558,12 @@ exports[`renders list section with subheader 1`] = ` "scale": 1, }, "colors": { - "backdrop": "rgba(50, 47, 55, 0.4)", "background": "rgba(254, 247, 255, 1)", "elevation": { "level0": "transparent", - "level1": "rgba(255, 255, 255, 1)", - "level2": "rgba(247, 242, 250, 1)", - "level3": "rgba(243, 237, 247, 1)", + "level1": "rgba(247, 242, 250, 1)", + "level2": "rgba(243, 237, 247, 1)", + "level3": "rgba(236, 230, 240, 1)", "level4": "rgba(236, 230, 240, 1)", "level5": "rgba(230, 224, 233, 1)", }, @@ -587,7 +584,6 @@ exports[`renders list section with subheader 1`] = ` "onSecondaryFixed": "rgba(29, 25, 43, 1)", "onSecondaryFixedVariant": "rgba(74, 68, 88, 1)", "onSurface": "rgba(29, 27, 32, 1)", - "onSurfaceDisabled": "rgba(29, 27, 32, 0.38)", "onSurfaceVariant": "rgba(73, 69, 79, 1)", "onTertiary": "rgba(255, 255, 255, 1)", "onTertiaryContainer": "rgba(49, 17, 29, 1)", @@ -605,6 +601,7 @@ exports[`renders list section with subheader 1`] = ` "secondaryFixed": "rgba(232, 222, 248, 1)", "secondaryFixedDim": "rgba(204, 194, 220, 1)", "shadow": "rgba(0, 0, 0, 1)", + "stateLayerPressed": "rgba(29, 27, 32, 0.1)", "surface": "rgba(254, 247, 255, 1)", "surfaceBright": "rgba(254, 247, 255, 1)", "surfaceContainer": "rgba(243, 237, 247, 1)", @@ -613,7 +610,6 @@ exports[`renders list section with subheader 1`] = ` "surfaceContainerLow": "rgba(247, 242, 250, 1)", "surfaceContainerLowest": "rgba(255, 255, 255, 1)", "surfaceDim": "rgba(222, 216, 225, 1)", - "surfaceDisabled": "rgba(29, 27, 32, 0.12)", "surfaceVariant": "rgba(231, 224, 236, 1)", "tertiary": "rgba(125, 82, 96, 1)", "tertiaryContainer": "rgba(255, 216, 228, 1)", @@ -1102,13 +1098,12 @@ exports[`renders list section without subheader 1`] = ` "scale": 1, }, "colors": { - "backdrop": "rgba(50, 47, 55, 0.4)", "background": "rgba(254, 247, 255, 1)", "elevation": { "level0": "transparent", - "level1": "rgba(255, 255, 255, 1)", - "level2": "rgba(247, 242, 250, 1)", - "level3": "rgba(243, 237, 247, 1)", + "level1": "rgba(247, 242, 250, 1)", + "level2": "rgba(243, 237, 247, 1)", + "level3": "rgba(236, 230, 240, 1)", "level4": "rgba(236, 230, 240, 1)", "level5": "rgba(230, 224, 233, 1)", }, @@ -1129,7 +1124,6 @@ exports[`renders list section without subheader 1`] = ` "onSecondaryFixed": "rgba(29, 25, 43, 1)", "onSecondaryFixedVariant": "rgba(74, 68, 88, 1)", "onSurface": "rgba(29, 27, 32, 1)", - "onSurfaceDisabled": "rgba(29, 27, 32, 0.38)", "onSurfaceVariant": "rgba(73, 69, 79, 1)", "onTertiary": "rgba(255, 255, 255, 1)", "onTertiaryContainer": "rgba(49, 17, 29, 1)", @@ -1147,6 +1141,7 @@ exports[`renders list section without subheader 1`] = ` "secondaryFixed": "rgba(232, 222, 248, 1)", "secondaryFixedDim": "rgba(204, 194, 220, 1)", "shadow": "rgba(0, 0, 0, 1)", + "stateLayerPressed": "rgba(29, 27, 32, 0.1)", "surface": "rgba(254, 247, 255, 1)", "surfaceBright": "rgba(254, 247, 255, 1)", "surfaceContainer": "rgba(243, 237, 247, 1)", @@ -1155,7 +1150,6 @@ exports[`renders list section without subheader 1`] = ` "surfaceContainerLow": "rgba(247, 242, 250, 1)", "surfaceContainerLowest": "rgba(255, 255, 255, 1)", "surfaceDim": "rgba(222, 216, 225, 1)", - "surfaceDisabled": "rgba(29, 27, 32, 0.12)", "surfaceVariant": "rgba(231, 224, 236, 1)", "tertiary": "rgba(125, 82, 96, 1)", "tertiaryContainer": "rgba(255, 216, 228, 1)", diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index d3a7c1b1a1..2a09d12a10 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -36,7 +36,7 @@ exports[`renders menu with content styles 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderStyle": "solid", "borderWidth": 1, @@ -104,6 +104,9 @@ exports[`renders menu with content styles 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -259,7 +262,7 @@ exports[`renders menu with content styles 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 4, "borderTopLeftRadius": 0, "borderTopRightRadius": 0, @@ -288,7 +291,7 @@ exports[`renders menu with content styles 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 4, "borderTopLeftRadius": 0, "borderTopRightRadius": 0, @@ -362,6 +365,9 @@ exports[`renders menu with content styles 1`] = ` { "flexDirection": "row", }, + { + "opacity": 1, + }, undefined, ] } @@ -483,6 +489,9 @@ exports[`renders menu with content styles 1`] = ` { "flexDirection": "row", }, + { + "opacity": 1, + }, undefined, ] } @@ -590,7 +599,7 @@ exports[`renders not visible menu 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderStyle": "solid", "borderWidth": 1, @@ -658,6 +667,9 @@ exports[`renders not visible menu 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -755,7 +767,7 @@ exports[`renders visible menu 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderStyle": "solid", "borderWidth": 1, @@ -823,6 +835,9 @@ exports[`renders visible menu 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } @@ -978,7 +993,7 @@ exports[`renders visible menu 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 4, "opacity": 0, "shadowColor": "#000", @@ -1005,7 +1020,7 @@ exports[`renders visible menu 1`] = ` pointerEvents="box-none" style={ { - "backgroundColor": "rgba(247, 242, 250, 1)", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 4, "flex": undefined, "paddingVertical": 8, @@ -1077,6 +1092,9 @@ exports[`renders visible menu 1`] = ` { "flexDirection": "row", }, + { + "opacity": 1, + }, undefined, ] } @@ -1198,6 +1216,9 @@ exports[`renders visible menu 1`] = ` { "flexDirection": "row", }, + { + "opacity": 1, + }, undefined, ] } diff --git a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap index 069b8c854d..e879443bf9 100644 --- a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap @@ -59,6 +59,9 @@ exports[`Menu Item renders menu item 1`] = ` { "flexDirection": "row", }, + { + "opacity": 1, + }, undefined, ] } @@ -220,6 +223,9 @@ exports[`Menu Item renders menu item 1`] = ` { "flexDirection": "row", }, + { + "opacity": 1, + }, undefined, ] } @@ -381,6 +387,9 @@ exports[`Menu Item renders menu item 1`] = ` { "flexDirection": "row", }, + { + "opacity": 0.38, + }, undefined, ] } @@ -403,7 +412,7 @@ exports[`Menu Item renders menu item 1`] = ` style={ [ { - "color": "rgba(29, 27, 32, 0.38)", + "color": "rgba(73, 69, 79, 1)", "fontSize": 24, }, [ @@ -466,7 +475,7 @@ exports[`Menu Item renders menu item 1`] = ` }, [ { - "color": "rgba(29, 27, 32, 0.38)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -542,6 +551,9 @@ exports[`Menu Item renders menu item 1`] = ` { "flexDirection": "row", }, + { + "opacity": 0.38, + }, undefined, ] } @@ -564,7 +576,7 @@ exports[`Menu Item renders menu item 1`] = ` style={ [ { - "color": "rgba(29, 27, 32, 0.38)", + "color": "rgba(73, 69, 79, 1)", "fontSize": 24, }, [ @@ -627,7 +639,7 @@ exports[`Menu Item renders menu item 1`] = ` }, [ { - "color": "rgba(29, 27, 32, 0.38)", + "color": "rgba(29, 27, 32, 1)", "fontFamily": "System", "fontSize": 16, "fontWeight": "400", @@ -703,6 +715,9 @@ exports[`Menu Item renders menu item 1`] = ` { "flexDirection": "row", }, + { + "opacity": 1, + }, undefined, ] } diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap index bbf1c420f7..1954a404ef 100644 --- a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap @@ -5,7 +5,7 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -23,7 +23,7 @@ exports[`activity indicator snapshot test 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -63,7 +63,7 @@ exports[`activity indicator snapshot test 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -140,35 +140,43 @@ exports[`activity indicator snapshot test 1`] = ` } testID="search-bar-icon" > - + - magnify - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + magnify + + @@ -415,7 +423,7 @@ exports[`renders with placeholder 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -433,7 +441,7 @@ exports[`renders with placeholder 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -473,7 +481,7 @@ exports[`renders with placeholder 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -550,35 +558,43 @@ exports[`renders with placeholder 1`] = ` } testID="search-bar-icon" > - + - magnify - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + magnify + + @@ -657,7 +673,7 @@ exports[`renders with placeholder 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -734,35 +750,43 @@ exports[`renders with placeholder 1`] = ` } testID="search-bar-clear-icon" > - + - close - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + + @@ -776,7 +800,7 @@ exports[`renders with text 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "shadowColor": "#000", "shadowOffset": { @@ -794,7 +818,7 @@ exports[`renders with text 1`] = ` style={ { "alignItems": "center", - "backgroundColor": "rgba(243, 237, 247, 1)", + "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, "flex": undefined, "flexDirection": "row", @@ -834,7 +858,7 @@ exports[`renders with text 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -911,35 +935,43 @@ exports[`renders with text 1`] = ` } testID="search-bar-icon" > - + - magnify - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + magnify + + @@ -1014,7 +1046,7 @@ exports[`renders with text 1`] = ` style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -1091,35 +1123,43 @@ exports[`renders with text 1`] = ` } testID="search-bar-clear-icon" > - + - close - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + + diff --git a/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap b/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap index 914c9d8c8a..895061bb52 100644 --- a/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap @@ -91,6 +91,7 @@ exports[`renders segmented button 1`] = ` "paddingVertical": 9, }, { + "opacity": 1, "paddingVertical": 9, }, ] @@ -216,6 +217,7 @@ exports[`renders segmented button 1`] = ` "paddingVertical": 9, }, { + "opacity": 1, "paddingVertical": 9, }, ] diff --git a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap index 401868fabf..ce2a9c8637 100644 --- a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap @@ -410,6 +410,9 @@ exports[`renders snackbar with action button 1`] = ` "flexDirection": "row", "justifyContent": "center", }, + { + "opacity": 1, + }, undefined, ] } diff --git a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap index 090fdaec66..0a7334fa12 100644 --- a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap @@ -21,6 +21,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -65,6 +66,9 @@ exports[`call onPress when affix adornment pressed 1`] = ` { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -206,6 +210,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 16, "paddingRight": 16, "textAlign": "left", @@ -278,6 +283,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` [ { "color": "rgba(73, 69, 79, 1)", + "opacity": 1, }, { "fontFamily": "System", @@ -319,6 +325,7 @@ exports[`correctly applies a component as the text label 1`] = ` "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -363,6 +370,9 @@ exports[`correctly applies a component as the text label 1`] = ` { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -520,6 +530,7 @@ exports[`correctly applies a component as the text label 1`] = ` "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 16, "paddingRight": 16, "textAlign": "left", @@ -561,6 +572,7 @@ exports[`correctly applies cursorColor prop 1`] = ` "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -605,6 +617,9 @@ exports[`correctly applies cursorColor prop 1`] = ` { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -746,6 +761,7 @@ exports[`correctly applies cursorColor prop 1`] = ` "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 16, "paddingRight": 16, "textAlign": "left", @@ -787,6 +803,7 @@ exports[`correctly applies default textAlign based on default RTL 1`] = ` "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -831,6 +848,9 @@ exports[`correctly applies default textAlign based on default RTL 1`] = ` { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -972,6 +992,7 @@ exports[`correctly applies default textAlign based on default RTL 1`] = ` "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 16, "paddingRight": 16, "textAlign": "left", @@ -1050,6 +1071,9 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -1232,6 +1256,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingHorizontal": 16, "textAlign": "left", "textAlignVertical": "top", @@ -1272,6 +1297,7 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -1316,6 +1342,9 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -1457,6 +1486,7 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 16, "paddingRight": 16, "textAlign": "left", @@ -1500,6 +1530,7 @@ exports[`correctly applies textAlign center 1`] = ` "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -1544,6 +1575,9 @@ exports[`correctly applies textAlign center 1`] = ` { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -1685,6 +1719,7 @@ exports[`correctly applies textAlign center 1`] = ` "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 16, "paddingRight": 16, "textAlign": "center", @@ -1726,6 +1761,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -1770,6 +1806,9 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -1911,6 +1950,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 16, "paddingRight": 56, "textAlign": "left", @@ -1952,6 +1992,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm [ { "color": "rgba(73, 69, 79, 1)", + "opacity": 1, }, { "fontFamily": "System", @@ -1980,6 +2021,9 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "position": "absolute", "width": 24, }, + { + "opacity": 1, + }, { "right": 16, "top": 16, @@ -2012,7 +2056,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -2088,35 +2132,43 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm } testID="right-icon-adornment" > - + - heart - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + heart + + @@ -2145,6 +2197,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "bottom": 0, "height": 1, "left": 0, + "opacity": 1, "position": "absolute", "right": 0, "transform": [ @@ -2189,6 +2242,9 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm { "zIndex": 3, }, + { + "opacity": 1, + }, ] } > @@ -2330,6 +2386,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "letterSpacing": 0.15, "lineHeight": undefined, "minWidth": 65, + "opacity": 1, "paddingLeft": 56, "paddingRight": 56, "textAlign": "left", @@ -2360,6 +2417,9 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "position": "absolute", "width": 24, }, + { + "opacity": 1, + }, { "left": 16, "top": 16, @@ -2392,7 +2452,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm style={ { "backgroundColor": "transparent", - "borderColor": "rgba(121, 116, 126, 1)", + "borderColor": "rgba(202, 196, 208, 1)", "borderRadius": 20, "borderWidth": 0, "elevation": 0, @@ -2468,35 +2528,43 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm } testID="left-icon-adornment" > - + - heart - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + heart + + @@ -2522,6 +2590,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm [ { "color": "rgba(73, 69, 79, 1)", + "opacity": 1, }, { "fontFamily": "System", diff --git a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap index 0e475cd073..a8feb453e3 100644 --- a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap @@ -5,7 +5,7 @@ exports[`renders disabled toggle button 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(29, 25, 43, 0.12)", + "backgroundColor": "rgba(230, 224, 233, 1)", "borderRadius": 4, "height": 42, "margin": 0, @@ -25,7 +25,7 @@ exports[`renders disabled toggle button 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(29, 25, 43, 0.12)", + "backgroundColor": "rgba(230, 224, 233, 1)", "borderColor": "rgba(121, 116, 126, 1)", "borderRadius": 4, "borderWidth": 0, @@ -105,35 +105,43 @@ exports[`renders disabled toggle button 1`] = ` } testID="icon-button" > - + - heart - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + heart + + @@ -144,7 +152,7 @@ exports[`renders toggle button 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(29, 25, 43, 0.12)", + "backgroundColor": "rgba(230, 224, 233, 1)", "borderRadius": 4, "height": 42, "margin": 0, @@ -164,7 +172,7 @@ exports[`renders toggle button 1`] = ` collapsable={false} style={ { - "backgroundColor": "rgba(29, 25, 43, 0.12)", + "backgroundColor": "rgba(230, 224, 233, 1)", "borderColor": "rgba(121, 116, 126, 1)", "borderRadius": 4, "borderWidth": 0, @@ -239,35 +247,43 @@ exports[`renders toggle button 1`] = ` } testID="icon-button" > - + - heart - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + heart + + @@ -278,7 +294,7 @@ exports[`renders unchecked toggle button 1`] = ` collapsable={false} style={ { - "backgroundColor": "transparent", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 4, "height": 42, "margin": 0, @@ -298,7 +314,7 @@ exports[`renders unchecked toggle button 1`] = ` collapsable={false} style={ { - "backgroundColor": "transparent", + "backgroundColor": "rgba(243, 237, 247, 1)", "borderColor": "rgba(121, 116, 126, 1)", "borderRadius": 4, "borderWidth": 0, @@ -378,35 +394,43 @@ exports[`renders unchecked toggle button 1`] = ` } testID="icon-button" > - + - heart - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + heart + + diff --git a/src/core/__tests__/theming.test.tsx b/src/core/__tests__/theming.test.tsx index 7b069cd482..f9efe7752e 100644 --- a/src/core/__tests__/theming.test.tsx +++ b/src/core/__tests__/theming.test.tsx @@ -91,7 +91,7 @@ describe('adaptNavigationTheme', () => { ...NavigationLightTheme.colors, primary: MD3LightTheme.colors.primary, background: MD3LightTheme.colors.background, - card: MD3LightTheme.colors.elevation.level2, + card: MD3LightTheme.colors.surfaceContainer, text: MD3LightTheme.colors.onSurface, border: MD3LightTheme.colors.outline, notification: MD3LightTheme.colors.error, @@ -103,7 +103,7 @@ describe('adaptNavigationTheme', () => { ...NavigationDarkTheme.colors, primary: MD3DarkTheme.colors.primary, background: MD3DarkTheme.colors.background, - card: MD3DarkTheme.colors.elevation.level2, + card: MD3DarkTheme.colors.surfaceContainer, text: MD3DarkTheme.colors.onSurface, border: MD3DarkTheme.colors.outline, notification: MD3DarkTheme.colors.error, @@ -125,7 +125,7 @@ describe('adaptNavigationTheme', () => { ...NavigationLightTheme.colors, primary: colors.primary, background: colors.background, - card: colors.elevation.level2, + card: colors.surfaceContainer, text: colors.onSurface, border: colors.outline, notification: colors.error, @@ -146,7 +146,7 @@ describe('adaptNavigationTheme', () => { ...NavigationDarkTheme.colors, primary: colors.primary, background: colors.background, - card: colors.elevation.level2, + card: colors.surfaceContainer, text: colors.onSurface, border: colors.outline, notification: colors.error, @@ -167,7 +167,7 @@ describe('adaptNavigationTheme', () => { ...NavigationCustomLightTheme.colors, primary: colors.primary, background: colors.background, - card: colors.elevation.level2, + card: colors.surfaceContainer, text: colors.onSurface, border: colors.outline, notification: colors.error, @@ -191,7 +191,7 @@ describe('adaptNavigationTheme', () => { ...NavigationLightTheme.colors, primary: colors.primary, background: colors.background, - card: colors.elevation.level2, + card: colors.surfaceContainer, text: colors.onSurface, border: colors.outline, notification: colors.error, @@ -213,7 +213,7 @@ describe('adaptNavigationTheme', () => { ...NavigationDarkTheme.colors, primary: colors.primary, background: colors.background, - card: colors.elevation.level2, + card: colors.surfaceContainer, text: colors.onSurface, border: colors.outline, notification: colors.error, @@ -232,7 +232,7 @@ describe('adaptNavigationTheme', () => { ...NavigationThemeWithFonts.colors, primary: MD3LightTheme.colors.primary, background: MD3LightTheme.colors.background, - card: MD3LightTheme.colors.elevation.level2, + card: MD3LightTheme.colors.surfaceContainer, text: MD3LightTheme.colors.onSurface, border: MD3LightTheme.colors.outline, notification: MD3LightTheme.colors.error, diff --git a/src/core/theming.tsx b/src/core/theming.tsx index 042a83d960..04649515c6 100644 --- a/src/core/theming.tsx +++ b/src/core/theming.tsx @@ -1,15 +1,9 @@ import type { ComponentType } from 'react'; import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; -import color from 'color'; import { MD3DarkTheme, MD3LightTheme } from '../styles/themes'; -import type { - InternalTheme, - MD3Theme, - MD3AndroidColors, - NavigationTheme, -} from '../types'; +import type { InternalTheme, MD3Theme, NavigationTheme } from '../types'; export const DefaultTheme = MD3LightTheme; @@ -105,7 +99,7 @@ const getAdaptedTheme = ( ...theme.colors, primary: materialTheme.colors.primary, background: materialTheme.colors.background, - card: materialTheme.colors.elevation.level2, + card: materialTheme.colors.surfaceContainer, text: materialTheme.colors.onSurface, border: materialTheme.colors.outline, notification: materialTheme.colors.error, @@ -142,19 +136,3 @@ const getAdaptedTheme = ( return base; }; - -export const getDynamicThemeElevations = (scheme: MD3AndroidColors) => { - const elevationValues = ['transparent', 0.05, 0.08, 0.11, 0.12, 0.14]; - return elevationValues.reduce((elevations, elevationValue, index) => { - return { - ...elevations, - [`level${index}`]: - index === 0 - ? elevationValue - : color(scheme.surface) - .mix(color(scheme.primary), elevationValue as number) - .rgb() - .string(), - }; - }, {}); -}; diff --git a/src/styles/themes/DynamicTheme.android.tsx b/src/styles/themes/DynamicTheme.android.tsx new file mode 100644 index 0000000000..b16b4bd040 --- /dev/null +++ b/src/styles/themes/DynamicTheme.android.tsx @@ -0,0 +1,502 @@ +import { Platform, PlatformColor } from 'react-native'; + +import { MD3DarkTheme } from './v3/DarkTheme'; +import { MD3LightTheme } from './v3/LightTheme'; +import type { MD3Theme } from '../../types'; + +const isApi34 = (Platform.Version as number) >= 34; +const isApi31 = (Platform.Version as number) >= 31; + +const ac = (name: string) => + PlatformColor(`@android:color/${name}`) as unknown as string; + +/** + * Picks the correct color value for the current Android API level. + * - API 34+: uses the named role resource (system_*_light/dark) + * - API 31-33: uses the tonal accent resource (system_accent*_NNN), or ref + * - API < 31: uses the reference palette string from the base theme + * @see https://github.com/material-components/material-components-android/blob/master/docs/theming/Color.md + */ +const pick = (api34: string, api31: string, ref: string): string => + isApi34 ? ac(api34) : isApi31 && api31 != null ? ac(api31) : ref; + +// Known limitation: surface/container roles on API 31-33 use +// @color/m3_ref_palette_dynamic_neutral_variant* (MCL resources that require a +// native DynamicColors setup). No @android:color/ equivalent exists for those +// slots. Reference palette values are used as fallback on API 31-33. + +const lightColors = { + primary: pick( + 'system_primary_light', + 'system_accent1_600', + MD3LightTheme.colors.primary + ), + onPrimary: pick( + 'system_on_primary_light', + 'system_accent1_0', + MD3LightTheme.colors.onPrimary + ), + primaryContainer: pick( + 'system_primary_container_light', + 'system_accent1_100', + MD3LightTheme.colors.primaryContainer + ), + onPrimaryContainer: pick( + 'system_on_primary_container_light', + 'system_accent1_900', + MD3LightTheme.colors.onPrimaryContainer + ), + inversePrimary: pick( + 'system_primary_dark', + 'system_accent1_200', + MD3LightTheme.colors.inversePrimary + ), + secondary: pick( + 'system_secondary_light', + 'system_accent2_600', + MD3LightTheme.colors.secondary + ), + onSecondary: pick( + 'system_on_secondary_light', + 'system_accent2_0', + MD3LightTheme.colors.onSecondary + ), + secondaryContainer: pick( + 'system_secondary_container_light', + 'system_accent2_100', + MD3LightTheme.colors.secondaryContainer + ), + onSecondaryContainer: pick( + 'system_on_secondary_container_light', + 'system_accent2_900', + MD3LightTheme.colors.onSecondaryContainer + ), + tertiary: pick( + 'system_tertiary_light', + 'system_accent3_600', + MD3LightTheme.colors.tertiary + ), + onTertiary: pick( + 'system_on_tertiary_light', + 'system_accent3_0', + MD3LightTheme.colors.onTertiary + ), + tertiaryContainer: pick( + 'system_tertiary_container_light', + 'system_accent3_100', + MD3LightTheme.colors.tertiaryContainer + ), + onTertiaryContainer: pick( + 'system_on_tertiary_container_light', + 'system_accent3_900', + MD3LightTheme.colors.onTertiaryContainer + ), + error: pick( + 'system_error_light', + MD3LightTheme.colors.error, + MD3LightTheme.colors.error + ), + onError: pick( + 'system_on_error_light', + MD3LightTheme.colors.onError, + MD3LightTheme.colors.onError + ), + errorContainer: pick( + 'system_error_container_light', + MD3LightTheme.colors.errorContainer, + MD3LightTheme.colors.errorContainer + ), + onErrorContainer: pick( + 'system_on_error_container_light', + MD3LightTheme.colors.onErrorContainer, + MD3LightTheme.colors.onErrorContainer + ), + onSurface: pick( + 'system_on_surface_light', + 'system_neutral1_900', + MD3LightTheme.colors.onSurface + ), + onBackground: pick( + 'system_on_background_light', + 'system_neutral1_900', + MD3LightTheme.colors.onBackground + ), + onSurfaceVariant: pick( + 'system_on_surface_variant_light', + 'system_neutral2_700', + MD3LightTheme.colors.onSurfaceVariant + ), + outline: pick( + 'system_outline_light', + 'system_neutral2_500', + MD3LightTheme.colors.outline + ), + outlineVariant: pick( + 'system_outline_variant_light', + 'system_neutral2_200', + MD3LightTheme.colors.outlineVariant + ), + inverseSurface: pick( + 'system_surface_dark', + 'system_neutral1_800', + MD3LightTheme.colors.inverseSurface + ), + inverseOnSurface: pick( + 'system_on_surface_dark', + 'system_neutral1_50', + MD3LightTheme.colors.inverseOnSurface + ), + surfaceContainerLowest: pick( + 'system_surface_container_lowest_light', + 'system_neutral2_0', + MD3LightTheme.colors.surfaceContainerLowest + ), + surfaceContainerLow: pick( + 'system_surface_container_low_light', + MD3LightTheme.colors.surfaceContainerLow, + MD3LightTheme.colors.surfaceContainerLow + ), + surfaceContainerHighest: pick( + 'system_surface_container_highest_light', + 'system_neutral2_100', + MD3LightTheme.colors.surfaceContainerHighest + ), + surface: pick( + 'system_surface_light', + MD3LightTheme.colors.surface, + MD3LightTheme.colors.surface + ), + surfaceDim: pick( + 'system_surface_dim_light', + MD3LightTheme.colors.surfaceDim, + MD3LightTheme.colors.surfaceDim + ), + surfaceBright: pick( + 'system_surface_bright_light', + MD3LightTheme.colors.surfaceBright, + MD3LightTheme.colors.surfaceBright + ), + surfaceContainer: pick( + 'system_surface_container_light', + MD3LightTheme.colors.surfaceContainer, + MD3LightTheme.colors.surfaceContainer + ), + surfaceContainerHigh: pick( + 'system_surface_container_high_light', + MD3LightTheme.colors.surfaceContainerHigh, + MD3LightTheme.colors.surfaceContainerHigh + ), + background: pick( + 'system_background_light', + MD3LightTheme.colors.background, + MD3LightTheme.colors.background + ), + surfaceVariant: pick( + 'system_surface_variant_light', + MD3LightTheme.colors.surfaceVariant, + MD3LightTheme.colors.surfaceVariant + ), + primaryFixed: pick( + 'system_primary_fixed', + 'system_accent1_100', + MD3LightTheme.colors.primaryFixed + ), + primaryFixedDim: pick( + 'system_primary_fixed_dim', + 'system_accent1_200', + MD3LightTheme.colors.primaryFixedDim + ), + onPrimaryFixed: pick( + 'system_on_primary_fixed', + 'system_accent1_900', + MD3LightTheme.colors.onPrimaryFixed + ), + onPrimaryFixedVariant: pick( + 'system_on_primary_fixed_variant', + 'system_accent1_700', + MD3LightTheme.colors.onPrimaryFixedVariant + ), + secondaryFixed: pick( + 'system_secondary_fixed', + 'system_accent2_100', + MD3LightTheme.colors.secondaryFixed + ), + secondaryFixedDim: pick( + 'system_secondary_fixed_dim', + 'system_accent2_200', + MD3LightTheme.colors.secondaryFixedDim + ), + onSecondaryFixed: pick( + 'system_on_secondary_fixed', + 'system_accent2_900', + MD3LightTheme.colors.onSecondaryFixed + ), + onSecondaryFixedVariant: pick( + 'system_on_secondary_fixed_variant', + 'system_accent2_700', + MD3LightTheme.colors.onSecondaryFixedVariant + ), + tertiaryFixed: pick( + 'system_tertiary_fixed', + 'system_accent3_100', + MD3LightTheme.colors.tertiaryFixed + ), + tertiaryFixedDim: pick( + 'system_tertiary_fixed_dim', + 'system_accent3_200', + MD3LightTheme.colors.tertiaryFixedDim + ), + onTertiaryFixed: pick( + 'system_on_tertiary_fixed', + 'system_accent3_900', + MD3LightTheme.colors.onTertiaryFixed + ), + onTertiaryFixedVariant: pick( + 'system_on_tertiary_fixed_variant', + 'system_accent3_700', + MD3LightTheme.colors.onTertiaryFixedVariant + ), +}; + +const darkColors = { + primary: pick( + 'system_primary_dark', + 'system_accent1_200', + MD3DarkTheme.colors.primary + ), + onPrimary: pick( + 'system_on_primary_dark', + 'system_accent1_800', + MD3DarkTheme.colors.onPrimary + ), + primaryContainer: pick( + 'system_primary_container_dark', + 'system_accent1_700', + MD3DarkTheme.colors.primaryContainer + ), + onPrimaryContainer: pick( + 'system_on_primary_container_dark', + 'system_accent1_100', + MD3DarkTheme.colors.onPrimaryContainer + ), + inversePrimary: pick( + 'system_primary_light', + 'system_accent1_600', + MD3DarkTheme.colors.inversePrimary + ), + secondary: pick( + 'system_secondary_dark', + 'system_accent2_200', + MD3DarkTheme.colors.secondary + ), + onSecondary: pick( + 'system_on_secondary_dark', + 'system_accent2_800', + MD3DarkTheme.colors.onSecondary + ), + secondaryContainer: pick( + 'system_secondary_container_dark', + 'system_accent2_700', + MD3DarkTheme.colors.secondaryContainer + ), + onSecondaryContainer: pick( + 'system_on_secondary_container_dark', + 'system_accent2_100', + MD3DarkTheme.colors.onSecondaryContainer + ), + tertiary: pick( + 'system_tertiary_dark', + 'system_accent3_200', + MD3DarkTheme.colors.tertiary + ), + onTertiary: pick( + 'system_on_tertiary_dark', + 'system_accent3_800', + MD3DarkTheme.colors.onTertiary + ), + tertiaryContainer: pick( + 'system_tertiary_container_dark', + 'system_accent3_700', + MD3DarkTheme.colors.tertiaryContainer + ), + onTertiaryContainer: pick( + 'system_on_tertiary_container_dark', + 'system_accent3_100', + MD3DarkTheme.colors.onTertiaryContainer + ), + error: pick( + 'system_error_dark', + MD3DarkTheme.colors.error, + MD3DarkTheme.colors.error + ), + onError: pick( + 'system_on_error_dark', + MD3DarkTheme.colors.onError, + MD3DarkTheme.colors.onError + ), + errorContainer: pick( + 'system_error_container_dark', + MD3DarkTheme.colors.errorContainer, + MD3DarkTheme.colors.errorContainer + ), + onErrorContainer: pick( + 'system_on_error_container_dark', + MD3DarkTheme.colors.onErrorContainer, + MD3DarkTheme.colors.onErrorContainer + ), + onSurface: pick( + 'system_on_surface_dark', + 'system_neutral1_100', + MD3DarkTheme.colors.onSurface + ), + onBackground: pick( + 'system_on_background_dark', + 'system_neutral1_100', + MD3DarkTheme.colors.onBackground + ), + onSurfaceVariant: pick( + 'system_on_surface_variant_dark', + 'system_neutral2_200', + MD3DarkTheme.colors.onSurfaceVariant + ), + outline: pick( + 'system_outline_dark', + 'system_neutral2_400', + MD3DarkTheme.colors.outline + ), + outlineVariant: pick( + 'system_outline_variant_dark', + 'system_neutral2_700', + MD3DarkTheme.colors.outlineVariant + ), + inverseSurface: pick( + 'system_surface_light', + 'system_neutral1_100', + MD3DarkTheme.colors.inverseSurface + ), + inverseOnSurface: pick( + 'system_on_surface_light', + 'system_neutral1_800', + MD3DarkTheme.colors.inverseOnSurface + ), + surfaceContainerLowest: pick( + 'system_surface_container_lowest_dark', + MD3DarkTheme.colors.surfaceContainerLowest, + MD3DarkTheme.colors.surfaceContainerLowest + ), + surfaceContainerLow: pick( + 'system_surface_container_low_dark', + 'system_neutral2_900', + MD3DarkTheme.colors.surfaceContainerLow + ), + surfaceContainerHighest: pick( + 'system_surface_container_highest_dark', + MD3DarkTheme.colors.surfaceContainerHighest, + MD3DarkTheme.colors.surfaceContainerHighest + ), + surface: pick( + 'system_surface_dark', + MD3DarkTheme.colors.surface, + MD3DarkTheme.colors.surface + ), + surfaceDim: pick( + 'system_surface_dim_dark', + MD3DarkTheme.colors.surfaceDim, + MD3DarkTheme.colors.surfaceDim + ), + surfaceBright: pick( + 'system_surface_bright_dark', + MD3DarkTheme.colors.surfaceBright, + MD3DarkTheme.colors.surfaceBright + ), + surfaceContainer: pick( + 'system_surface_container_dark', + MD3DarkTheme.colors.surfaceContainer, + MD3DarkTheme.colors.surfaceContainer + ), + surfaceContainerHigh: pick( + 'system_surface_container_high_dark', + MD3DarkTheme.colors.surfaceContainerHigh, + MD3DarkTheme.colors.surfaceContainerHigh + ), + background: pick( + 'system_background_dark', + MD3DarkTheme.colors.background, + MD3DarkTheme.colors.background + ), + surfaceVariant: pick( + 'system_surface_variant_dark', + MD3DarkTheme.colors.surfaceVariant, + MD3DarkTheme.colors.surfaceVariant + ), + primaryFixed: pick( + 'system_primary_fixed', + 'system_accent1_100', + MD3DarkTheme.colors.primaryFixed + ), + primaryFixedDim: pick( + 'system_primary_fixed_dim', + 'system_accent1_200', + MD3DarkTheme.colors.primaryFixedDim + ), + onPrimaryFixed: pick( + 'system_on_primary_fixed', + 'system_accent1_900', + MD3DarkTheme.colors.onPrimaryFixed + ), + onPrimaryFixedVariant: pick( + 'system_on_primary_fixed_variant', + 'system_accent1_700', + MD3DarkTheme.colors.onPrimaryFixedVariant + ), + secondaryFixed: pick( + 'system_secondary_fixed', + 'system_accent2_100', + MD3DarkTheme.colors.secondaryFixed + ), + secondaryFixedDim: pick( + 'system_secondary_fixed_dim', + 'system_accent2_200', + MD3DarkTheme.colors.secondaryFixedDim + ), + onSecondaryFixed: pick( + 'system_on_secondary_fixed', + 'system_accent2_900', + MD3DarkTheme.colors.onSecondaryFixed + ), + onSecondaryFixedVariant: pick( + 'system_on_secondary_fixed_variant', + 'system_accent2_700', + MD3DarkTheme.colors.onSecondaryFixedVariant + ), + tertiaryFixed: pick( + 'system_tertiary_fixed', + 'system_accent3_100', + MD3DarkTheme.colors.tertiaryFixed + ), + tertiaryFixedDim: pick( + 'system_tertiary_fixed_dim', + 'system_accent3_200', + MD3DarkTheme.colors.tertiaryFixedDim + ), + onTertiaryFixed: pick( + 'system_on_tertiary_fixed', + 'system_accent3_900', + MD3DarkTheme.colors.onTertiaryFixed + ), + onTertiaryFixedVariant: pick( + 'system_on_tertiary_fixed_variant', + 'system_accent3_700', + MD3DarkTheme.colors.onTertiaryFixedVariant + ), +}; + +export const DynamicLightTheme: MD3Theme = { + ...MD3LightTheme, + colors: { ...MD3LightTheme.colors, ...lightColors }, +}; + +export const DynamicDarkTheme: MD3Theme = { + ...MD3DarkTheme, + colors: { ...MD3DarkTheme.colors, ...darkColors }, +}; diff --git a/src/styles/themes/DynamicTheme.tsx b/src/styles/themes/DynamicTheme.tsx new file mode 100644 index 0000000000..6669c63a45 --- /dev/null +++ b/src/styles/themes/DynamicTheme.tsx @@ -0,0 +1,2 @@ +export { MD3DarkTheme as MD3DynamicDarkTheme } from './v3/DarkTheme'; +export { MD3LightTheme as MD3DynamicLightTheme } from './v3/LightTheme'; diff --git a/src/styles/themes/index.ts b/src/styles/themes/index.ts index 39bcb6648c..f992ef7e73 100644 --- a/src/styles/themes/index.ts +++ b/src/styles/themes/index.ts @@ -1,2 +1,3 @@ export { MD3LightTheme } from './v3/LightTheme'; export { MD3DarkTheme } from './v3/DarkTheme'; +export { DynamicLightTheme, DynamicDarkTheme } from './DynamicTheme'; diff --git a/src/styles/themes/v3/DarkTheme.tsx b/src/styles/themes/v3/DarkTheme.tsx index d87404942a..c9aa34305b 100644 --- a/src/styles/themes/v3/DarkTheme.tsx +++ b/src/styles/themes/v3/DarkTheme.tsx @@ -1,10 +1,10 @@ import color from 'color'; import { MD3LightTheme } from './LightTheme'; -import { MD3Colors, tokens } from './tokens'; +import { tokens } from './tokens'; import type { MD3Theme } from '../../../types'; -const { palette, opacity } = tokens.md.ref; +const { palette, stateOpacity } = tokens.md.ref; export const MD3DarkTheme: MD3Theme = { ...MD3LightTheme, @@ -26,10 +26,6 @@ export const MD3DarkTheme: MD3Theme = { surfaceContainerHigh: palette.neutral17, surfaceContainerHighest: palette.neutral22, surfaceVariant: palette.neutralVariant30, - surfaceDisabled: color(palette.neutral90) - .alpha(opacity.level2) - .rgb() - .string(), background: palette.neutral6, error: palette.error80, errorContainer: palette.error30, @@ -41,10 +37,6 @@ export const MD3DarkTheme: MD3Theme = { onTertiaryContainer: palette.tertiary90, onSurface: palette.neutral90, onSurfaceVariant: palette.neutralVariant80, - onSurfaceDisabled: color(palette.neutral90) - .alpha(opacity.level4) - .rgb() - .string(), onError: palette.error20, onErrorContainer: palette.error80, onBackground: palette.neutral90, @@ -67,12 +59,15 @@ export const MD3DarkTheme: MD3Theme = { onTertiaryFixedVariant: palette.tertiary30, shadow: palette.neutral0, scrim: palette.neutral0, - backdrop: color(MD3Colors.neutralVariant20).alpha(0.4).rgb().string(), + stateLayerPressed: color(palette.neutral90) + .alpha(stateOpacity.pressed) + .rgb() + .string(), elevation: { level0: 'transparent', - level1: palette.neutral4, - level2: palette.neutral10, - level3: palette.neutral12, + level1: palette.neutral10, + level2: palette.neutral12, + level3: palette.neutral17, level4: palette.neutral17, level5: palette.neutral22, }, diff --git a/src/styles/themes/v3/LightTheme.tsx b/src/styles/themes/v3/LightTheme.tsx index 9782f3b90e..92924717f1 100644 --- a/src/styles/themes/v3/LightTheme.tsx +++ b/src/styles/themes/v3/LightTheme.tsx @@ -1,10 +1,10 @@ import color from 'color'; -import { MD3Colors, tokens } from './tokens'; +import { tokens } from './tokens'; import type { MD3Theme } from '../../../types'; import configureFonts from '../../fonts'; -const { palette, opacity } = tokens.md.ref; +const { palette, stateOpacity } = tokens.md.ref; export const MD3LightTheme: MD3Theme = { dark: false, @@ -25,10 +25,6 @@ export const MD3LightTheme: MD3Theme = { surfaceContainerHigh: palette.neutral92, surfaceContainerHighest: palette.neutral90, surfaceVariant: palette.neutralVariant90, - surfaceDisabled: color(palette.neutral10) - .alpha(opacity.level2) - .rgb() - .string(), background: palette.neutral98, error: palette.error40, errorContainer: palette.error90, @@ -40,10 +36,6 @@ export const MD3LightTheme: MD3Theme = { onTertiaryContainer: palette.tertiary10, onSurface: palette.neutral10, onSurfaceVariant: palette.neutralVariant30, - onSurfaceDisabled: color(palette.neutral10) - .alpha(opacity.level4) - .rgb() - .string(), onError: palette.error100, onErrorContainer: palette.error10, onBackground: palette.neutral10, @@ -66,12 +58,15 @@ export const MD3LightTheme: MD3Theme = { onTertiaryFixedVariant: palette.tertiary30, shadow: palette.neutral0, scrim: palette.neutral0, - backdrop: color(MD3Colors.neutralVariant20).alpha(0.4).rgb().string(), + stateLayerPressed: color(palette.neutral10) + .alpha(stateOpacity.pressed) + .rgb() + .string(), elevation: { level0: 'transparent', - level1: palette.neutral100, - level2: palette.neutral96, - level3: palette.neutral94, + level1: palette.neutral96, + level2: palette.neutral94, + level3: palette.neutral92, level4: palette.neutral92, level5: palette.neutral90, }, diff --git a/src/styles/themes/v3/tokens.tsx b/src/styles/themes/v3/tokens.tsx index 6eb27445fd..12486aca53 100644 --- a/src/styles/themes/v3/tokens.tsx +++ b/src/styles/themes/v3/tokens.tsx @@ -116,12 +116,19 @@ const ref = { weightMedium: '500' as Font['fontWeight'], }, - opacity: { - level1: 0.08, - level2: 0.12, - level3: 0.16, - level4: 0.38, + /** State layers opacity + * @see https://m3.material.io/foundations/interaction/states/state-layers + */ + stateOpacity: { + dragged: 0.16, + pressed: 0.1, + focus: 0.1, + hover: 0.08, + disabled: 0.38, + enabled: 1.0, }, + + scrimAlpha: 0.32, }; const regularType = { diff --git a/src/types.tsx b/src/types.tsx index b7a0317e80..9206ec1555 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -44,7 +44,6 @@ export type MD3Colors = { surfaceContainerHigh: string; surfaceContainerHighest: string; surfaceVariant: string; - surfaceDisabled: string; background: string; error: string; errorContainer: string; @@ -56,7 +55,6 @@ export type MD3Colors = { onTertiaryContainer: string; onSurface: string; onSurfaceVariant: string; - onSurfaceDisabled: string; onError: string; onErrorContainer: string; onBackground: string; @@ -79,44 +77,15 @@ export type MD3Colors = { onTertiaryFixedVariant: string; shadow: string; scrim: string; - backdrop: string; + /** Pre-computed state layer color at press opacity (0.10). + * Used for ripple effects. Avoids runtime alpha manipulation + * which is incompatible with PlatformColor on Android. + * TODO: revisit after https://github.com/facebook/react-native/pull/56395 + * @see https://m3.material.io/foundations/interaction/states/state-layers */ + stateLayerPressed: string; elevation: MD3ElevationColors; }; -export type MD3AndroidColors = { - primary: number; - primaryContainer: number; - secondary: number; - secondaryContainer: number; - tertiary: number; - tertiaryContainer: number; - surface: number; - surfaceVariant: number; - background: number; - error: number; - errorContainer: number; - onPrimary: number; - onPrimaryContainer: number; - onSecondary: number; - onSecondaryContainer: number; - onTertiary: number; - onTertiaryContainer: number; - onSurface: number; - onSurfaceVariant: number; - onError: number; - onErrorContainer: number; - onBackground: number; - outline: number; - outlineVariant: number; - inverseSurface: number; - inverseOnSurface: number; - inversePrimary: number; - shadow: number; - scrim: number; -}; - -export type MD3Palette = {}; - export type ThemeProp = $DeepPartial; export type ThemeBase = { diff --git a/yarn.lock b/yarn.lock index 3920e64583..9696c756fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3887,13 +3887,6 @@ __metadata: languageName: node linkType: hard -"@material/material-color-utilities@npm:^0.2.7": - version: 0.2.7 - resolution: "@material/material-color-utilities@npm:0.2.7" - checksum: 10c0/7734f8d7cffe6a92d47fcca82b4846e39ffd79be9c057f3299466696dcdee5379b8a8a3aa6a9692d40bd6900905f689105d9a8ce47882ab22348df10621f70cd - languageName: node - linkType: hard - "@mdx-js/mdx@npm:^0.20.3": version: 0.20.3 resolution: "@mdx-js/mdx@npm:0.20.3" @@ -4317,20 +4310,6 @@ __metadata: languageName: node linkType: hard -"@pchmn/expo-material3-theme@npm:^1.3.2": - version: 1.3.2 - resolution: "@pchmn/expo-material3-theme@npm:1.3.2" - dependencies: - "@material/material-color-utilities": "npm:^0.2.7" - color: "npm:^4.2.3" - peerDependencies: - expo: "*" - react: "*" - react-native: "*" - checksum: 10c0/89aa0a65b04f4a7f709c35bbdc2f7830dff73e0335877d36a091c55fb174ce82e4b2c67690657978b03fc563ab4c20565ded0ed7faec94d0a384a9c5085dfe19 - languageName: node - linkType: hard - "@polka/url@npm:^1.0.0-next.24": version: 1.0.0-next.29 resolution: "@polka/url@npm:1.0.0-next.29" @@ -20063,7 +20042,6 @@ __metadata: "@babel/core": "npm:^7.25.2" "@expo/vector-icons": "npm:^15.0.2" "@expo/webpack-config": "npm:~19.0.1" - "@pchmn/expo-material3-theme": "npm:^1.3.2" "@react-native-async-storage/async-storage": "npm:2.2.0" "@react-native-masked-view/masked-view": "npm:0.3.2" "@react-navigation/bottom-tabs": "npm:^7.3.10" From f7a2a6526c1e6899d05c9ba30ae787b17efa6b5b Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Mon, 11 May 2026 08:52:52 +0300 Subject: [PATCH 06/94] refactor: remove MD3 prefix and use deprecated aliases (#4910) * refactor: remove MD3 prefix and use deprecated aliases * refactor: strip remaining v3 version markers --- .../__fixtures__/rewrite-imports/output.js | 2 +- src/components/ActivityIndicator.tsx | 4 +- src/components/Appbar/Appbar.tsx | 4 +- src/components/Appbar/AppbarAction.tsx | 4 +- src/components/Appbar/AppbarContent.tsx | 8 +- src/components/Appbar/utils.ts | 6 +- src/components/Avatar/AvatarIcon.tsx | 2 +- src/components/Avatar/AvatarText.tsx | 2 +- src/components/Banner.tsx | 4 +- .../BottomNavigation/BottomNavigationBar.tsx | 6 +- src/components/BottomNavigation/utils.ts | 8 +- src/components/Button/Button.tsx | 4 +- src/components/Button/utils.tsx | 10 +- src/components/Card/CardCover.tsx | 2 +- src/components/Card/CardTitle.tsx | 6 +- src/components/Card/utils.tsx | 6 +- src/components/Checkbox/CheckboxItem.tsx | 6 +- src/components/Checkbox/utils.ts | 2 +- src/components/Chip/Chip.tsx | 8 +- src/components/Chip/helpers.tsx | 6 +- src/components/Dialog/Dialog.tsx | 4 +- src/components/Dialog/DialogIcon.tsx | 4 +- src/components/Dialog/DialogScrollArea.tsx | 4 +- src/components/Dialog/DialogTitle.tsx | 4 +- src/components/Drawer/DrawerSection.tsx | 4 +- src/components/FAB/AnimatedFAB.tsx | 4 +- src/components/HelperText/utils.ts | 2 +- src/components/Icon.tsx | 4 +- src/components/IconButton/IconButton.tsx | 4 +- src/components/IconButton/utils.ts | 2 +- src/components/List/ListIcon.tsx | 8 +- src/components/List/ListImage.tsx | 2 +- src/components/List/ListSection.tsx | 4 +- src/components/MaterialCommunityIcon.tsx | 2 +- src/components/Menu/Menu.tsx | 8 +- src/components/Menu/MenuItem.tsx | 4 +- src/components/Menu/utils.ts | 2 +- src/components/Modal.tsx | 2 +- src/components/ProgressBar.tsx | 4 +- .../RadioButton/RadioButtonItem.tsx | 6 +- src/components/Searchbar.tsx | 4 +- src/components/SegmentedButtons/utils.ts | 2 +- src/components/Snackbar.tsx | 4 +- src/components/Surface.tsx | 16 +- src/components/Switch/utils.ts | 2 +- src/components/TextInput/Adornment/utils.ts | 2 +- src/components/TextInput/helpers.tsx | 2 +- src/components/Typography/types.tsx | 4 +- src/components/__tests__/AnimatedFAB.test.tsx | 4 +- .../__tests__/Appbar/Appbar.test.tsx | 2 +- src/components/__tests__/Avatar.test.tsx | 2 +- src/components/__tests__/Badge.test.tsx | 2 +- .../__tests__/BottomNavigation.test.tsx | 14 +- src/components/__tests__/Button.test.tsx | 4 +- src/components/__tests__/Card/Card.test.tsx | 6 +- .../__tests__/Checkbox/utils.test.tsx | 2 +- src/components/__tests__/Chip.test.tsx | 2 +- src/components/__tests__/IconButton.test.tsx | 4 +- .../__tests__/ListAccordion.test.tsx | 2 +- src/components/__tests__/ListItem.test.tsx | 2 +- src/components/__tests__/ListSection.test.tsx | 2 +- src/components/__tests__/Menu.test.tsx | 4 +- src/components/__tests__/MenuItem.test.tsx | 2 +- src/components/__tests__/Modal.test.tsx | 6 +- .../__tests__/SegmentedButton.test.tsx | 2 +- src/components/__tests__/Snackbar.test.tsx | 2 +- src/components/__tests__/Switch.test.tsx | 2 +- src/components/__tests__/TextInput.test.tsx | 4 +- .../__tests__/Typography/Text.test.tsx | 14 +- src/core/__tests__/PaperProvider.test.tsx | 10 +- src/core/__tests__/theming.test.tsx | 106 ++++---- src/core/theming.tsx | 28 +- src/deprecated.ts | 47 ++++ src/index.tsx | 22 +- src/styles/__tests__/fonts.test.js | 2 +- src/styles/fonts.tsx | 36 +-- src/styles/overlay.tsx | 70 ----- src/styles/shadow.tsx | 18 +- src/styles/themes/{v3 => }/DarkTheme.tsx | 8 +- src/styles/themes/DynamicTheme.android.tsx | 252 +++++++++--------- src/styles/themes/DynamicTheme.tsx | 4 +- src/styles/themes/{v3 => }/LightTheme.tsx | 6 +- src/styles/themes/{v2 => }/colors.tsx | 0 src/styles/themes/index.ts | 4 +- src/styles/themes/{v3 => }/tokens.tsx | 4 +- src/types.tsx | 27 +- src/utils/animations.ts | 5 + 87 files changed, 464 insertions(+), 483 deletions(-) create mode 100644 src/deprecated.ts delete mode 100644 src/styles/overlay.tsx rename src/styles/themes/{v3 => }/DarkTheme.tsx (93%) rename src/styles/themes/{v3 => }/LightTheme.tsx (94%) rename src/styles/themes/{v2 => }/colors.tsx (100%) rename src/styles/themes/{v3 => }/tokens.tsx (98%) create mode 100644 src/utils/animations.ts diff --git a/src/babel/__fixtures__/rewrite-imports/output.js b/src/babel/__fixtures__/rewrite-imports/output.js index 7204b0ca33..7105f4216e 100644 --- a/src/babel/__fixtures__/rewrite-imports/output.js +++ b/src/babel/__fixtures__/rewrite-imports/output.js @@ -4,7 +4,7 @@ import BottomNavigation from "react-native-paper/lib/module/components/BottomNav import Button from "react-native-paper/lib/module/components/Button/Button"; import FAB from "react-native-paper/lib/module/components/FAB"; import Appbar from "react-native-paper/lib/module/components/Appbar"; -import { MD3Colors } from "react-native-paper/lib/module/styles/themes/v3/tokens"; +import { MD3Colors } from "react-native-paper/lib/module/deprecated"; import { NonExistent, NonExistentSecond as Stuff } from "react-native-paper/lib/module/index.js"; import { ThemeProvider } from "react-native-paper/lib/module/core/theming"; import { withTheme } from "react-native-paper/lib/module/core/theming"; diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 054a54bcad..cfbd6c948a 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -45,10 +45,10 @@ const DURATION = 2400; * ## Usage * ```js * import * as React from 'react'; - * import { ActivityIndicator, MD3Colors } from 'react-native-paper'; + * import { ActivityIndicator, Palette } from 'react-native-paper'; * * const MyComponent = () => ( - * + * * ); * * export default MyComponent; diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index 707f5c15c2..a9de112054 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -18,7 +18,7 @@ import { AppbarChildProps, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Elevation, ThemeProp } from '../../types'; +import type { Elevation, ThemeProp } from '../../types'; import Surface from '../Surface'; export type Props = Omit< @@ -234,7 +234,7 @@ const Appbar = ({ insets, restStyle, ]} - elevation={elevation as MD3Elevation} + elevation={elevation as Elevation} container {...rest} > diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index 212fcca952..1f960ff3a1 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -4,7 +4,7 @@ import type { Animated, StyleProp, View, ViewStyle } from 'react-native'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Theme } from '../../types'; +import type { Theme } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; @@ -86,7 +86,7 @@ const AppbarAction = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; const actionIconColor = iconColor ? iconColor diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index 4a886a64ba..e899311694 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -16,8 +16,8 @@ import { modeTextVariant } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, - MD3Theme, - MD3TypescaleKey, + Theme, + TypescaleKey, ThemeProp, } from '../../types'; import Text, { TextRef } from '../Typography/Text'; @@ -107,7 +107,7 @@ const AppbarContent = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors, fonts } = theme as MD3Theme; + const { colors, fonts } = theme as Theme; const titleTextColor = titleColor ? titleColor : colors.onSurface; @@ -118,7 +118,7 @@ const AppbarContent = ({ 'center-aligned': styles.v3DefaultContainer, }; - const variant = modeTextVariant[mode] as MD3TypescaleKey; + const variant = modeTextVariant[mode] as TypescaleKey; const contentWrapperProps = { pointerEvents: 'box-none' as ViewProps['pointerEvents'], diff --git a/src/components/Appbar/utils.ts b/src/components/Appbar/utils.ts index 893326dead..fe40f1129c 100644 --- a/src/components/Appbar/utils.ts +++ b/src/components/Appbar/utils.ts @@ -2,8 +2,8 @@ import React from 'react'; import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import { StyleSheet, Animated } from 'react-native'; -import { white } from '../../styles/themes/v2/colors'; -import type { InternalTheme, MD3Theme, ThemeProp } from '../../types'; +import { white } from '../../styles/themes/colors'; +import type { InternalTheme, Theme, ThemeProp } from '../../types'; export type AppbarModes = 'small' | 'medium' | 'large' | 'center-aligned'; @@ -27,7 +27,7 @@ export const getAppbarBackgroundColor = ( customBackground?: ColorValue, elevated?: boolean ) => { - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; if (customBackground) { return customBackground; } diff --git a/src/components/Avatar/AvatarIcon.tsx b/src/components/Avatar/AvatarIcon.tsx index f1a0206f1f..80ae6f68d3 100644 --- a/src/components/Avatar/AvatarIcon.tsx +++ b/src/components/Avatar/AvatarIcon.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import { white } from '../../styles/themes/v2/colors'; +import { white } from '../../styles/themes/colors'; import type { ThemeProp } from '../../types'; import getContrastingColor from '../../utils/getContrastingColor'; import Icon, { IconSource } from '../Icon'; diff --git a/src/components/Avatar/AvatarText.tsx b/src/components/Avatar/AvatarText.tsx index f93dd894a4..b6bd27623a 100644 --- a/src/components/Avatar/AvatarText.tsx +++ b/src/components/Avatar/AvatarText.tsx @@ -9,7 +9,7 @@ import { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import { white } from '../../styles/themes/v2/colors'; +import { white } from '../../styles/themes/colors'; import type { ThemeProp } from '../../types'; import getContrastingColor from '../../utils/getContrastingColor'; import Text from '../Typography/Text'; diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index b313637baa..b1a00b4ce4 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -9,7 +9,7 @@ import Icon, { IconSource } from './Icon'; import Surface from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; -import type { $Omit, $RemoveChildren, MD3Theme, ThemeProp } from '../types'; +import type { $Omit, $RemoveChildren, Theme, ThemeProp } from '../types'; const DEFAULT_MAX_WIDTH = 960; @@ -131,7 +131,7 @@ const Banner = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; const { current: position } = React.useRef( new Animated.Value(visible ? 1 : 0) ); diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index 696ec8fe83..3992463322 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -19,7 +19,7 @@ import { getLabelColor, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Theme, ThemeProp } from '../../types'; +import type { Theme, ThemeProp } from '../../types'; import useAnimatedValue from '../../utils/useAnimatedValue'; import useAnimatedValueArray from '../../utils/useAnimatedValueArray'; import useIsKeyboardShown from '../../utils/useIsKeyboardShown'; @@ -333,7 +333,7 @@ const BottomNavigationBar = ({ theme: themeOverrides, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; const { bottom, left, right } = useSafeAreaInsets(); const { scale } = theme.animation; const compact = compactProp ?? false; @@ -582,7 +582,7 @@ const BottomNavigationBar = ({ const isLegacyOrV3Shifting = shifting && labeled; - const font = (theme as MD3Theme).fonts.labelMedium; + const font = (theme as Theme).fonts.labelMedium; return renderTouchable({ key: route.key, diff --git a/src/components/BottomNavigation/utils.ts b/src/components/BottomNavigation/utils.ts index 20208d4b7d..fbfe1a3766 100644 --- a/src/components/BottomNavigation/utils.ts +++ b/src/components/BottomNavigation/utils.ts @@ -1,4 +1,4 @@ -import type { InternalTheme, MD3Theme } from '../../types'; +import type { InternalTheme, Theme } from '../../types'; export const getActiveTintColor = ({ activeColor, @@ -11,7 +11,7 @@ export const getActiveTintColor = ({ return activeColor; } - return (theme as MD3Theme).colors.onSecondaryContainer; + return (theme as Theme).colors.onSecondaryContainer; }; export const getInactiveTintColor = ({ @@ -25,7 +25,7 @@ export const getInactiveTintColor = ({ return inactiveColor; } - return (theme as MD3Theme).colors.onSurfaceVariant; + return (theme as Theme).colors.onSurfaceVariant; }; export const getLabelColor = ({ @@ -39,7 +39,7 @@ export const getLabelColor = ({ focused: boolean; theme: InternalTheme; }) => { - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; if (hasColor) { return tintColor; } diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 9f0b3a98dc..1cb2ca6bed 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -18,7 +18,7 @@ import { getButtonTouchableRippleStyle, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $Omit, MD3Theme, ThemeProp } from '../../types'; +import type { $Omit, Theme, ThemeProp } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; import hasTouchHandler from '../../utils/hasTouchHandler'; import { splitStyles } from '../../utils/splitStyles'; @@ -305,7 +305,7 @@ const Button = ( const { color: customLabelColor, fontSize: customLabelSize } = StyleSheet.flatten(labelStyle) || {}; - const font = (theme as MD3Theme).fonts.labelLarge; + const font = (theme as Theme).fonts.labelLarge; const textStyle = { color: textColor, diff --git a/src/components/Button/utils.tsx b/src/components/Button/utils.tsx index 5189c9c376..ff0dd218fd 100644 --- a/src/components/Button/utils.tsx +++ b/src/components/Button/utils.tsx @@ -1,8 +1,8 @@ import type { ViewStyle } from 'react-native'; -import { black, white } from '../../styles/themes/v2/colors'; -import { tokens } from '../../styles/themes/v3/tokens'; -import type { InternalTheme, MD3Theme } from '../../types'; +import { black, white } from '../../styles/themes/colors'; +import { tokens } from '../../styles/themes/tokens'; +import type { InternalTheme, Theme } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; const { stateOpacity } = tokens.md.ref; @@ -46,7 +46,7 @@ const getButtonBackgroundColor = ({ }: BaseProps & { customButtonColor?: string; }) => { - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; if (customButtonColor && !disabled) { return customButtonColor; } @@ -85,7 +85,7 @@ const getButtonTextColor = ({ backgroundColor: string; dark?: boolean; }) => { - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; if (customTextColor && !disabled) { return customTextColor; } diff --git a/src/components/Card/CardCover.tsx b/src/components/Card/CardCover.tsx index e0147381dc..5364c09068 100644 --- a/src/components/Card/CardCover.tsx +++ b/src/components/Card/CardCover.tsx @@ -3,7 +3,7 @@ import { Image, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { getCardCoverStyle } from './utils'; import { useInternalTheme } from '../../core/theming'; -import { grey200 } from '../../styles/themes/v2/colors'; +import { grey200 } from '../../styles/themes/colors'; import type { ThemeProp } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index f24809c405..b37f903499 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -8,7 +8,7 @@ import { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { MD3TypescaleKey, ThemeProp } from '../../types'; +import type { TypescaleKey, ThemeProp } from '../../types'; import Text from '../Typography/Text'; export type Props = React.ComponentPropsWithRef & { @@ -40,7 +40,7 @@ export type Props = React.ComponentPropsWithRef & { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - titleVariant?: keyof typeof MD3TypescaleKey; + titleVariant?: keyof typeof TypescaleKey; /** * Text for the subtitle. Note that this will only accept a string or ``-based node. */ @@ -69,7 +69,7 @@ export type Props = React.ComponentPropsWithRef & { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - subtitleVariant?: keyof typeof MD3TypescaleKey; + subtitleVariant?: keyof typeof TypescaleKey; /** * Callback which returns a React element to display on the left side. */ diff --git a/src/components/Card/utils.tsx b/src/components/Card/utils.tsx index 9a4ed5df7d..b3d776662d 100644 --- a/src/components/Card/utils.tsx +++ b/src/components/Card/utils.tsx @@ -1,6 +1,6 @@ import type { StyleProp, ViewStyle } from 'react-native'; -import type { InternalTheme, MD3Theme } from '../../types'; +import type { InternalTheme, Theme } from '../../types'; type CardMode = 'elevated' | 'outlined' | 'contained'; @@ -41,7 +41,7 @@ export const getCardCoverStyle = ({ }; const getBorderColor = ({ theme }: { theme: InternalTheme }) => { - return (theme as MD3Theme).colors.outline; + return (theme as Theme).colors.outline; }; const getBackgroundColor = ({ @@ -51,7 +51,7 @@ const getBackgroundColor = ({ theme: InternalTheme; isMode: (mode: CardMode) => boolean; }) => { - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; if (isMode('contained')) { return colors.surfaceVariant; } diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 5dfe86c64a..4ad9eafa53 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -13,8 +13,8 @@ import Checkbox from './Checkbox'; import CheckboxAndroid from './CheckboxAndroid'; import CheckboxIOS from './CheckboxIOS'; import { useInternalTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/v3/tokens'; -import type { ThemeProp, MD3TypescaleKey } from '../../types'; +import { tokens } from '../../styles/themes/tokens'; +import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple, { Props as TouchableRippleProps, } from '../TouchableRipple/TouchableRipple'; @@ -86,7 +86,7 @@ export type Props = { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - labelVariant?: keyof typeof MD3TypescaleKey; + labelVariant?: keyof typeof TypescaleKey; /** * @optional */ diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index 7a9e8add67..025717f308 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 2c79e8201d..b6e3e1d282 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -17,8 +17,8 @@ import useLatestCallback from 'use-latest-callback'; import { ChipAvatarProps, getChipColors } from './helpers'; import { useInternalTheme } from '../../core/theming'; -import { white } from '../../styles/themes/v2/colors'; -import type { $Omit, EllipsizeProp, MD3Theme, ThemeProp } from '../../types'; +import { white } from '../../styles/themes/colors'; +import type { $Omit, EllipsizeProp, Theme, ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; @@ -286,7 +286,7 @@ const Chip = ({ }; const labelTextStyle = { color: textColor, - ...(theme as MD3Theme).fonts.labelLarge, + ...(theme as Theme).fonts.labelLarge, }; return ( theme as MD3Theme; +const md3 = (theme: InternalTheme) => theme as Theme; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 66936a1dc9..fd6597c34e 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -15,7 +15,7 @@ import DialogIcon from './DialogIcon'; import DialogScrollArea from './DialogScrollArea'; import DialogTitle from './DialogTitle'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Theme, ThemeProp } from '../../types'; +import type { Theme, ThemeProp } from '../../types'; import Modal from '../Modal'; import { DialogChildProps } from './utils'; @@ -105,7 +105,7 @@ const Dialog = ({ }: Props) => { const { right, left } = useSafeAreaInsets(); const theme = useInternalTheme(themeOverrides); - const { roundness } = theme as MD3Theme; + const { roundness } = theme as Theme; const borderRadius = 7 * roundness; const backgroundColor = theme.colors.surfaceContainerHigh; diff --git a/src/components/Dialog/DialogIcon.tsx b/src/components/Dialog/DialogIcon.tsx index 7682054003..30431190b1 100644 --- a/src/components/Dialog/DialogIcon.tsx +++ b/src/components/Dialog/DialogIcon.tsx @@ -4,7 +4,7 @@ import { StyleSheet, View } from 'react-native'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Theme } from '../../types'; +import type { Theme } from '../../types'; import Icon, { IconSource } from '../Icon'; export type Props = { @@ -70,7 +70,7 @@ const DialogIcon = ({ theme: themeOverrides, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; //@ts-ignore const iconColor = color || colors.secondary; diff --git a/src/components/Dialog/DialogScrollArea.tsx b/src/components/Dialog/DialogScrollArea.tsx index 5deeb4a5e6..69fd0ac23e 100644 --- a/src/components/Dialog/DialogScrollArea.tsx +++ b/src/components/Dialog/DialogScrollArea.tsx @@ -4,7 +4,7 @@ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import type { ThemeProp } from 'src/types'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Theme } from '../../types'; +import type { Theme } from '../../types'; export type Props = React.ComponentPropsWithRef & { /** @@ -51,7 +51,7 @@ export type Props = React.ComponentPropsWithRef & { */ const DialogScrollArea = (props: Props) => { const theme = useInternalTheme(props.theme); - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; const borderStyles = { borderColor: colors.surfaceVariant, borderTopWidth: 1, diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index 407acb68b3..5ee8a990ac 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { StyleProp, StyleSheet, TextStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Theme, ThemeProp } from '../../types'; +import type { Theme, ThemeProp } from '../../types'; import Text from '../Typography/Text'; export type Props = React.ComponentPropsWithRef & { @@ -52,7 +52,7 @@ const DialogTitle = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors, fonts } = theme as MD3Theme; + const { colors, fonts } = theme as Theme; const headerTextStyle = { color: colors.onSurface, diff --git a/src/components/Drawer/DrawerSection.tsx b/src/components/Drawer/DrawerSection.tsx index a701d38ad6..891a5aee98 100644 --- a/src/components/Drawer/DrawerSection.tsx +++ b/src/components/Drawer/DrawerSection.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import { MD3Colors } from '../../styles/themes/v3/tokens'; +import { Palette } from '../../styles/themes/tokens'; import type { ThemeProp } from '../../types'; import Divider from '../Divider'; import Text from '../Typography/Text'; @@ -127,7 +127,7 @@ const styles = StyleSheet.create({ marginTop: 4, }, v3Divider: { - backgroundColor: MD3Colors.neutralVariant50, + backgroundColor: Palette.neutralVariant50, }, }); diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index ed4a200b09..af3222a4a2 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -355,7 +355,7 @@ const AnimatedFAB = ({ const md3Elevation = !isIOS ? 0 : 3; - const shadowStyle = styles.v3Shadow; + const shadowStyle = styles.shadow; const baseStyle = [StyleSheet.absoluteFill, shadowStyle]; const newAccessibilityState = { ...accessibilityState }; @@ -555,7 +555,7 @@ const styles = StyleSheet.create({ shadowWrapper: { elevation: 0, }, - v3Shadow: { + shadow: { elevation: 3, }, iconWrapper: { diff --git a/src/components/HelperText/utils.ts b/src/components/HelperText/utils.ts index 767cb74ef0..214948c767 100644 --- a/src/components/HelperText/utils.ts +++ b/src/components/HelperText/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index b886e06fe2..e792e2670e 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -86,12 +86,12 @@ export type Props = IconProps & { * ## Usage * ```js * import * as React from 'react'; - * import { Icon, MD3Colors } from 'react-native-paper'; + * import { Icon, Palette } from 'react-native-paper'; * * const MyComponent = () => ( * * ); diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 8cf5bff8d5..ec67a2ca1f 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -92,12 +92,12 @@ export type Props = Omit<$RemoveChildren, 'style'> & { * ## Usage * ```js * import * as React from 'react'; - * import { IconButton, MD3Colors } from 'react-native-paper'; + * import { IconButton, Palette } from 'react-native-paper'; * * const MyComponent = () => ( * console.log('Pressed')} * /> diff --git a/src/components/IconButton/utils.ts b/src/components/IconButton/utils.ts index 653a272750..53586925b7 100644 --- a/src/components/IconButton/utils.ts +++ b/src/components/IconButton/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/List/ListIcon.tsx b/src/components/List/ListIcon.tsx index 108d642383..c76865ccd3 100644 --- a/src/components/List/ListIcon.tsx +++ b/src/components/List/ListIcon.tsx @@ -29,13 +29,13 @@ const ICON_SIZE = 24; * ## Usage * ```js * import * as React from 'react'; - * import { List, MD3Colors } from 'react-native-paper'; + * import { List, Palette } from 'react-native-paper'; * * const MyComponent = () => ( * <> - * - * - * + * + * + * * * ); * diff --git a/src/components/List/ListImage.tsx b/src/components/List/ListImage.tsx index 8eb887bddc..ec1cf18f82 100644 --- a/src/components/List/ListImage.tsx +++ b/src/components/List/ListImage.tsx @@ -25,7 +25,7 @@ export type Props = { * ## Usage * ```js * import * as React from 'react'; - * import { List, MD3Colors } from 'react-native-paper'; + * import { List, Palette } from 'react-native-paper'; * * const MyComponent = () => ( * <> diff --git a/src/components/List/ListSection.tsx b/src/components/List/ListSection.tsx index 59640c70da..be5481ab21 100644 --- a/src/components/List/ListSection.tsx +++ b/src/components/List/ListSection.tsx @@ -37,7 +37,7 @@ export type Props = React.ComponentPropsWithRef & { * ## Usage * ```js * import * as React from 'react'; - * import { List, MD3Colors } from 'react-native-paper'; + * import { List, Palette } from 'react-native-paper'; * * const MyComponent = () => ( * @@ -45,7 +45,7 @@ export type Props = React.ComponentPropsWithRef & { * } /> * } + * left={() => } * /> * * ); diff --git a/src/components/MaterialCommunityIcon.tsx b/src/components/MaterialCommunityIcon.tsx index 52719bdbe1..9426be685b 100644 --- a/src/components/MaterialCommunityIcon.tsx +++ b/src/components/MaterialCommunityIcon.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ComponentProps } from 'react'; import { StyleSheet, Text, Platform, Role, ViewProps } from 'react-native'; -import { black } from '../styles/themes/v2/colors'; +import { black } from '../styles/themes/colors'; export type IconProps = { name: ComponentProps['name']; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 47727f7e10..a3f798d03a 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -23,7 +23,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import MenuItem from './MenuItem'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Elevation, MD3Theme, ThemeProp } from '../../types'; +import type { Elevation, Theme, ThemeProp } from '../../types'; import { ElevationLevels } from '../../types'; import { addEventListener } from '../../utils/addEventListener'; import { BackHandler } from '../../utils/BackHandler/BackHandler'; @@ -72,7 +72,7 @@ export type Props = { * Elevation level of the menu's content. Shadow styles are calculated based on this value. Default `backgroundColor` is taken from the corresponding `theme.colors.elevation` property. By default equals `2`. * @supported Available in v5.x with theme version 3 */ - elevation?: MD3Elevation; + elevation?: Elevation; /** * Mode of the menu's content. * - `elevated` - Surface with a shadow and background color corresponding to set `elevation` value. @@ -104,7 +104,7 @@ const EASING = Easing.bezier(0.4, 0, 0.2, 1); const WINDOW_LAYOUT = Dimensions.get('window'); -const DEFAULT_ELEVATION: MD3Elevation = 2; +const DEFAULT_ELEVATION: Elevation = 2; export const ELEVATION_LEVELS_MAP = Object.values( ElevationLevels ) as ElevationLevels[]; @@ -196,7 +196,7 @@ const Menu = ({ keyboardShouldPersistTaps, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors: md3Colors } = theme as MD3Theme; + const { colors: md3Colors } = theme as Theme; const insets = useSafeAreaInsets(); const [rendered, setRendered] = React.useState(visible); const [left, setLeft] = React.useState(0); diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index ac77147031..89d185257b 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -17,7 +17,7 @@ import { MIN_WIDTH, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { MD3Theme, ThemeProp } from '../../types'; +import type { Theme, ThemeProp } from '../../types'; import Icon, { IconSource } from '../Icon'; import TouchableRipple, { Props as TouchableRippleProps, @@ -163,7 +163,7 @@ const MenuItem = ({ const titleTextStyle = { color: titleColor, - ...(theme as MD3Theme).fonts.bodyLarge, + ...(theme as Theme).fonts.bodyLarge, }; const newAccessibilityState = { ...accessibilityState, disabled }; diff --git a/src/components/Menu/utils.ts b/src/components/Menu/utils.ts index f9e3d5d9a1..4288676bbd 100644 --- a/src/components/Menu/utils.ts +++ b/src/components/Menu/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import type { InternalTheme } from '../../types'; import type { IconSource } from '../Icon'; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 21b1ffd39d..368f9e245f 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -14,7 +14,7 @@ import useLatestCallback from 'use-latest-callback'; import Surface from './Surface'; import { useInternalTheme } from '../core/theming'; -import { tokens } from '../styles/themes/v3/tokens'; +import { tokens } from '../styles/themes/tokens'; import type { ThemeProp } from '../types'; import { addEventListener } from '../utils/addEventListener'; import { BackHandler } from '../utils/BackHandler/BackHandler'; diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index c307df2479..eefed6e50d 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -61,10 +61,10 @@ const { isRTL } = I18nManager; * ## Usage * ```js * import * as React from 'react'; - * import { ProgressBar, MD3Colors } from 'react-native-paper'; + * import { ProgressBar, Palette } from 'react-native-paper'; * * const MyComponent = () => ( - * + * * ); * * export default MyComponent; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index 25f5d99b00..fc67da6131 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -15,8 +15,8 @@ import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; import RadioButtonIOS from './RadioButtonIOS'; import { handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/v3/tokens'; -import type { ThemeProp, MD3TypescaleKey } from '../../types'; +import { tokens } from '../../styles/themes/tokens'; +import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple, { Props as TouchableRippleProps, } from '../TouchableRipple/TouchableRipple'; @@ -88,7 +88,7 @@ export type Props = { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - labelVariant?: keyof typeof MD3TypescaleKey; + labelVariant?: keyof typeof TypescaleKey; /** * Specifies the largest possible scale a label font can reach. */ diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index ce6088ecb1..76c9e03218 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -20,7 +20,7 @@ import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; import { useInternalTheme } from '../core/theming'; -import type { MD3Theme, ThemeProp } from '../types'; +import type { Theme, ThemeProp } from '../types'; import { forwardRef } from '../utils/forwardRef'; interface Style { @@ -193,7 +193,7 @@ const Searchbar = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); - const { colors, fonts } = theme as MD3Theme; + const { colors, fonts } = theme as Theme; const root = React.useRef(null); React.useImperativeHandle(ref, () => ({ diff --git a/src/components/SegmentedButtons/utils.ts b/src/components/SegmentedButtons/utils.ts index bff11f5615..b2772989ae 100644 --- a/src/components/SegmentedButtons/utils.ts +++ b/src/components/SegmentedButtons/utils.ts @@ -1,6 +1,6 @@ import { ViewStyle } from 'react-native'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index 9ee01c7cb2..1c299d7eb8 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -19,7 +19,7 @@ import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; -import type { $Omit, $RemoveChildren, MD3Theme, ThemeProp } from '../types'; +import type { $Omit, $RemoveChildren, Theme, ThemeProp } from '../types'; export type Props = $Omit, 'mode'> & { /** @@ -235,7 +235,7 @@ const Snackbar = ({ } }, [visible, handleOnVisible, handleOnHidden]); - const { colors, roundness } = theme as MD3Theme; + const { colors, roundness } = theme as Theme; if (hidden) { return null; diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index 2c8fad2461..16350b8770 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -10,13 +10,13 @@ import { } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import { isAnimatedValue } from '../styles/overlay'; import shadow from '../styles/shadow'; -import type { MD3Elevation, MD3Theme, ThemeProp } from '../types'; +import type { Elevation, Theme, ThemeProp } from '../types'; +import { isAnimatedValue } from '../utils/animations'; import { forwardRef } from '../utils/forwardRef'; import { splitStyles } from '../utils/splitStyles'; -type Elevation = 0 | 1 | 2 | 3 | 4 | 5 | Animated.Value; +type SurfaceElevation = Elevation | Animated.Value; export type Props = Omit, 'style'> & { /** @@ -34,7 +34,7 @@ export type Props = Omit, 'style'> & { * Note: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`. * It's no longer supported with theme version 3 and you should use `elevation` property instead. */ - elevation?: Elevation; + elevation?: SurfaceElevation; /** * @supported Available in v5.x with theme version 3 * Mode of the Surface. @@ -90,7 +90,7 @@ const iOSShadowOutputRanges = [ ]; const inputRange = [0, 1, 2, 3, 4, 5]; function getStyleForShadowLayer( - elevation: Elevation, + elevation: SurfaceElevation, layer: 0 | 1 ): Animated.WithAnimatedValue { if (isAnimatedValue(elevation)) { @@ -129,7 +129,7 @@ function getStyleForShadowLayer( const SurfaceIOS = forwardRef< View, Omit & { - elevation: Elevation; + elevation: SurfaceElevation; backgroundColor?: string | Animated.AnimatedInterpolation; } >( @@ -253,7 +253,7 @@ const Surface = forwardRef( ) => { const theme = useInternalTheme(overridenTheme); - const { colors } = theme as MD3Theme; + const { colors } = theme as Theme; const inputRange = [0, 1, 2, 3, 4, 5]; @@ -262,7 +262,7 @@ const Surface = forwardRef( return elevation.interpolate({ inputRange, outputRange: inputRange.map((elevation) => { - return colors.elevation?.[`level${elevation as MD3Elevation}`]; + return colors.elevation?.[`level${elevation as Elevation}`]; }), }); } diff --git a/src/components/Switch/utils.ts b/src/components/Switch/utils.ts index c0afc7c142..4672e2b7f0 100644 --- a/src/components/Switch/utils.ts +++ b/src/components/Switch/utils.ts @@ -9,7 +9,7 @@ import { grey700, white, black, -} from '../../styles/themes/v2/colors'; +} from '../../styles/themes/colors'; import type { InternalTheme } from '../../types'; type BaseProps = { diff --git a/src/components/TextInput/Adornment/utils.ts b/src/components/TextInput/Adornment/utils.ts index e7d57985d6..aaf20a221c 100644 --- a/src/components/TextInput/Adornment/utils.ts +++ b/src/components/TextInput/Adornment/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../../styles/themes/v3/tokens'; +import { tokens } from '../../../styles/themes/tokens'; import type { InternalTheme } from '../../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/TextInput/helpers.tsx b/src/components/TextInput/helpers.tsx index b1111b2a1a..24c3fe079c 100644 --- a/src/components/TextInput/helpers.tsx +++ b/src/components/TextInput/helpers.tsx @@ -14,7 +14,7 @@ import { MD3_OUTLINED_INPUT_OFFSET, } from './constants'; import type { TextInputLabelProp } from './types'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/Typography/types.tsx b/src/components/Typography/types.tsx index 61a6dc7c1e..3f1754521a 100644 --- a/src/components/Typography/types.tsx +++ b/src/components/Typography/types.tsx @@ -1,5 +1,5 @@ -import type { MD3TypescaleKey } from '../../types'; +import type { TypescaleKey } from '../../types'; export type VariantProp = | (T extends string ? (string extends T ? never : T) : never) - | keyof typeof MD3TypescaleKey; + | keyof typeof TypescaleKey; diff --git a/src/components/__tests__/AnimatedFAB.test.tsx b/src/components/__tests__/AnimatedFAB.test.tsx index a31f6d5fb4..e2209cf424 100644 --- a/src/components/__tests__/AnimatedFAB.test.tsx +++ b/src/components/__tests__/AnimatedFAB.test.tsx @@ -6,12 +6,12 @@ import { Animated, StyleSheet } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; import { act } from 'react-test-renderer'; -import { MD3Colors } from '../../styles/themes/v3/tokens'; +import { Palette } from '../../styles/themes/tokens'; import AnimatedFAB from '../FAB/AnimatedFAB'; const styles = StyleSheet.create({ background: { - backgroundColor: MD3Colors.tertiary50, + backgroundColor: Palette.tertiary50, }, }); diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index e1c55fa8c8..8f8600f9bb 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -5,7 +5,7 @@ import { act, render } from '@testing-library/react-native'; import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'; import { getTheme } from '../../../core/theming'; -import { tokens } from '../../../styles/themes/v3/tokens'; +import { tokens } from '../../../styles/themes/tokens'; import Appbar from '../../Appbar'; import { getAppbarBackgroundColor, diff --git a/src/components/__tests__/Avatar.test.tsx b/src/components/__tests__/Avatar.test.tsx index 96d69582b6..c339e5b95f 100644 --- a/src/components/__tests__/Avatar.test.tsx +++ b/src/components/__tests__/Avatar.test.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/v2/colors'; +import { red500 } from '../../styles/themes/colors'; import * as Avatar from '../Avatar/Avatar'; const styles = StyleSheet.create({ diff --git a/src/components/__tests__/Badge.test.tsx b/src/components/__tests__/Badge.test.tsx index 32730659a1..8fde099736 100644 --- a/src/components/__tests__/Badge.test.tsx +++ b/src/components/__tests__/Badge.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/v2/colors'; +import { red500 } from '../../styles/themes/colors'; import Badge from '../Badge'; it('renders badge', () => { diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index f5bdeced00..be08d7fc17 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -4,7 +4,7 @@ import { Animated, Easing, Platform, StyleSheet } from 'react-native'; import { act, fireEvent, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { MD3Colors } from '../../styles/themes/v3/tokens'; +import { Palette } from '../../styles/themes/tokens'; import BottomNavigation from '../BottomNavigation/BottomNavigation'; import BottomNavigationRouteScreen from '../BottomNavigation/BottomNavigationRouteScreen'; import { @@ -16,7 +16,7 @@ import Icon from '../Icon'; const styles = StyleSheet.create({ backgroundColor: { - backgroundColor: MD3Colors.error60, + backgroundColor: Palette.error60, }, }); @@ -376,7 +376,7 @@ it('renders custom background color passed to barStyle property', () => { ); const wrapper = getByTestId('bottom-navigation-bar-content'); - expect(wrapper).toHaveStyle({ backgroundColor: MD3Colors.error60 }); + expect(wrapper).toHaveStyle({ backgroundColor: Palette.error60 }); }); it('renders a single tab', () => { @@ -478,7 +478,7 @@ describe('getActiveTintColor', () => { it.each` activeColor | expected ${'#FBF7DB'} | ${'#FBF7DB'} - ${undefined} | ${MD3Colors.secondary10} + ${undefined} | ${Palette.secondary10} `( 'returns $expected when activeColor: $activeColor', ({ activeColor, expected }) => { @@ -493,7 +493,7 @@ describe('getInactiveTintColor', () => { it.each` inactiveColor | expected ${'#853D4B'} | ${'#853D4B'} - ${undefined} | ${MD3Colors.neutralVariant30} + ${undefined} | ${Palette.neutralVariant30} `( 'returns $expected when inactiveColor: $inactiveColor', ({ inactiveColor, expected }) => { @@ -512,8 +512,8 @@ describe('getLabelColor', () => { tintColor | focused | expected ${'#FBF7DB'} | ${true} | ${'#FBF7DB'} ${'#853D4B'} | ${true} | ${'#853D4B'} - ${undefined} | ${true} | ${MD3Colors.neutral10} - ${undefined} | ${false} | ${MD3Colors.neutralVariant30} + ${undefined} | ${true} | ${Palette.neutral10} + ${undefined} | ${false} | ${Palette.neutralVariant30} `( 'returns $expected when tintColor: $tintColor, focused: $focused', ({ tintColor, focused, expected }) => { diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index b47be8c53e..894d7ce8e2 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -4,8 +4,8 @@ import { Animated, StyleSheet } from 'react-native'; import { act, fireEvent, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { pink500, white } from '../../styles/themes/v2/colors'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { pink500, white } from '../../styles/themes/colors'; +import { tokens } from '../../styles/themes/tokens'; import Button from '../Button/Button'; import { getButtonColors } from '../Button/utils'; diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index a7dc736c62..e8df99d568 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -4,7 +4,7 @@ import { Animated, StyleSheet, Text } from 'react-native'; import { act, render } from '@testing-library/react-native'; import { getTheme } from '../../../core/theming'; -import { MD3Colors } from '../../../styles/themes/v3/tokens'; +import { Palette } from '../../../styles/themes/tokens'; import Button from '../../Button/Button'; import Card from '../../Card/Card'; import { getCardColors, getCardCoverStyle } from '../../Card/utils'; @@ -53,14 +53,14 @@ describe('Card', () => { {null} ); expect(getByLabelText('card')).toHaveStyle({ - borderColor: MD3Colors.error50, + borderColor: Palette.error50, }); }); diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index 799a343768..eca368146b 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,5 +1,5 @@ import { getTheme } from '../../../core/theming'; -import { tokens } from '../../../styles/themes/v3/tokens'; +import { tokens } from '../../../styles/themes/tokens'; import { getAndroidSelectionControlColor, getSelectionControlIOSColor, diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 76044715b2..1459a9151d 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -5,7 +5,7 @@ import { act, render } from '@testing-library/react-native'; import color from 'color'; import { getTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import Chip from '../Chip/Chip'; import { getChipColors } from '../Chip/helpers'; diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index fdd3316850..88faf4ab78 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -4,8 +4,8 @@ import { Animated, StyleSheet } from 'react-native'; import { act, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { pink500 } from '../../styles/themes/v2/colors'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { pink500 } from '../../styles/themes/colors'; +import { tokens } from '../../styles/themes/tokens'; import IconButton from '../IconButton/IconButton'; import { getIconButtonColor } from '../IconButton/utils'; diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index 65a6a9d4de..fedf9670be 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -4,7 +4,7 @@ import { StyleSheet, View } from 'react-native'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { red500 } from '../../styles/themes/v2/colors'; +import { red500 } from '../../styles/themes/colors'; import ListAccordion from '../List/ListAccordion'; import ListAccordionGroup from '../List/ListAccordionGroup'; import ListIcon from '../List/ListIcon'; diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index 036697f9ec..8f00f441ae 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -4,7 +4,7 @@ import { Text, View } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/v2/colors'; +import { red500 } from '../../styles/themes/colors'; import Chip from '../Chip/Chip'; import IconButton from '../IconButton/IconButton'; import ListIcon from '../List/ListIcon'; diff --git a/src/components/__tests__/ListSection.test.tsx b/src/components/__tests__/ListSection.test.tsx index 8cc1578b8f..d09a0aa4a7 100644 --- a/src/components/__tests__/ListSection.test.tsx +++ b/src/components/__tests__/ListSection.test.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import { render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/v2/colors'; +import { red500 } from '../../styles/themes/colors'; import ListIcon from '../List/ListIcon'; import ListItem from '../List/ListItem'; import ListSection from '../List/ListSection'; diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 21ed4d0802..753124e478 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -4,7 +4,7 @@ import { Animated, Dimensions, StyleSheet, View } from 'react-native'; import { act, render, screen, waitFor } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { MD3Elevation } from '../../types'; +import { Elevation } from '../../types'; import Button from '../Button/Button'; import Menu, { ELEVATION_LEVELS_MAP } from '../Menu/Menu'; import Portal from '../Portal/Portal'; @@ -68,7 +68,7 @@ it('renders menu with content styles', () => { expect(tree).toMatchSnapshot(); }); -([0, 1, 2, 3, 4, 5] as MD3Elevation[]).forEach((elevation) => +([0, 1, 2, 3, 4, 5] as Elevation[]).forEach((elevation) => it(`renders menu with background color based on elevation value = ${elevation}`, () => { const theme = getTheme(); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index dead127da1..034645d2fd 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index d040cc62de..a7d442efe3 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -8,8 +8,8 @@ import { import { act, fireEvent, render } from '@testing-library/react-native'; -import { MD3LightTheme } from '../../styles/themes'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { LightTheme } from '../../styles/themes'; +import { tokens } from '../../styles/themes/tokens'; import Modal from '../Modal'; const { scrimAlpha } = tokens.md.ref; @@ -58,7 +58,7 @@ describe('Modal', () => { ); expect(getByTestId('modal-backdrop')).toHaveStyle({ - backgroundColor: MD3LightTheme.colors.scrim, + backgroundColor: LightTheme.colors.scrim, }); }); diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 10385d5172..994c26d765 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { tokens } from '../../styles/themes/tokens'; import SegmentedButtons from '../SegmentedButtons/SegmentedButtons'; import { getDisabledSegmentedButtonStyle, diff --git a/src/components/__tests__/Snackbar.test.tsx b/src/components/__tests__/Snackbar.test.tsx index e8f71f3319..425b918495 100644 --- a/src/components/__tests__/Snackbar.test.tsx +++ b/src/components/__tests__/Snackbar.test.tsx @@ -3,7 +3,7 @@ import { Animated, StyleSheet, Text, View } from 'react-native'; import { act, render } from '@testing-library/react-native'; -import { red200, white } from '../../styles/themes/v2/colors'; +import { red200, white } from '../../styles/themes/colors'; import Snackbar from '../Snackbar'; const styles = StyleSheet.create({ diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index ee7a82a15d..c555c8ad2b 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -13,7 +13,7 @@ import { grey800, pink500, grey700, -} from '../../styles/themes/v2/colors'; +} from '../../styles/themes/colors'; import Switch from '../Switch/Switch'; import { getSwitchColor } from '../Switch/utils'; diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index 2fbebb326f..e052755eaf 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -5,8 +5,8 @@ import { I18nManager, Platform, StyleSheet, Text, View } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; import { DefaultTheme, getTheme, ThemeProvider } from '../../core/theming'; -import { red500 } from '../../styles/themes/v2/colors'; -import { tokens } from '../../styles/themes/v3/tokens'; +import { red500 } from '../../styles/themes/colors'; +import { tokens } from '../../styles/themes/tokens'; import { getFlatInputColors, getOutlinedInputColors, diff --git a/src/components/__tests__/Typography/Text.test.tsx b/src/components/__tests__/Typography/Text.test.tsx index 2560920322..0e0bb64303 100644 --- a/src/components/__tests__/Typography/Text.test.tsx +++ b/src/components/__tests__/Typography/Text.test.tsx @@ -4,8 +4,8 @@ import { render } from '@testing-library/react-native'; import PaperProvider from '../../../core/PaperProvider'; import configureFonts from '../../../styles/fonts'; -import { MD3LightTheme } from '../../../styles/themes'; -import { tokens } from '../../../styles/themes/v3/tokens'; +import { LightTheme } from '../../../styles/themes'; +import { tokens } from '../../../styles/themes/tokens'; import Text, { customText } from '../../Typography/Text'; const content = 'Something rendered as a child content'; @@ -64,7 +64,7 @@ it('renders v3 Text component with custom variant correctly', () => { } as const; const theme = { - ...MD3LightTheme, + ...LightTheme, fonts: configureFonts({ config: fontConfig }), }; const Text = customText<'customVariant'>(); @@ -86,9 +86,7 @@ it("nested text with variant should override parent's variant", () => { ); - expect(getByTestId('parent-text')).toHaveStyle( - MD3LightTheme.fonts.displayLarge - ); + expect(getByTestId('parent-text')).toHaveStyle(LightTheme.fonts.displayLarge); }); it("nested non-text component should not override parent's variant", () => { @@ -100,9 +98,7 @@ it("nested non-text component should not override parent's variant", () => { ); - expect(getByTestId('parent-text')).toHaveStyle( - MD3LightTheme.fonts.displayLarge - ); + expect(getByTestId('parent-text')).toHaveStyle(LightTheme.fonts.displayLarge); }); it("nested text without variant, but with styles, should override parent's styles", () => { diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 03ebbdc9f7..6ae08641c1 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -8,7 +8,7 @@ import { import { render, act } from '@testing-library/react-native'; -import { MD3LightTheme, MD3DarkTheme } from '../../styles/themes'; +import { LightTheme, DarkTheme } from '../../styles/themes'; import type { ThemeProp } from '../../types'; import PaperProvider from '../PaperProvider'; import { useTheme } from '../theming'; @@ -102,8 +102,8 @@ const createProvider = (theme?: ThemeProp) => { ); }; -const ExtendedLightTheme = { ...MD3LightTheme } as ThemeProp; -const ExtendedDarkTheme = { ...MD3DarkTheme } as ThemeProp; +const ExtendedLightTheme = { ...LightTheme } as ThemeProp; +const ExtendedDarkTheme = { ...DarkTheme } as ThemeProp; describe('PaperProvider', () => { beforeEach(() => { @@ -125,13 +125,13 @@ describe('PaperProvider', () => { it('handles overriding animation with the custom one', () => { const { getByTestId } = render( createProvider({ - ...MD3LightTheme, + ...LightTheme, animation: { defaultAnimationDuration: 250 }, }) ); expect(getByTestId('provider-child-view').props.theme).toStrictEqual({ - ...MD3LightTheme, + ...LightTheme, animation: { scale: 1, defaultAnimationDuration: 250 }, }); }); diff --git a/src/core/__tests__/theming.test.tsx b/src/core/__tests__/theming.test.tsx index f9efe7752e..0771e32650 100644 --- a/src/core/__tests__/theming.test.tsx +++ b/src/core/__tests__/theming.test.tsx @@ -1,4 +1,4 @@ -import { MD3DarkTheme, MD3LightTheme } from '../../styles/themes'; +import { DarkTheme, LightTheme } from '../../styles/themes'; import { adaptNavigationTheme } from '../theming'; const NavigationLightTheme = { @@ -40,17 +40,17 @@ const NavigationCustomLightTheme = { }; const AppCustomLightTheme = { - ...MD3LightTheme, + ...LightTheme, colors: { - ...MD3LightTheme.colors, + ...LightTheme.colors, primary: 'purple', }, }; const AppCustomDarkTheme = { - ...MD3DarkTheme, + ...DarkTheme, colors: { - ...MD3DarkTheme.colors, + ...DarkTheme.colors, primary: 'orchid', }, }; @@ -89,37 +89,37 @@ describe('adaptNavigationTheme', () => { ...NavigationLightTheme, colors: { ...NavigationLightTheme.colors, - primary: MD3LightTheme.colors.primary, - background: MD3LightTheme.colors.background, - card: MD3LightTheme.colors.surfaceContainer, - text: MD3LightTheme.colors.onSurface, - border: MD3LightTheme.colors.outline, - notification: MD3LightTheme.colors.error, + primary: LightTheme.colors.primary, + background: LightTheme.colors.background, + card: LightTheme.colors.surfaceContainer, + text: LightTheme.colors.onSurface, + border: LightTheme.colors.outline, + notification: LightTheme.colors.error, }, }, DarkTheme: { ...NavigationDarkTheme, colors: { ...NavigationDarkTheme.colors, - primary: MD3DarkTheme.colors.primary, - background: MD3DarkTheme.colors.background, - card: MD3DarkTheme.colors.surfaceContainer, - text: MD3DarkTheme.colors.onSurface, - border: MD3DarkTheme.colors.outline, - notification: MD3DarkTheme.colors.error, + primary: DarkTheme.colors.primary, + background: DarkTheme.colors.background, + card: DarkTheme.colors.surfaceContainer, + text: DarkTheme.colors.onSurface, + border: DarkTheme.colors.outline, + notification: DarkTheme.colors.error, }, }, }); }); it('should return adapted navigation light theme', () => { - const { LightTheme } = adaptNavigationTheme({ + const { LightTheme: navLight } = adaptNavigationTheme({ reactNavigationLight: NavigationLightTheme, }); - const { colors } = MD3LightTheme; + const { colors } = LightTheme; - expect(LightTheme).toMatchObject({ + expect(navLight).toMatchObject({ ...NavigationLightTheme, colors: { ...NavigationLightTheme.colors, @@ -134,13 +134,13 @@ describe('adaptNavigationTheme', () => { }); it('should return adapted navigation dark theme', () => { - const { DarkTheme } = adaptNavigationTheme({ + const { DarkTheme: navDark } = adaptNavigationTheme({ reactNavigationDark: NavigationDarkTheme, }); - const { colors } = MD3DarkTheme; + const { colors } = DarkTheme; - expect(DarkTheme).toMatchObject({ + expect(navDark).toMatchObject({ ...NavigationDarkTheme, colors: { ...NavigationDarkTheme.colors, @@ -155,13 +155,13 @@ describe('adaptNavigationTheme', () => { }); it('should return adapted custom navigation theme', () => { - const { LightTheme } = adaptNavigationTheme({ + const { LightTheme: navLight } = adaptNavigationTheme({ reactNavigationLight: NavigationCustomLightTheme, }); - const { colors } = MD3LightTheme; + const { colors } = LightTheme; - expect(LightTheme).toMatchObject({ + expect(navLight).toMatchObject({ ...NavigationCustomLightTheme, colors: { ...NavigationCustomLightTheme.colors, @@ -178,14 +178,14 @@ describe('adaptNavigationTheme', () => { }); it('should return adapted navigation light theme based on custom app light theme', () => { - const { LightTheme } = adaptNavigationTheme({ + const { LightTheme: navLight } = adaptNavigationTheme({ reactNavigationLight: NavigationLightTheme, materialLight: AppCustomLightTheme, }); const { colors } = AppCustomLightTheme; - expect(LightTheme).toMatchObject({ + expect(navLight).toMatchObject({ ...NavigationLightTheme, colors: { ...NavigationLightTheme.colors, @@ -200,14 +200,14 @@ describe('adaptNavigationTheme', () => { }); it('should return adapted navigation dark theme based on custom app dark theme', () => { - const { DarkTheme } = adaptNavigationTheme({ + const { DarkTheme: navDark } = adaptNavigationTheme({ reactNavigationDark: NavigationDarkTheme, materialDark: AppCustomDarkTheme, }); const { colors } = AppCustomDarkTheme; - expect(DarkTheme).toMatchObject({ + expect(navDark).toMatchObject({ ...NavigationDarkTheme, colors: { ...NavigationDarkTheme.colors, @@ -222,53 +222,53 @@ describe('adaptNavigationTheme', () => { }); it('should adapt navigation theme with fonts', () => { - const { LightTheme } = adaptNavigationTheme({ + const { LightTheme: navLight } = adaptNavigationTheme({ reactNavigationLight: NavigationThemeWithFonts, }); - expect(LightTheme).toMatchObject({ + expect(navLight).toMatchObject({ ...NavigationThemeWithFonts, colors: { ...NavigationThemeWithFonts.colors, - primary: MD3LightTheme.colors.primary, - background: MD3LightTheme.colors.background, - card: MD3LightTheme.colors.surfaceContainer, - text: MD3LightTheme.colors.onSurface, - border: MD3LightTheme.colors.outline, - notification: MD3LightTheme.colors.error, + primary: LightTheme.colors.primary, + background: LightTheme.colors.background, + card: LightTheme.colors.surfaceContainer, + text: LightTheme.colors.onSurface, + border: LightTheme.colors.outline, + notification: LightTheme.colors.error, }, fonts: { regular: { - fontFamily: MD3LightTheme.fonts.bodyMedium.fontFamily, - fontWeight: MD3LightTheme.fonts.bodyMedium.fontWeight, - letterSpacing: MD3LightTheme.fonts.bodyMedium.letterSpacing, + fontFamily: LightTheme.fonts.bodyMedium.fontFamily, + fontWeight: LightTheme.fonts.bodyMedium.fontWeight, + letterSpacing: LightTheme.fonts.bodyMedium.letterSpacing, }, medium: { - fontFamily: MD3LightTheme.fonts.titleMedium.fontFamily, - fontWeight: MD3LightTheme.fonts.titleMedium.fontWeight, - letterSpacing: MD3LightTheme.fonts.titleMedium.letterSpacing, + fontFamily: LightTheme.fonts.titleMedium.fontFamily, + fontWeight: LightTheme.fonts.titleMedium.fontWeight, + letterSpacing: LightTheme.fonts.titleMedium.letterSpacing, }, bold: { - fontFamily: MD3LightTheme.fonts.headlineSmall.fontFamily, - fontWeight: MD3LightTheme.fonts.headlineSmall.fontWeight, - letterSpacing: MD3LightTheme.fonts.headlineSmall.letterSpacing, + fontFamily: LightTheme.fonts.headlineSmall.fontFamily, + fontWeight: LightTheme.fonts.headlineSmall.fontWeight, + letterSpacing: LightTheme.fonts.headlineSmall.letterSpacing, }, heavy: { - fontFamily: MD3LightTheme.fonts.headlineLarge.fontFamily, - fontWeight: MD3LightTheme.fonts.headlineLarge.fontWeight, - letterSpacing: MD3LightTheme.fonts.headlineLarge.letterSpacing, + fontFamily: LightTheme.fonts.headlineLarge.fontFamily, + fontWeight: LightTheme.fonts.headlineLarge.fontWeight, + letterSpacing: LightTheme.fonts.headlineLarge.letterSpacing, }, }, }); }); it('should not expect fonts on theme without fonts', () => { - const { LightTheme, DarkTheme } = adaptNavigationTheme({ + const { LightTheme: navLight, DarkTheme: navDark } = adaptNavigationTheme({ reactNavigationLight: NavigationLightTheme, reactNavigationDark: NavigationDarkTheme, }); - expect(LightTheme).not.toHaveProperty('fonts'); - expect(DarkTheme).not.toHaveProperty('fonts'); + expect(navLight).not.toHaveProperty('fonts'); + expect(navDark).not.toHaveProperty('fonts'); }); }); diff --git a/src/core/theming.tsx b/src/core/theming.tsx index 04649515c6..6da3aaf13f 100644 --- a/src/core/theming.tsx +++ b/src/core/theming.tsx @@ -2,18 +2,18 @@ import type { ComponentType } from 'react'; import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; -import { MD3DarkTheme, MD3LightTheme } from '../styles/themes'; -import type { InternalTheme, MD3Theme, NavigationTheme } from '../types'; +import { DarkTheme, LightTheme } from '../styles/themes'; +import type { InternalTheme, Theme, NavigationTheme } from '../types'; -export const DefaultTheme = MD3LightTheme; +export const DefaultTheme = LightTheme; export const { ThemeProvider, withTheme, useTheme: useAppTheme, -} = createTheming(MD3LightTheme); +} = createTheming(LightTheme); -export function useTheme(overrides?: $DeepPartial) { +export function useTheme(overrides?: $DeepPartial) { return useAppTheme(overrides); } @@ -26,8 +26,8 @@ export const withInternalTheme = ( ) => withTheme(WrappedComponent); export const defaultThemes = { - light: MD3LightTheme, - dark: MD3DarkTheme, + light: LightTheme, + dark: DarkTheme, }; export const getTheme = ( @@ -41,14 +41,14 @@ export const getTheme = ( // eslint-disable-next-line no-redeclare export function adaptNavigationTheme(themes: { reactNavigationLight: T; - materialLight?: MD3Theme; + materialLight?: Theme; }): { LightTheme: NavigationTheme; }; // eslint-disable-next-line no-redeclare export function adaptNavigationTheme(themes: { reactNavigationDark: T; - materialDark?: MD3Theme; + materialDark?: Theme; }): { DarkTheme: NavigationTheme; }; @@ -59,8 +59,8 @@ export function adaptNavigationTheme< >(themes: { reactNavigationLight: TLight; reactNavigationDark: TDark; - materialLight?: MD3Theme; - materialDark?: MD3Theme; + materialLight?: Theme; + materialDark?: Theme; }): { LightTheme: TLight; DarkTheme: TDark }; // eslint-disable-next-line no-redeclare export function adaptNavigationTheme(themes: any) { @@ -72,8 +72,8 @@ export function adaptNavigationTheme(themes: any) { } = themes; const MD3Themes = { - light: materialLight || MD3LightTheme, - dark: materialDark || MD3DarkTheme, + light: materialLight || LightTheme, + dark: materialDark || DarkTheme, }; const result: { LightTheme?: any; DarkTheme?: any } = {}; @@ -91,7 +91,7 @@ export function adaptNavigationTheme(themes: any) { const getAdaptedTheme = ( theme: T, - materialTheme: MD3Theme + materialTheme: Theme ): T => { const base = { ...theme, diff --git a/src/deprecated.ts b/src/deprecated.ts new file mode 100644 index 0000000000..06979bdaa1 --- /dev/null +++ b/src/deprecated.ts @@ -0,0 +1,47 @@ +/** + * Compatibility aliases for v5 API names that were renamed in v6. + * + * This file is the single home for every `@deprecated` re-export so that, when + * the time comes to remove them, deletion is a single `rm src/deprecated.ts` + * (plus removing the corresponding re-export block from `src/index.tsx`). + * + * Do not add anything here that isn't a deprecated alias. + */ + +import { DarkTheme } from './styles/themes/DarkTheme'; +import { LightTheme } from './styles/themes/LightTheme'; +import { Palette } from './styles/themes/tokens'; +import { TypescaleKey } from './types'; +import type { Theme, Elevation } from './types'; + +/** + * @deprecated Use `LightTheme` instead. Will be removed in a future version. + */ +export const MD3LightTheme = LightTheme; + +/** + * @deprecated Use `DarkTheme` instead. Will be removed in a future version. + */ +export const MD3DarkTheme = DarkTheme; + +/** + * @deprecated Use `Palette` instead. Will be removed in a future version. + */ +export const MD3Colors = Palette; + +/** + * @deprecated Use `TypescaleKey` instead. Will be removed in a future version. + */ +export const MD3TypescaleKey = TypescaleKey; +// eslint-disable-next-line no-redeclare +export type MD3TypescaleKey = TypescaleKey; + +/** + * @deprecated Use `Theme` instead. Will be removed in a future version. + */ +export type MD3Theme = Theme; + +/** + * @deprecated Use `Elevation` instead. Will be removed in a future version. + */ +export type MD3Elevation = Elevation; diff --git a/src/index.tsx b/src/index.tsx index ec0c4d4786..536e923d4a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,4 @@ -export { MD3Colors } from './styles/themes/v3/tokens'; +export { Palette } from './styles/themes/tokens'; export { useTheme, @@ -13,7 +13,6 @@ export * from './styles/themes'; export { default as Provider } from './core/PaperProvider'; export { default as PaperProvider } from './core/PaperProvider'; export { default as shadow } from './styles/shadow'; -export { default as overlay } from './styles/overlay'; export { default as configureFonts } from './styles/fonts'; import * as Avatar from './components/Avatar/Avatar'; @@ -146,9 +145,18 @@ export type { MaterialBottomTabScreenProps, } from './react-navigation'; -export type { - MD3Theme, - ThemeBase, - MD3Elevation, - MD3TypescaleKey, +export { + TypescaleKey, + type Theme, + type ThemeBase, + type Elevation, } from './types'; + +export { + MD3LightTheme, + MD3DarkTheme, + MD3Colors, + MD3TypescaleKey, + type MD3Theme, + type MD3Elevation, +} from './deprecated'; diff --git a/src/styles/__tests__/fonts.test.js b/src/styles/__tests__/fonts.test.js index 6deed087a4..e632bdbcf0 100644 --- a/src/styles/__tests__/fonts.test.js +++ b/src/styles/__tests__/fonts.test.js @@ -130,7 +130,7 @@ const loadFonts = () => { const fonts = require('../fonts'); configureFonts = fonts.default; - typescale = require('../themes/v3/tokens').typescale; + typescale = require('../themes/tokens').typescale; }); return { configureFonts, typescale }; diff --git a/src/styles/fonts.tsx b/src/styles/fonts.tsx index 8d26388853..a2267fb20d 100644 --- a/src/styles/fonts.tsx +++ b/src/styles/fonts.tsx @@ -1,18 +1,18 @@ -import type { MD3Type, MD3Typescale, MD3TypescaleKey } from '../types'; -import { typescale } from './themes/v3/tokens'; +import type { TypescaleStyle, Typescale, TypescaleKey } from '../types'; +import { typescale } from './themes/tokens'; -type MD3FontsConfig = +type FontsConfig = | { - [key in MD3TypescaleKey]: Partial; + [key in TypescaleKey]: Partial; } | { - [key: string]: MD3Type; + [key: string]: TypescaleStyle; } - | Partial; + | Partial; -function configureV3Fonts( - config: MD3FontsConfig -): MD3Typescale | (MD3Typescale & { [key: string]: MD3Type }) { +function configureFontsConfig( + config: FontsConfig +): Typescale | (Typescale & { [key: string]: TypescaleStyle }) { if (!config) { return typescale; } @@ -27,7 +27,7 @@ function configureV3Fonts( variantName, { ...variantProperties, ...config }, ]) - ) as MD3Typescale; + ) as Typescale; } return Object.assign( @@ -35,7 +35,7 @@ function configureV3Fonts( typescale, ...Object.entries(config).map(([variantName, variantProperties]) => ({ [variantName]: { - ...typescale[variantName as MD3TypescaleKey], + ...typescale[variantName as TypescaleKey], ...variantProperties, }, })) @@ -44,18 +44,18 @@ function configureV3Fonts( // eslint-disable-next-line no-redeclare export default function configureFonts(params?: { - config?: Partial; -}): MD3Typescale; + config?: Partial; +}): Typescale; // eslint-disable-next-line no-redeclare export default function configureFonts(params?: { - config?: Partial>>; -}): MD3Typescale; + config?: Partial>>; +}): Typescale; // eslint-disable-next-line no-redeclare export default function configureFonts(params: { - config: Record; -}): MD3Typescale & { [key: string]: MD3Type }; + config: Record; +}): Typescale & { [key: string]: TypescaleStyle }; // eslint-disable-next-line no-redeclare export default function configureFonts(params?: any) { const { config } = params || {}; - return configureV3Fonts(config); + return configureFontsConfig(config); } diff --git a/src/styles/overlay.tsx b/src/styles/overlay.tsx deleted file mode 100644 index 050e0c06d1..0000000000 --- a/src/styles/overlay.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Animated } from 'react-native'; - -import color from 'color'; - -import { MD3DarkTheme } from './themes'; - -export const isAnimatedValue = ( - it: number | string | Animated.AnimatedInterpolation -): it is Animated.Value => it instanceof Animated.Value; - -export default function overlay( - elevation: T, - surfaceColor: string = MD3DarkTheme.colors.surface -): T extends number ? string : Animated.AnimatedInterpolation { - if (isAnimatedValue(elevation)) { - const inputRange = [0, 1, 2, 3, 8, 24]; - - // @ts-expect-error: TS doesn't seem to refine the type correctly - return elevation.interpolate({ - inputRange, - outputRange: inputRange.map((elevation) => { - return calculateColor(surfaceColor, elevation); - }), - }); - } - - // @ts-expect-error: TS doesn't seem to refine the type correctly - return calculateColor(surfaceColor, elevation); -} - -function calculateColor(surfaceColor: string, elevation: number = 1) { - let overlayTransparency: number; - if (elevation >= 1 && elevation <= 24) { - overlayTransparency = elevationOverlayTransparency[elevation]; - } else if (elevation > 24) { - overlayTransparency = elevationOverlayTransparency[24]; - } else { - overlayTransparency = elevationOverlayTransparency[1]; - } - return color(surfaceColor) - .mix(color('white'), overlayTransparency * 0.01) - .hex(); -} - -const elevationOverlayTransparency: Record = { - 1: 5, - 2: 7, - 3: 8, - 4: 9, - 5: 10, - 6: 11, - 7: 11.5, - 8: 12, - 9: 12.5, - 10: 13, - 11: 13.5, - 12: 14, - 13: 14.25, - 14: 14.5, - 15: 14.75, - 16: 15, - 17: 15.12, - 18: 15.24, - 19: 15.36, - 20: 15.48, - 21: 15.6, - 22: 15.72, - 23: 15.84, - 24: 16, -}; diff --git a/src/styles/shadow.tsx b/src/styles/shadow.tsx index 57cc3612de..32e056fa1f 100644 --- a/src/styles/shadow.tsx +++ b/src/styles/shadow.tsx @@ -1,22 +1,18 @@ import { Animated } from 'react-native'; -import { MD3Colors } from './themes/v3/tokens'; +import { Palette } from './themes/tokens'; -const MD3_SHADOW_OPACITY = 0.3; -const MD3_SHADOW_COLOR = MD3Colors.primary0; +const SHADOW_OPACITY = 0.3; +const SHADOW_COLOR = Palette.primary0; export default function shadow(elevation: number | Animated.Value = 0) { - return v3Shadow(elevation); -} - -function v3Shadow(elevation: number | Animated.Value = 0) { const inputRange = [0, 1, 2, 3, 4, 5]; const shadowHeight = [0, 1, 2, 4, 6, 8]; const shadowRadius = [0, 3, 6, 8, 10, 12]; if (elevation instanceof Animated.Value) { return { - shadowColor: MD3_SHADOW_COLOR, + shadowColor: SHADOW_COLOR, shadowOffset: { width: new Animated.Value(0), height: elevation.interpolate({ @@ -26,7 +22,7 @@ function v3Shadow(elevation: number | Animated.Value = 0) { }, shadowOpacity: elevation.interpolate({ inputRange: [0, 1], - outputRange: [0, MD3_SHADOW_OPACITY], + outputRange: [0, SHADOW_OPACITY], extrapolate: 'clamp', }), shadowRadius: elevation.interpolate({ @@ -36,8 +32,8 @@ function v3Shadow(elevation: number | Animated.Value = 0) { }; } else { return { - shadowColor: MD3_SHADOW_COLOR, - shadowOpacity: elevation ? MD3_SHADOW_OPACITY : 0, + shadowColor: SHADOW_COLOR, + shadowOpacity: elevation ? SHADOW_OPACITY : 0, shadowOffset: { width: 0, height: shadowHeight[elevation], diff --git a/src/styles/themes/v3/DarkTheme.tsx b/src/styles/themes/DarkTheme.tsx similarity index 93% rename from src/styles/themes/v3/DarkTheme.tsx rename to src/styles/themes/DarkTheme.tsx index c9aa34305b..693855680e 100644 --- a/src/styles/themes/v3/DarkTheme.tsx +++ b/src/styles/themes/DarkTheme.tsx @@ -1,13 +1,13 @@ import color from 'color'; -import { MD3LightTheme } from './LightTheme'; +import { LightTheme } from './LightTheme'; import { tokens } from './tokens'; -import type { MD3Theme } from '../../../types'; +import type { Theme } from '../../types'; const { palette, stateOpacity } = tokens.md.ref; -export const MD3DarkTheme: MD3Theme = { - ...MD3LightTheme, +export const DarkTheme: Theme = { + ...LightTheme, dark: true, mode: 'adaptive', colors: { diff --git a/src/styles/themes/DynamicTheme.android.tsx b/src/styles/themes/DynamicTheme.android.tsx index b16b4bd040..f2791670e8 100644 --- a/src/styles/themes/DynamicTheme.android.tsx +++ b/src/styles/themes/DynamicTheme.android.tsx @@ -1,8 +1,8 @@ import { Platform, PlatformColor } from 'react-native'; -import { MD3DarkTheme } from './v3/DarkTheme'; -import { MD3LightTheme } from './v3/LightTheme'; -import type { MD3Theme } from '../../types'; +import { DarkTheme } from './DarkTheme'; +import { LightTheme } from './LightTheme'; +import type { Theme } from '../../types'; const isApi34 = (Platform.Version as number) >= 34; const isApi31 = (Platform.Version as number) >= 31; @@ -29,232 +29,232 @@ const lightColors = { primary: pick( 'system_primary_light', 'system_accent1_600', - MD3LightTheme.colors.primary + LightTheme.colors.primary ), onPrimary: pick( 'system_on_primary_light', 'system_accent1_0', - MD3LightTheme.colors.onPrimary + LightTheme.colors.onPrimary ), primaryContainer: pick( 'system_primary_container_light', 'system_accent1_100', - MD3LightTheme.colors.primaryContainer + LightTheme.colors.primaryContainer ), onPrimaryContainer: pick( 'system_on_primary_container_light', 'system_accent1_900', - MD3LightTheme.colors.onPrimaryContainer + LightTheme.colors.onPrimaryContainer ), inversePrimary: pick( 'system_primary_dark', 'system_accent1_200', - MD3LightTheme.colors.inversePrimary + LightTheme.colors.inversePrimary ), secondary: pick( 'system_secondary_light', 'system_accent2_600', - MD3LightTheme.colors.secondary + LightTheme.colors.secondary ), onSecondary: pick( 'system_on_secondary_light', 'system_accent2_0', - MD3LightTheme.colors.onSecondary + LightTheme.colors.onSecondary ), secondaryContainer: pick( 'system_secondary_container_light', 'system_accent2_100', - MD3LightTheme.colors.secondaryContainer + LightTheme.colors.secondaryContainer ), onSecondaryContainer: pick( 'system_on_secondary_container_light', 'system_accent2_900', - MD3LightTheme.colors.onSecondaryContainer + LightTheme.colors.onSecondaryContainer ), tertiary: pick( 'system_tertiary_light', 'system_accent3_600', - MD3LightTheme.colors.tertiary + LightTheme.colors.tertiary ), onTertiary: pick( 'system_on_tertiary_light', 'system_accent3_0', - MD3LightTheme.colors.onTertiary + LightTheme.colors.onTertiary ), tertiaryContainer: pick( 'system_tertiary_container_light', 'system_accent3_100', - MD3LightTheme.colors.tertiaryContainer + LightTheme.colors.tertiaryContainer ), onTertiaryContainer: pick( 'system_on_tertiary_container_light', 'system_accent3_900', - MD3LightTheme.colors.onTertiaryContainer + LightTheme.colors.onTertiaryContainer ), error: pick( 'system_error_light', - MD3LightTheme.colors.error, - MD3LightTheme.colors.error + LightTheme.colors.error, + LightTheme.colors.error ), onError: pick( 'system_on_error_light', - MD3LightTheme.colors.onError, - MD3LightTheme.colors.onError + LightTheme.colors.onError, + LightTheme.colors.onError ), errorContainer: pick( 'system_error_container_light', - MD3LightTheme.colors.errorContainer, - MD3LightTheme.colors.errorContainer + LightTheme.colors.errorContainer, + LightTheme.colors.errorContainer ), onErrorContainer: pick( 'system_on_error_container_light', - MD3LightTheme.colors.onErrorContainer, - MD3LightTheme.colors.onErrorContainer + LightTheme.colors.onErrorContainer, + LightTheme.colors.onErrorContainer ), onSurface: pick( 'system_on_surface_light', 'system_neutral1_900', - MD3LightTheme.colors.onSurface + LightTheme.colors.onSurface ), onBackground: pick( 'system_on_background_light', 'system_neutral1_900', - MD3LightTheme.colors.onBackground + LightTheme.colors.onBackground ), onSurfaceVariant: pick( 'system_on_surface_variant_light', 'system_neutral2_700', - MD3LightTheme.colors.onSurfaceVariant + LightTheme.colors.onSurfaceVariant ), outline: pick( 'system_outline_light', 'system_neutral2_500', - MD3LightTheme.colors.outline + LightTheme.colors.outline ), outlineVariant: pick( 'system_outline_variant_light', 'system_neutral2_200', - MD3LightTheme.colors.outlineVariant + LightTheme.colors.outlineVariant ), inverseSurface: pick( 'system_surface_dark', 'system_neutral1_800', - MD3LightTheme.colors.inverseSurface + LightTheme.colors.inverseSurface ), inverseOnSurface: pick( 'system_on_surface_dark', 'system_neutral1_50', - MD3LightTheme.colors.inverseOnSurface + LightTheme.colors.inverseOnSurface ), surfaceContainerLowest: pick( 'system_surface_container_lowest_light', 'system_neutral2_0', - MD3LightTheme.colors.surfaceContainerLowest + LightTheme.colors.surfaceContainerLowest ), surfaceContainerLow: pick( 'system_surface_container_low_light', - MD3LightTheme.colors.surfaceContainerLow, - MD3LightTheme.colors.surfaceContainerLow + LightTheme.colors.surfaceContainerLow, + LightTheme.colors.surfaceContainerLow ), surfaceContainerHighest: pick( 'system_surface_container_highest_light', 'system_neutral2_100', - MD3LightTheme.colors.surfaceContainerHighest + LightTheme.colors.surfaceContainerHighest ), surface: pick( 'system_surface_light', - MD3LightTheme.colors.surface, - MD3LightTheme.colors.surface + LightTheme.colors.surface, + LightTheme.colors.surface ), surfaceDim: pick( 'system_surface_dim_light', - MD3LightTheme.colors.surfaceDim, - MD3LightTheme.colors.surfaceDim + LightTheme.colors.surfaceDim, + LightTheme.colors.surfaceDim ), surfaceBright: pick( 'system_surface_bright_light', - MD3LightTheme.colors.surfaceBright, - MD3LightTheme.colors.surfaceBright + LightTheme.colors.surfaceBright, + LightTheme.colors.surfaceBright ), surfaceContainer: pick( 'system_surface_container_light', - MD3LightTheme.colors.surfaceContainer, - MD3LightTheme.colors.surfaceContainer + LightTheme.colors.surfaceContainer, + LightTheme.colors.surfaceContainer ), surfaceContainerHigh: pick( 'system_surface_container_high_light', - MD3LightTheme.colors.surfaceContainerHigh, - MD3LightTheme.colors.surfaceContainerHigh + LightTheme.colors.surfaceContainerHigh, + LightTheme.colors.surfaceContainerHigh ), background: pick( 'system_background_light', - MD3LightTheme.colors.background, - MD3LightTheme.colors.background + LightTheme.colors.background, + LightTheme.colors.background ), surfaceVariant: pick( 'system_surface_variant_light', - MD3LightTheme.colors.surfaceVariant, - MD3LightTheme.colors.surfaceVariant + LightTheme.colors.surfaceVariant, + LightTheme.colors.surfaceVariant ), primaryFixed: pick( 'system_primary_fixed', 'system_accent1_100', - MD3LightTheme.colors.primaryFixed + LightTheme.colors.primaryFixed ), primaryFixedDim: pick( 'system_primary_fixed_dim', 'system_accent1_200', - MD3LightTheme.colors.primaryFixedDim + LightTheme.colors.primaryFixedDim ), onPrimaryFixed: pick( 'system_on_primary_fixed', 'system_accent1_900', - MD3LightTheme.colors.onPrimaryFixed + LightTheme.colors.onPrimaryFixed ), onPrimaryFixedVariant: pick( 'system_on_primary_fixed_variant', 'system_accent1_700', - MD3LightTheme.colors.onPrimaryFixedVariant + LightTheme.colors.onPrimaryFixedVariant ), secondaryFixed: pick( 'system_secondary_fixed', 'system_accent2_100', - MD3LightTheme.colors.secondaryFixed + LightTheme.colors.secondaryFixed ), secondaryFixedDim: pick( 'system_secondary_fixed_dim', 'system_accent2_200', - MD3LightTheme.colors.secondaryFixedDim + LightTheme.colors.secondaryFixedDim ), onSecondaryFixed: pick( 'system_on_secondary_fixed', 'system_accent2_900', - MD3LightTheme.colors.onSecondaryFixed + LightTheme.colors.onSecondaryFixed ), onSecondaryFixedVariant: pick( 'system_on_secondary_fixed_variant', 'system_accent2_700', - MD3LightTheme.colors.onSecondaryFixedVariant + LightTheme.colors.onSecondaryFixedVariant ), tertiaryFixed: pick( 'system_tertiary_fixed', 'system_accent3_100', - MD3LightTheme.colors.tertiaryFixed + LightTheme.colors.tertiaryFixed ), tertiaryFixedDim: pick( 'system_tertiary_fixed_dim', 'system_accent3_200', - MD3LightTheme.colors.tertiaryFixedDim + LightTheme.colors.tertiaryFixedDim ), onTertiaryFixed: pick( 'system_on_tertiary_fixed', 'system_accent3_900', - MD3LightTheme.colors.onTertiaryFixed + LightTheme.colors.onTertiaryFixed ), onTertiaryFixedVariant: pick( 'system_on_tertiary_fixed_variant', 'system_accent3_700', - MD3LightTheme.colors.onTertiaryFixedVariant + LightTheme.colors.onTertiaryFixedVariant ), }; @@ -262,241 +262,241 @@ const darkColors = { primary: pick( 'system_primary_dark', 'system_accent1_200', - MD3DarkTheme.colors.primary + DarkTheme.colors.primary ), onPrimary: pick( 'system_on_primary_dark', 'system_accent1_800', - MD3DarkTheme.colors.onPrimary + DarkTheme.colors.onPrimary ), primaryContainer: pick( 'system_primary_container_dark', 'system_accent1_700', - MD3DarkTheme.colors.primaryContainer + DarkTheme.colors.primaryContainer ), onPrimaryContainer: pick( 'system_on_primary_container_dark', 'system_accent1_100', - MD3DarkTheme.colors.onPrimaryContainer + DarkTheme.colors.onPrimaryContainer ), inversePrimary: pick( 'system_primary_light', 'system_accent1_600', - MD3DarkTheme.colors.inversePrimary + DarkTheme.colors.inversePrimary ), secondary: pick( 'system_secondary_dark', 'system_accent2_200', - MD3DarkTheme.colors.secondary + DarkTheme.colors.secondary ), onSecondary: pick( 'system_on_secondary_dark', 'system_accent2_800', - MD3DarkTheme.colors.onSecondary + DarkTheme.colors.onSecondary ), secondaryContainer: pick( 'system_secondary_container_dark', 'system_accent2_700', - MD3DarkTheme.colors.secondaryContainer + DarkTheme.colors.secondaryContainer ), onSecondaryContainer: pick( 'system_on_secondary_container_dark', 'system_accent2_100', - MD3DarkTheme.colors.onSecondaryContainer + DarkTheme.colors.onSecondaryContainer ), tertiary: pick( 'system_tertiary_dark', 'system_accent3_200', - MD3DarkTheme.colors.tertiary + DarkTheme.colors.tertiary ), onTertiary: pick( 'system_on_tertiary_dark', 'system_accent3_800', - MD3DarkTheme.colors.onTertiary + DarkTheme.colors.onTertiary ), tertiaryContainer: pick( 'system_tertiary_container_dark', 'system_accent3_700', - MD3DarkTheme.colors.tertiaryContainer + DarkTheme.colors.tertiaryContainer ), onTertiaryContainer: pick( 'system_on_tertiary_container_dark', 'system_accent3_100', - MD3DarkTheme.colors.onTertiaryContainer + DarkTheme.colors.onTertiaryContainer ), error: pick( 'system_error_dark', - MD3DarkTheme.colors.error, - MD3DarkTheme.colors.error + DarkTheme.colors.error, + DarkTheme.colors.error ), onError: pick( 'system_on_error_dark', - MD3DarkTheme.colors.onError, - MD3DarkTheme.colors.onError + DarkTheme.colors.onError, + DarkTheme.colors.onError ), errorContainer: pick( 'system_error_container_dark', - MD3DarkTheme.colors.errorContainer, - MD3DarkTheme.colors.errorContainer + DarkTheme.colors.errorContainer, + DarkTheme.colors.errorContainer ), onErrorContainer: pick( 'system_on_error_container_dark', - MD3DarkTheme.colors.onErrorContainer, - MD3DarkTheme.colors.onErrorContainer + DarkTheme.colors.onErrorContainer, + DarkTheme.colors.onErrorContainer ), onSurface: pick( 'system_on_surface_dark', 'system_neutral1_100', - MD3DarkTheme.colors.onSurface + DarkTheme.colors.onSurface ), onBackground: pick( 'system_on_background_dark', 'system_neutral1_100', - MD3DarkTheme.colors.onBackground + DarkTheme.colors.onBackground ), onSurfaceVariant: pick( 'system_on_surface_variant_dark', 'system_neutral2_200', - MD3DarkTheme.colors.onSurfaceVariant + DarkTheme.colors.onSurfaceVariant ), outline: pick( 'system_outline_dark', 'system_neutral2_400', - MD3DarkTheme.colors.outline + DarkTheme.colors.outline ), outlineVariant: pick( 'system_outline_variant_dark', 'system_neutral2_700', - MD3DarkTheme.colors.outlineVariant + DarkTheme.colors.outlineVariant ), inverseSurface: pick( 'system_surface_light', 'system_neutral1_100', - MD3DarkTheme.colors.inverseSurface + DarkTheme.colors.inverseSurface ), inverseOnSurface: pick( 'system_on_surface_light', 'system_neutral1_800', - MD3DarkTheme.colors.inverseOnSurface + DarkTheme.colors.inverseOnSurface ), surfaceContainerLowest: pick( 'system_surface_container_lowest_dark', - MD3DarkTheme.colors.surfaceContainerLowest, - MD3DarkTheme.colors.surfaceContainerLowest + DarkTheme.colors.surfaceContainerLowest, + DarkTheme.colors.surfaceContainerLowest ), surfaceContainerLow: pick( 'system_surface_container_low_dark', 'system_neutral2_900', - MD3DarkTheme.colors.surfaceContainerLow + DarkTheme.colors.surfaceContainerLow ), surfaceContainerHighest: pick( 'system_surface_container_highest_dark', - MD3DarkTheme.colors.surfaceContainerHighest, - MD3DarkTheme.colors.surfaceContainerHighest + DarkTheme.colors.surfaceContainerHighest, + DarkTheme.colors.surfaceContainerHighest ), surface: pick( 'system_surface_dark', - MD3DarkTheme.colors.surface, - MD3DarkTheme.colors.surface + DarkTheme.colors.surface, + DarkTheme.colors.surface ), surfaceDim: pick( 'system_surface_dim_dark', - MD3DarkTheme.colors.surfaceDim, - MD3DarkTheme.colors.surfaceDim + DarkTheme.colors.surfaceDim, + DarkTheme.colors.surfaceDim ), surfaceBright: pick( 'system_surface_bright_dark', - MD3DarkTheme.colors.surfaceBright, - MD3DarkTheme.colors.surfaceBright + DarkTheme.colors.surfaceBright, + DarkTheme.colors.surfaceBright ), surfaceContainer: pick( 'system_surface_container_dark', - MD3DarkTheme.colors.surfaceContainer, - MD3DarkTheme.colors.surfaceContainer + DarkTheme.colors.surfaceContainer, + DarkTheme.colors.surfaceContainer ), surfaceContainerHigh: pick( 'system_surface_container_high_dark', - MD3DarkTheme.colors.surfaceContainerHigh, - MD3DarkTheme.colors.surfaceContainerHigh + DarkTheme.colors.surfaceContainerHigh, + DarkTheme.colors.surfaceContainerHigh ), background: pick( 'system_background_dark', - MD3DarkTheme.colors.background, - MD3DarkTheme.colors.background + DarkTheme.colors.background, + DarkTheme.colors.background ), surfaceVariant: pick( 'system_surface_variant_dark', - MD3DarkTheme.colors.surfaceVariant, - MD3DarkTheme.colors.surfaceVariant + DarkTheme.colors.surfaceVariant, + DarkTheme.colors.surfaceVariant ), primaryFixed: pick( 'system_primary_fixed', 'system_accent1_100', - MD3DarkTheme.colors.primaryFixed + DarkTheme.colors.primaryFixed ), primaryFixedDim: pick( 'system_primary_fixed_dim', 'system_accent1_200', - MD3DarkTheme.colors.primaryFixedDim + DarkTheme.colors.primaryFixedDim ), onPrimaryFixed: pick( 'system_on_primary_fixed', 'system_accent1_900', - MD3DarkTheme.colors.onPrimaryFixed + DarkTheme.colors.onPrimaryFixed ), onPrimaryFixedVariant: pick( 'system_on_primary_fixed_variant', 'system_accent1_700', - MD3DarkTheme.colors.onPrimaryFixedVariant + DarkTheme.colors.onPrimaryFixedVariant ), secondaryFixed: pick( 'system_secondary_fixed', 'system_accent2_100', - MD3DarkTheme.colors.secondaryFixed + DarkTheme.colors.secondaryFixed ), secondaryFixedDim: pick( 'system_secondary_fixed_dim', 'system_accent2_200', - MD3DarkTheme.colors.secondaryFixedDim + DarkTheme.colors.secondaryFixedDim ), onSecondaryFixed: pick( 'system_on_secondary_fixed', 'system_accent2_900', - MD3DarkTheme.colors.onSecondaryFixed + DarkTheme.colors.onSecondaryFixed ), onSecondaryFixedVariant: pick( 'system_on_secondary_fixed_variant', 'system_accent2_700', - MD3DarkTheme.colors.onSecondaryFixedVariant + DarkTheme.colors.onSecondaryFixedVariant ), tertiaryFixed: pick( 'system_tertiary_fixed', 'system_accent3_100', - MD3DarkTheme.colors.tertiaryFixed + DarkTheme.colors.tertiaryFixed ), tertiaryFixedDim: pick( 'system_tertiary_fixed_dim', 'system_accent3_200', - MD3DarkTheme.colors.tertiaryFixedDim + DarkTheme.colors.tertiaryFixedDim ), onTertiaryFixed: pick( 'system_on_tertiary_fixed', 'system_accent3_900', - MD3DarkTheme.colors.onTertiaryFixed + DarkTheme.colors.onTertiaryFixed ), onTertiaryFixedVariant: pick( 'system_on_tertiary_fixed_variant', 'system_accent3_700', - MD3DarkTheme.colors.onTertiaryFixedVariant + DarkTheme.colors.onTertiaryFixedVariant ), }; -export const DynamicLightTheme: MD3Theme = { - ...MD3LightTheme, - colors: { ...MD3LightTheme.colors, ...lightColors }, +export const DynamicLightTheme: Theme = { + ...LightTheme, + colors: { ...LightTheme.colors, ...lightColors }, }; -export const DynamicDarkTheme: MD3Theme = { - ...MD3DarkTheme, - colors: { ...MD3DarkTheme.colors, ...darkColors }, +export const DynamicDarkTheme: Theme = { + ...DarkTheme, + colors: { ...DarkTheme.colors, ...darkColors }, }; diff --git a/src/styles/themes/DynamicTheme.tsx b/src/styles/themes/DynamicTheme.tsx index 6669c63a45..d932f5a624 100644 --- a/src/styles/themes/DynamicTheme.tsx +++ b/src/styles/themes/DynamicTheme.tsx @@ -1,2 +1,2 @@ -export { MD3DarkTheme as MD3DynamicDarkTheme } from './v3/DarkTheme'; -export { MD3LightTheme as MD3DynamicLightTheme } from './v3/LightTheme'; +export { DarkTheme as DynamicDarkTheme } from './DarkTheme'; +export { LightTheme as DynamicLightTheme } from './LightTheme'; diff --git a/src/styles/themes/v3/LightTheme.tsx b/src/styles/themes/LightTheme.tsx similarity index 94% rename from src/styles/themes/v3/LightTheme.tsx rename to src/styles/themes/LightTheme.tsx index 92924717f1..5566b9fadf 100644 --- a/src/styles/themes/v3/LightTheme.tsx +++ b/src/styles/themes/LightTheme.tsx @@ -1,12 +1,12 @@ import color from 'color'; import { tokens } from './tokens'; -import type { MD3Theme } from '../../../types'; -import configureFonts from '../../fonts'; +import type { Theme } from '../../types'; +import configureFonts from '../fonts'; const { palette, stateOpacity } = tokens.md.ref; -export const MD3LightTheme: MD3Theme = { +export const LightTheme: Theme = { dark: false, roundness: 4, colors: { diff --git a/src/styles/themes/v2/colors.tsx b/src/styles/themes/colors.tsx similarity index 100% rename from src/styles/themes/v2/colors.tsx rename to src/styles/themes/colors.tsx diff --git a/src/styles/themes/index.ts b/src/styles/themes/index.ts index f992ef7e73..4063fcba08 100644 --- a/src/styles/themes/index.ts +++ b/src/styles/themes/index.ts @@ -1,3 +1,3 @@ -export { MD3LightTheme } from './v3/LightTheme'; -export { MD3DarkTheme } from './v3/DarkTheme'; +export { LightTheme } from './LightTheme'; +export { DarkTheme } from './DarkTheme'; export { DynamicLightTheme, DynamicDarkTheme } from './DynamicTheme'; diff --git a/src/styles/themes/v3/tokens.tsx b/src/styles/themes/tokens.tsx similarity index 98% rename from src/styles/themes/v3/tokens.tsx rename to src/styles/themes/tokens.tsx index 12486aca53..987bca6ace 100644 --- a/src/styles/themes/v3/tokens.tsx +++ b/src/styles/themes/tokens.tsx @@ -1,6 +1,6 @@ import { Platform } from 'react-native'; -import type { Font } from '../../../types'; +import type { Font } from '../../types'; const ref = { palette: { @@ -250,4 +250,4 @@ export const tokens = { }, }; -export const MD3Colors = ref.palette; +export const Palette = ref.palette; diff --git a/src/types.tsx b/src/types.tsx index 9206ec1555..b424142b86 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -28,7 +28,7 @@ export type Fonts = { type Mode = 'adaptive' | 'exact'; -export type MD3Colors = { +export type ThemeColors = { primary: string; primaryContainer: string; secondary: string; @@ -83,7 +83,7 @@ export type MD3Colors = { * TODO: revisit after https://github.com/facebook/react-native/pull/56395 * @see https://m3.material.io/foundations/interaction/states/state-layers */ stateLayerPressed: string; - elevation: MD3ElevationColors; + elevation: ElevationColors; }; export type ThemeProp = $DeepPartial; @@ -98,15 +98,14 @@ export type ThemeBase = { }; }; -export type MD3Theme = ThemeBase & { - colors: MD3Colors; - fonts: MD3Typescale; +export type Theme = ThemeBase & { + colors: ThemeColors; + fonts: Typescale; }; -export type InternalTheme = MD3Theme; +export type InternalTheme = Theme; -// MD3 types -export enum MD3TypescaleKey { +export enum TypescaleKey { displayLarge = 'displayLarge', displayMedium = 'displayMedium', displaySmall = 'displaySmall', @@ -128,7 +127,7 @@ export enum MD3TypescaleKey { bodySmall = 'bodySmall', } -export type MD3Type = { +export type TypescaleStyle = { fontFamily: string; letterSpacing: number; fontWeight: Font['fontWeight']; @@ -137,14 +136,14 @@ export type MD3Type = { fontStyle?: Font['fontStyle']; }; -export type MD3Typescale = +export type Typescale = | { - [key in MD3TypescaleKey]: MD3Type; + [key in TypescaleKey]: TypescaleStyle; } & { - ['default']: Omit; + ['default']: Omit; }; -export type MD3Elevation = 0 | 1 | 2 | 3 | 4 | 5; +export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; export enum ElevationLevels { 'level0', @@ -155,7 +154,7 @@ export enum ElevationLevels { 'level5', } -export type MD3ElevationColors = { +export type ElevationColors = { [key in keyof typeof ElevationLevels]: string; }; diff --git a/src/utils/animations.ts b/src/utils/animations.ts new file mode 100644 index 0000000000..7af142bbf3 --- /dev/null +++ b/src/utils/animations.ts @@ -0,0 +1,5 @@ +import { Animated } from 'react-native'; + +export const isAnimatedValue = ( + it: number | string | Animated.AnimatedInterpolation +): it is Animated.Value => it instanceof Animated.Value; From 2a0072e3e700018afe4d11ad817886cfe5558a64 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Mon, 11 May 2026 09:10:29 +0300 Subject: [PATCH 07/94] refactor: move styles/ into src/theme/ tree (#4915) --- src/components/Appbar/AppbarHeader.tsx | 2 +- src/components/Appbar/utils.ts | 2 +- src/components/Avatar/AvatarIcon.tsx | 2 +- src/components/Avatar/AvatarText.tsx | 2 +- src/components/Button/utils.tsx | 4 +- src/components/Card/CardCover.tsx | 2 +- src/components/Checkbox/CheckboxItem.tsx | 2 +- src/components/Checkbox/utils.ts | 2 +- src/components/Chip/Chip.tsx | 2 +- src/components/Chip/helpers.tsx | 2 +- src/components/Drawer/DrawerSection.tsx | 2 +- src/components/HelperText/utils.ts | 2 +- src/components/IconButton/utils.ts | 2 +- src/components/MaterialCommunityIcon.tsx | 2 +- src/components/Menu/utils.ts | 2 +- src/components/Modal.tsx | 2 +- .../RadioButton/RadioButtonItem.tsx | 2 +- src/components/SegmentedButtons/utils.ts | 2 +- src/components/Surface.tsx | 2 +- src/components/Switch/utils.ts | 2 +- src/components/TextInput/Adornment/utils.ts | 2 +- src/components/TextInput/helpers.tsx | 2 +- src/components/__tests__/AnimatedFAB.test.tsx | 2 +- .../__tests__/Appbar/Appbar.test.tsx | 2 +- src/components/__tests__/Avatar.test.tsx | 2 +- src/components/__tests__/Badge.test.tsx | 2 +- .../__tests__/BottomNavigation.test.tsx | 2 +- src/components/__tests__/Button.test.tsx | 4 +- src/components/__tests__/Card/Card.test.tsx | 2 +- .../__tests__/Checkbox/utils.test.tsx | 2 +- src/components/__tests__/Chip.test.tsx | 2 +- src/components/__tests__/IconButton.test.tsx | 4 +- .../__tests__/ListAccordion.test.tsx | 2 +- src/components/__tests__/ListItem.test.tsx | 2 +- src/components/__tests__/ListSection.test.tsx | 2 +- src/components/__tests__/MenuItem.test.tsx | 2 +- src/components/__tests__/Modal.test.tsx | 4 +- .../__tests__/SegmentedButton.test.tsx | 2 +- src/components/__tests__/Snackbar.test.tsx | 2 +- src/components/__tests__/Switch.test.tsx | 2 +- src/components/__tests__/TextInput.test.tsx | 4 +- .../__tests__/Typography/Text.test.tsx | 6 +- src/core/__tests__/PaperProvider.test.tsx | 2 +- src/core/__tests__/theming.test.tsx | 2 +- src/core/theming.tsx | 139 +------------- src/deprecated.ts | 6 +- src/index.tsx | 8 +- src/{styles => theme}/__tests__/fonts.test.js | 2 +- src/{styles/themes => theme}/colors.tsx | 0 src/{styles => theme}/fonts.tsx | 2 +- src/theme/provider.tsx | 138 ++++++++++++++ .../themes => theme/schemes}/DarkTheme.tsx | 2 +- .../schemes}/DynamicTheme.android.tsx | 0 .../themes => theme/schemes}/DynamicTheme.tsx | 0 .../themes => theme/schemes}/LightTheme.tsx | 2 +- src/{styles/themes => theme/schemes}/index.ts | 0 src/{styles => theme}/shadow.tsx | 2 +- .../tokens.tsx => theme/tokens/index.ts} | 0 src/theme/types.ts | 179 +++++++++++++++++ src/types.tsx | 180 +----------------- 60 files changed, 382 insertions(+), 380 deletions(-) rename src/{styles => theme}/__tests__/fonts.test.js (98%) rename src/{styles/themes => theme}/colors.tsx (100%) rename src/{styles => theme}/fonts.tsx (97%) create mode 100644 src/theme/provider.tsx rename src/{styles/themes => theme/schemes}/DarkTheme.tsx (98%) rename src/{styles/themes => theme/schemes}/DynamicTheme.android.tsx (100%) rename src/{styles/themes => theme/schemes}/DynamicTheme.tsx (100%) rename src/{styles/themes => theme/schemes}/LightTheme.tsx (98%) rename src/{styles/themes => theme/schemes}/index.ts (100%) rename src/{styles => theme}/shadow.tsx (96%) rename src/{styles/themes/tokens.tsx => theme/tokens/index.ts} (100%) create mode 100644 src/theme/types.ts diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index 9efe81bba1..ed4ecce48d 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -18,7 +18,7 @@ import { getAppbarBorders, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import shadow from '../../styles/shadow'; +import shadow from '../../theme/shadow'; import type { ThemeProp } from '../../types'; export type Props = Omit< diff --git a/src/components/Appbar/utils.ts b/src/components/Appbar/utils.ts index fe40f1129c..fbd2e7f461 100644 --- a/src/components/Appbar/utils.ts +++ b/src/components/Appbar/utils.ts @@ -2,7 +2,7 @@ import React from 'react'; import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import { StyleSheet, Animated } from 'react-native'; -import { white } from '../../styles/themes/colors'; +import { white } from '../../theme/colors'; import type { InternalTheme, Theme, ThemeProp } from '../../types'; export type AppbarModes = 'small' | 'medium' | 'large' | 'center-aligned'; diff --git a/src/components/Avatar/AvatarIcon.tsx b/src/components/Avatar/AvatarIcon.tsx index 80ae6f68d3..44e543561a 100644 --- a/src/components/Avatar/AvatarIcon.tsx +++ b/src/components/Avatar/AvatarIcon.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import { white } from '../../styles/themes/colors'; +import { white } from '../../theme/colors'; import type { ThemeProp } from '../../types'; import getContrastingColor from '../../utils/getContrastingColor'; import Icon, { IconSource } from '../Icon'; diff --git a/src/components/Avatar/AvatarText.tsx b/src/components/Avatar/AvatarText.tsx index b6bd27623a..4014dbb3b0 100644 --- a/src/components/Avatar/AvatarText.tsx +++ b/src/components/Avatar/AvatarText.tsx @@ -9,7 +9,7 @@ import { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import { white } from '../../styles/themes/colors'; +import { white } from '../../theme/colors'; import type { ThemeProp } from '../../types'; import getContrastingColor from '../../utils/getContrastingColor'; import Text from '../Typography/Text'; diff --git a/src/components/Button/utils.tsx b/src/components/Button/utils.tsx index ff0dd218fd..37038afbd7 100644 --- a/src/components/Button/utils.tsx +++ b/src/components/Button/utils.tsx @@ -1,7 +1,7 @@ import type { ViewStyle } from 'react-native'; -import { black, white } from '../../styles/themes/colors'; -import { tokens } from '../../styles/themes/tokens'; +import { black, white } from '../../theme/colors'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme, Theme } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; diff --git a/src/components/Card/CardCover.tsx b/src/components/Card/CardCover.tsx index 5364c09068..43d01cd852 100644 --- a/src/components/Card/CardCover.tsx +++ b/src/components/Card/CardCover.tsx @@ -3,7 +3,7 @@ import { Image, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { getCardCoverStyle } from './utils'; import { useInternalTheme } from '../../core/theming'; -import { grey200 } from '../../styles/themes/colors'; +import { grey200 } from '../../theme/colors'; import type { ThemeProp } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 4ad9eafa53..008f03640d 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -13,7 +13,7 @@ import Checkbox from './Checkbox'; import CheckboxAndroid from './CheckboxAndroid'; import CheckboxIOS from './CheckboxIOS'; import { useInternalTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple, { Props as TouchableRippleProps, diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index 025717f308..626b45e1d2 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index b6e3e1d282..fe3b30ab49 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -17,7 +17,7 @@ import useLatestCallback from 'use-latest-callback'; import { ChipAvatarProps, getChipColors } from './helpers'; import { useInternalTheme } from '../../core/theming'; -import { white } from '../../styles/themes/colors'; +import { white } from '../../theme/colors'; import type { $Omit, EllipsizeProp, Theme, ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import type { IconSource } from '../Icon'; diff --git a/src/components/Chip/helpers.tsx b/src/components/Chip/helpers.tsx index 4990760d3c..af72870edf 100644 --- a/src/components/Chip/helpers.tsx +++ b/src/components/Chip/helpers.tsx @@ -2,7 +2,7 @@ import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import color from 'color'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme, Theme } from '../../types'; const md3 = (theme: InternalTheme) => theme as Theme; diff --git a/src/components/Drawer/DrawerSection.tsx b/src/components/Drawer/DrawerSection.tsx index 891a5aee98..14a44dcd6f 100644 --- a/src/components/Drawer/DrawerSection.tsx +++ b/src/components/Drawer/DrawerSection.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import { Palette } from '../../styles/themes/tokens'; +import { Palette } from '../../theme/tokens'; import type { ThemeProp } from '../../types'; import Divider from '../Divider'; import Text from '../Typography/Text'; diff --git a/src/components/HelperText/utils.ts b/src/components/HelperText/utils.ts index 214948c767..6c7fe16a58 100644 --- a/src/components/HelperText/utils.ts +++ b/src/components/HelperText/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/IconButton/utils.ts b/src/components/IconButton/utils.ts index 53586925b7..63ef8022c7 100644 --- a/src/components/IconButton/utils.ts +++ b/src/components/IconButton/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/MaterialCommunityIcon.tsx b/src/components/MaterialCommunityIcon.tsx index 9426be685b..8c1704a3de 100644 --- a/src/components/MaterialCommunityIcon.tsx +++ b/src/components/MaterialCommunityIcon.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ComponentProps } from 'react'; import { StyleSheet, Text, Platform, Role, ViewProps } from 'react-native'; -import { black } from '../styles/themes/colors'; +import { black } from '../theme/colors'; export type IconProps = { name: ComponentProps['name']; diff --git a/src/components/Menu/utils.ts b/src/components/Menu/utils.ts index 4288676bbd..9d17a863e6 100644 --- a/src/components/Menu/utils.ts +++ b/src/components/Menu/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; import type { IconSource } from '../Icon'; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 368f9e245f..040c3a5706 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -14,7 +14,7 @@ import useLatestCallback from 'use-latest-callback'; import Surface from './Surface'; import { useInternalTheme } from '../core/theming'; -import { tokens } from '../styles/themes/tokens'; +import { tokens } from '../theme/tokens'; import type { ThemeProp } from '../types'; import { addEventListener } from '../utils/addEventListener'; import { BackHandler } from '../utils/BackHandler/BackHandler'; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index fc67da6131..fe9b6843a8 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -15,7 +15,7 @@ import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; import RadioButtonIOS from './RadioButtonIOS'; import { handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple, { Props as TouchableRippleProps, diff --git a/src/components/SegmentedButtons/utils.ts b/src/components/SegmentedButtons/utils.ts index b2772989ae..af12651971 100644 --- a/src/components/SegmentedButtons/utils.ts +++ b/src/components/SegmentedButtons/utils.ts @@ -1,6 +1,6 @@ import { ViewStyle } from 'react-native'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index 16350b8770..7dcfd0a6cd 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -10,7 +10,7 @@ import { } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import shadow from '../styles/shadow'; +import shadow from '../theme/shadow'; import type { Elevation, Theme, ThemeProp } from '../types'; import { isAnimatedValue } from '../utils/animations'; import { forwardRef } from '../utils/forwardRef'; diff --git a/src/components/Switch/utils.ts b/src/components/Switch/utils.ts index 4672e2b7f0..7b05c42ee2 100644 --- a/src/components/Switch/utils.ts +++ b/src/components/Switch/utils.ts @@ -9,7 +9,7 @@ import { grey700, white, black, -} from '../../styles/themes/colors'; +} from '../../theme/colors'; import type { InternalTheme } from '../../types'; type BaseProps = { diff --git a/src/components/TextInput/Adornment/utils.ts b/src/components/TextInput/Adornment/utils.ts index aaf20a221c..40acdafbdb 100644 --- a/src/components/TextInput/Adornment/utils.ts +++ b/src/components/TextInput/Adornment/utils.ts @@ -1,4 +1,4 @@ -import { tokens } from '../../../styles/themes/tokens'; +import { tokens } from '../../../theme/tokens'; import type { InternalTheme } from '../../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/TextInput/helpers.tsx b/src/components/TextInput/helpers.tsx index 24c3fe079c..633c03f279 100644 --- a/src/components/TextInput/helpers.tsx +++ b/src/components/TextInput/helpers.tsx @@ -14,7 +14,7 @@ import { MD3_OUTLINED_INPUT_OFFSET, } from './constants'; import type { TextInputLabelProp } from './types'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; diff --git a/src/components/__tests__/AnimatedFAB.test.tsx b/src/components/__tests__/AnimatedFAB.test.tsx index e2209cf424..4017f019fc 100644 --- a/src/components/__tests__/AnimatedFAB.test.tsx +++ b/src/components/__tests__/AnimatedFAB.test.tsx @@ -6,7 +6,7 @@ import { Animated, StyleSheet } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; import { act } from 'react-test-renderer'; -import { Palette } from '../../styles/themes/tokens'; +import { Palette } from '../../theme/tokens'; import AnimatedFAB from '../FAB/AnimatedFAB'; const styles = StyleSheet.create({ diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 8f8600f9bb..0419e18e57 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -5,7 +5,7 @@ import { act, render } from '@testing-library/react-native'; import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'; import { getTheme } from '../../../core/theming'; -import { tokens } from '../../../styles/themes/tokens'; +import { tokens } from '../../../theme/tokens'; import Appbar from '../../Appbar'; import { getAppbarBackgroundColor, diff --git a/src/components/__tests__/Avatar.test.tsx b/src/components/__tests__/Avatar.test.tsx index c339e5b95f..d21100c0db 100644 --- a/src/components/__tests__/Avatar.test.tsx +++ b/src/components/__tests__/Avatar.test.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/colors'; +import { red500 } from '../../theme/colors'; import * as Avatar from '../Avatar/Avatar'; const styles = StyleSheet.create({ diff --git a/src/components/__tests__/Badge.test.tsx b/src/components/__tests__/Badge.test.tsx index 8fde099736..2beabe9eae 100644 --- a/src/components/__tests__/Badge.test.tsx +++ b/src/components/__tests__/Badge.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/colors'; +import { red500 } from '../../theme/colors'; import Badge from '../Badge'; it('renders badge', () => { diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index be08d7fc17..496987e254 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -4,7 +4,7 @@ import { Animated, Easing, Platform, StyleSheet } from 'react-native'; import { act, fireEvent, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { Palette } from '../../styles/themes/tokens'; +import { Palette } from '../../theme/tokens'; import BottomNavigation from '../BottomNavigation/BottomNavigation'; import BottomNavigationRouteScreen from '../BottomNavigation/BottomNavigationRouteScreen'; import { diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 894d7ce8e2..a203e1d237 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -4,8 +4,8 @@ import { Animated, StyleSheet } from 'react-native'; import { act, fireEvent, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { pink500, white } from '../../styles/themes/colors'; -import { tokens } from '../../styles/themes/tokens'; +import { pink500, white } from '../../theme/colors'; +import { tokens } from '../../theme/tokens'; import Button from '../Button/Button'; import { getButtonColors } from '../Button/utils'; diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index e8df99d568..fb61ca9602 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -4,7 +4,7 @@ import { Animated, StyleSheet, Text } from 'react-native'; import { act, render } from '@testing-library/react-native'; import { getTheme } from '../../../core/theming'; -import { Palette } from '../../../styles/themes/tokens'; +import { Palette } from '../../../theme/tokens'; import Button from '../../Button/Button'; import Card from '../../Card/Card'; import { getCardColors, getCardCoverStyle } from '../../Card/utils'; diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index eca368146b..a18dd2f78e 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,5 +1,5 @@ import { getTheme } from '../../../core/theming'; -import { tokens } from '../../../styles/themes/tokens'; +import { tokens } from '../../../theme/tokens'; import { getAndroidSelectionControlColor, getSelectionControlIOSColor, diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 1459a9151d..82841001ff 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -5,7 +5,7 @@ import { act, render } from '@testing-library/react-native'; import color from 'color'; import { getTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import Chip from '../Chip/Chip'; import { getChipColors } from '../Chip/helpers'; diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index 88faf4ab78..229b8b462e 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -4,8 +4,8 @@ import { Animated, StyleSheet } from 'react-native'; import { act, render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { pink500 } from '../../styles/themes/colors'; -import { tokens } from '../../styles/themes/tokens'; +import { pink500 } from '../../theme/colors'; +import { tokens } from '../../theme/tokens'; import IconButton from '../IconButton/IconButton'; import { getIconButtonColor } from '../IconButton/utils'; diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index fedf9670be..93e3c62bad 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -4,7 +4,7 @@ import { StyleSheet, View } from 'react-native'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { red500 } from '../../styles/themes/colors'; +import { red500 } from '../../theme/colors'; import ListAccordion from '../List/ListAccordion'; import ListAccordionGroup from '../List/ListAccordionGroup'; import ListIcon from '../List/ListIcon'; diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index 8f00f441ae..c2b7f0d88d 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -4,7 +4,7 @@ import { Text, View } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/colors'; +import { red500 } from '../../theme/colors'; import Chip from '../Chip/Chip'; import IconButton from '../IconButton/IconButton'; import ListIcon from '../List/ListIcon'; diff --git a/src/components/__tests__/ListSection.test.tsx b/src/components/__tests__/ListSection.test.tsx index d09a0aa4a7..91efbfc008 100644 --- a/src/components/__tests__/ListSection.test.tsx +++ b/src/components/__tests__/ListSection.test.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import { render } from '@testing-library/react-native'; -import { red500 } from '../../styles/themes/colors'; +import { red500 } from '../../theme/colors'; import ListIcon from '../List/ListIcon'; import ListItem from '../List/ListItem'; import ListSection from '../List/ListSection'; diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index 034645d2fd..98d433723d 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index a7d442efe3..9fd9eef832 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -8,8 +8,8 @@ import { import { act, fireEvent, render } from '@testing-library/react-native'; -import { LightTheme } from '../../styles/themes'; -import { tokens } from '../../styles/themes/tokens'; +import { LightTheme } from '../../theme/schemes'; +import { tokens } from '../../theme/tokens'; import Modal from '../Modal'; const { scrimAlpha } = tokens.md.ref; diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 994c26d765..06a7390626 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { tokens } from '../../styles/themes/tokens'; +import { tokens } from '../../theme/tokens'; import SegmentedButtons from '../SegmentedButtons/SegmentedButtons'; import { getDisabledSegmentedButtonStyle, diff --git a/src/components/__tests__/Snackbar.test.tsx b/src/components/__tests__/Snackbar.test.tsx index 425b918495..a5938d516b 100644 --- a/src/components/__tests__/Snackbar.test.tsx +++ b/src/components/__tests__/Snackbar.test.tsx @@ -3,7 +3,7 @@ import { Animated, StyleSheet, Text, View } from 'react-native'; import { act, render } from '@testing-library/react-native'; -import { red200, white } from '../../styles/themes/colors'; +import { red200, white } from '../../theme/colors'; import Snackbar from '../Snackbar'; const styles = StyleSheet.create({ diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index c555c8ad2b..2022439d76 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -13,7 +13,7 @@ import { grey800, pink500, grey700, -} from '../../styles/themes/colors'; +} from '../../theme/colors'; import Switch from '../Switch/Switch'; import { getSwitchColor } from '../Switch/utils'; diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index e052755eaf..a9f06c7a8e 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -5,8 +5,8 @@ import { I18nManager, Platform, StyleSheet, Text, View } from 'react-native'; import { fireEvent, render } from '@testing-library/react-native'; import { DefaultTheme, getTheme, ThemeProvider } from '../../core/theming'; -import { red500 } from '../../styles/themes/colors'; -import { tokens } from '../../styles/themes/tokens'; +import { red500 } from '../../theme/colors'; +import { tokens } from '../../theme/tokens'; import { getFlatInputColors, getOutlinedInputColors, diff --git a/src/components/__tests__/Typography/Text.test.tsx b/src/components/__tests__/Typography/Text.test.tsx index 0e0bb64303..67f99ae94b 100644 --- a/src/components/__tests__/Typography/Text.test.tsx +++ b/src/components/__tests__/Typography/Text.test.tsx @@ -3,9 +3,9 @@ import * as React from 'react'; import { render } from '@testing-library/react-native'; import PaperProvider from '../../../core/PaperProvider'; -import configureFonts from '../../../styles/fonts'; -import { LightTheme } from '../../../styles/themes'; -import { tokens } from '../../../styles/themes/tokens'; +import configureFonts from '../../../theme/fonts'; +import { LightTheme } from '../../../theme/schemes'; +import { tokens } from '../../../theme/tokens'; import Text, { customText } from '../../Typography/Text'; const content = 'Something rendered as a child content'; diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 6ae08641c1..1d04ec00ff 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -8,7 +8,7 @@ import { import { render, act } from '@testing-library/react-native'; -import { LightTheme, DarkTheme } from '../../styles/themes'; +import { LightTheme, DarkTheme } from '../../theme/schemes'; import type { ThemeProp } from '../../types'; import PaperProvider from '../PaperProvider'; import { useTheme } from '../theming'; diff --git a/src/core/__tests__/theming.test.tsx b/src/core/__tests__/theming.test.tsx index 0771e32650..cfca524f6f 100644 --- a/src/core/__tests__/theming.test.tsx +++ b/src/core/__tests__/theming.test.tsx @@ -1,4 +1,4 @@ -import { DarkTheme, LightTheme } from '../../styles/themes'; +import { DarkTheme, LightTheme } from '../../theme/schemes'; import { adaptNavigationTheme } from '../theming'; const NavigationLightTheme = { diff --git a/src/core/theming.tsx b/src/core/theming.tsx index 6da3aaf13f..ff478d0117 100644 --- a/src/core/theming.tsx +++ b/src/core/theming.tsx @@ -1,138 +1 @@ -import type { ComponentType } from 'react'; - -import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; - -import { DarkTheme, LightTheme } from '../styles/themes'; -import type { InternalTheme, Theme, NavigationTheme } from '../types'; - -export const DefaultTheme = LightTheme; - -export const { - ThemeProvider, - withTheme, - useTheme: useAppTheme, -} = createTheming(LightTheme); - -export function useTheme(overrides?: $DeepPartial) { - return useAppTheme(overrides); -} - -export const useInternalTheme = ( - themeOverrides: $DeepPartial | undefined -) => useAppTheme(themeOverrides); - -export const withInternalTheme = ( - WrappedComponent: ComponentType & C -) => withTheme(WrappedComponent); - -export const defaultThemes = { - light: LightTheme, - dark: DarkTheme, -}; - -export const getTheme = ( - isDark: Scheme = false as Scheme -): (typeof defaultThemes)[Scheme extends true ? 'dark' : 'light'] => { - const scheme = isDark ? 'dark' : 'light'; - - return defaultThemes[scheme]; -}; - -// eslint-disable-next-line no-redeclare -export function adaptNavigationTheme(themes: { - reactNavigationLight: T; - materialLight?: Theme; -}): { - LightTheme: NavigationTheme; -}; -// eslint-disable-next-line no-redeclare -export function adaptNavigationTheme(themes: { - reactNavigationDark: T; - materialDark?: Theme; -}): { - DarkTheme: NavigationTheme; -}; -// eslint-disable-next-line no-redeclare -export function adaptNavigationTheme< - TLight extends NavigationTheme, - TDark extends NavigationTheme ->(themes: { - reactNavigationLight: TLight; - reactNavigationDark: TDark; - materialLight?: Theme; - materialDark?: Theme; -}): { LightTheme: TLight; DarkTheme: TDark }; -// eslint-disable-next-line no-redeclare -export function adaptNavigationTheme(themes: any) { - const { - reactNavigationLight, - reactNavigationDark, - materialLight, - materialDark, - } = themes; - - const MD3Themes = { - light: materialLight || LightTheme, - dark: materialDark || DarkTheme, - }; - - const result: { LightTheme?: any; DarkTheme?: any } = {}; - - if (reactNavigationLight) { - result.LightTheme = getAdaptedTheme(reactNavigationLight, MD3Themes.light); - } - - if (reactNavigationDark) { - result.DarkTheme = getAdaptedTheme(reactNavigationDark, MD3Themes.dark); - } - - return result; -} - -const getAdaptedTheme = ( - theme: T, - materialTheme: Theme -): T => { - const base = { - ...theme, - colors: { - ...theme.colors, - primary: materialTheme.colors.primary, - background: materialTheme.colors.background, - card: materialTheme.colors.surfaceContainer, - text: materialTheme.colors.onSurface, - border: materialTheme.colors.outline, - notification: materialTheme.colors.error, - }, - }; - - if ('fonts' in theme) { - return { - ...base, - fonts: { - regular: { - fontFamily: materialTheme.fonts.bodyMedium.fontFamily, - fontWeight: materialTheme.fonts.bodyMedium.fontWeight, - letterSpacing: materialTheme.fonts.bodyMedium.letterSpacing, - }, - medium: { - fontFamily: materialTheme.fonts.titleMedium.fontFamily, - fontWeight: materialTheme.fonts.titleMedium.fontWeight, - letterSpacing: materialTheme.fonts.titleMedium.letterSpacing, - }, - bold: { - fontFamily: materialTheme.fonts.headlineSmall.fontFamily, - fontWeight: materialTheme.fonts.headlineSmall.fontWeight, - letterSpacing: materialTheme.fonts.headlineSmall.letterSpacing, - }, - heavy: { - fontFamily: materialTheme.fonts.headlineLarge.fontFamily, - fontWeight: materialTheme.fonts.headlineLarge.fontWeight, - letterSpacing: materialTheme.fonts.headlineLarge.letterSpacing, - }, - }, - }; - } - - return base; -}; +export * from '../theme/provider'; diff --git a/src/deprecated.ts b/src/deprecated.ts index 06979bdaa1..e294f6a44d 100644 --- a/src/deprecated.ts +++ b/src/deprecated.ts @@ -8,9 +8,9 @@ * Do not add anything here that isn't a deprecated alias. */ -import { DarkTheme } from './styles/themes/DarkTheme'; -import { LightTheme } from './styles/themes/LightTheme'; -import { Palette } from './styles/themes/tokens'; +import { DarkTheme } from './theme/schemes/DarkTheme'; +import { LightTheme } from './theme/schemes/LightTheme'; +import { Palette } from './theme/tokens'; import { TypescaleKey } from './types'; import type { Theme, Elevation } from './types'; diff --git a/src/index.tsx b/src/index.tsx index 536e923d4a..298658cf50 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,4 @@ -export { Palette } from './styles/themes/tokens'; +export { Palette } from './theme/tokens'; export { useTheme, @@ -8,12 +8,12 @@ export { adaptNavigationTheme, } from './core/theming'; -export * from './styles/themes'; +export * from './theme/schemes'; export { default as Provider } from './core/PaperProvider'; export { default as PaperProvider } from './core/PaperProvider'; -export { default as shadow } from './styles/shadow'; -export { default as configureFonts } from './styles/fonts'; +export { default as shadow } from './theme/shadow'; +export { default as configureFonts } from './theme/fonts'; import * as Avatar from './components/Avatar/Avatar'; import * as Drawer from './components/Drawer/Drawer'; diff --git a/src/styles/__tests__/fonts.test.js b/src/theme/__tests__/fonts.test.js similarity index 98% rename from src/styles/__tests__/fonts.test.js rename to src/theme/__tests__/fonts.test.js index e632bdbcf0..9aafe31616 100644 --- a/src/styles/__tests__/fonts.test.js +++ b/src/theme/__tests__/fonts.test.js @@ -130,7 +130,7 @@ const loadFonts = () => { const fonts = require('../fonts'); configureFonts = fonts.default; - typescale = require('../themes/tokens').typescale; + typescale = require('../tokens').typescale; }); return { configureFonts, typescale }; diff --git a/src/styles/themes/colors.tsx b/src/theme/colors.tsx similarity index 100% rename from src/styles/themes/colors.tsx rename to src/theme/colors.tsx diff --git a/src/styles/fonts.tsx b/src/theme/fonts.tsx similarity index 97% rename from src/styles/fonts.tsx rename to src/theme/fonts.tsx index a2267fb20d..3a67d00d67 100644 --- a/src/styles/fonts.tsx +++ b/src/theme/fonts.tsx @@ -1,5 +1,5 @@ import type { TypescaleStyle, Typescale, TypescaleKey } from '../types'; -import { typescale } from './themes/tokens'; +import { typescale } from './tokens'; type FontsConfig = | { diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx new file mode 100644 index 0000000000..22056d9354 --- /dev/null +++ b/src/theme/provider.tsx @@ -0,0 +1,138 @@ +import type { ComponentType } from 'react'; + +import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; + +import { DarkTheme, LightTheme } from './schemes'; +import type { InternalTheme, Theme, NavigationTheme } from '../types'; + +export const DefaultTheme = LightTheme; + +export const { + ThemeProvider, + withTheme, + useTheme: useAppTheme, +} = createTheming(LightTheme); + +export function useTheme(overrides?: $DeepPartial) { + return useAppTheme(overrides); +} + +export const useInternalTheme = ( + themeOverrides: $DeepPartial | undefined +) => useAppTheme(themeOverrides); + +export const withInternalTheme = ( + WrappedComponent: ComponentType & C +) => withTheme(WrappedComponent); + +export const defaultThemes = { + light: LightTheme, + dark: DarkTheme, +}; + +export const getTheme = ( + isDark: Scheme = false as Scheme +): (typeof defaultThemes)[Scheme extends true ? 'dark' : 'light'] => { + const scheme = isDark ? 'dark' : 'light'; + + return defaultThemes[scheme]; +}; + +// eslint-disable-next-line no-redeclare +export function adaptNavigationTheme(themes: { + reactNavigationLight: T; + materialLight?: Theme; +}): { + LightTheme: NavigationTheme; +}; +// eslint-disable-next-line no-redeclare +export function adaptNavigationTheme(themes: { + reactNavigationDark: T; + materialDark?: Theme; +}): { + DarkTheme: NavigationTheme; +}; +// eslint-disable-next-line no-redeclare +export function adaptNavigationTheme< + TLight extends NavigationTheme, + TDark extends NavigationTheme +>(themes: { + reactNavigationLight: TLight; + reactNavigationDark: TDark; + materialLight?: Theme; + materialDark?: Theme; +}): { LightTheme: TLight; DarkTheme: TDark }; +// eslint-disable-next-line no-redeclare +export function adaptNavigationTheme(themes: any) { + const { + reactNavigationLight, + reactNavigationDark, + materialLight, + materialDark, + } = themes; + + const MD3Themes = { + light: materialLight || LightTheme, + dark: materialDark || DarkTheme, + }; + + const result: { LightTheme?: any; DarkTheme?: any } = {}; + + if (reactNavigationLight) { + result.LightTheme = getAdaptedTheme(reactNavigationLight, MD3Themes.light); + } + + if (reactNavigationDark) { + result.DarkTheme = getAdaptedTheme(reactNavigationDark, MD3Themes.dark); + } + + return result; +} + +const getAdaptedTheme = ( + theme: T, + materialTheme: Theme +): T => { + const base = { + ...theme, + colors: { + ...theme.colors, + primary: materialTheme.colors.primary, + background: materialTheme.colors.background, + card: materialTheme.colors.surfaceContainer, + text: materialTheme.colors.onSurface, + border: materialTheme.colors.outline, + notification: materialTheme.colors.error, + }, + }; + + if ('fonts' in theme) { + return { + ...base, + fonts: { + regular: { + fontFamily: materialTheme.fonts.bodyMedium.fontFamily, + fontWeight: materialTheme.fonts.bodyMedium.fontWeight, + letterSpacing: materialTheme.fonts.bodyMedium.letterSpacing, + }, + medium: { + fontFamily: materialTheme.fonts.titleMedium.fontFamily, + fontWeight: materialTheme.fonts.titleMedium.fontWeight, + letterSpacing: materialTheme.fonts.titleMedium.letterSpacing, + }, + bold: { + fontFamily: materialTheme.fonts.headlineSmall.fontFamily, + fontWeight: materialTheme.fonts.headlineSmall.fontWeight, + letterSpacing: materialTheme.fonts.headlineSmall.letterSpacing, + }, + heavy: { + fontFamily: materialTheme.fonts.headlineLarge.fontFamily, + fontWeight: materialTheme.fonts.headlineLarge.fontWeight, + letterSpacing: materialTheme.fonts.headlineLarge.letterSpacing, + }, + }, + }; + } + + return base; +}; diff --git a/src/styles/themes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx similarity index 98% rename from src/styles/themes/DarkTheme.tsx rename to src/theme/schemes/DarkTheme.tsx index 693855680e..830bfec4b1 100644 --- a/src/styles/themes/DarkTheme.tsx +++ b/src/theme/schemes/DarkTheme.tsx @@ -1,8 +1,8 @@ import color from 'color'; import { LightTheme } from './LightTheme'; -import { tokens } from './tokens'; import type { Theme } from '../../types'; +import { tokens } from '../tokens'; const { palette, stateOpacity } = tokens.md.ref; diff --git a/src/styles/themes/DynamicTheme.android.tsx b/src/theme/schemes/DynamicTheme.android.tsx similarity index 100% rename from src/styles/themes/DynamicTheme.android.tsx rename to src/theme/schemes/DynamicTheme.android.tsx diff --git a/src/styles/themes/DynamicTheme.tsx b/src/theme/schemes/DynamicTheme.tsx similarity index 100% rename from src/styles/themes/DynamicTheme.tsx rename to src/theme/schemes/DynamicTheme.tsx diff --git a/src/styles/themes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx similarity index 98% rename from src/styles/themes/LightTheme.tsx rename to src/theme/schemes/LightTheme.tsx index 5566b9fadf..8d7f3854d9 100644 --- a/src/styles/themes/LightTheme.tsx +++ b/src/theme/schemes/LightTheme.tsx @@ -1,8 +1,8 @@ import color from 'color'; -import { tokens } from './tokens'; import type { Theme } from '../../types'; import configureFonts from '../fonts'; +import { tokens } from '../tokens'; const { palette, stateOpacity } = tokens.md.ref; diff --git a/src/styles/themes/index.ts b/src/theme/schemes/index.ts similarity index 100% rename from src/styles/themes/index.ts rename to src/theme/schemes/index.ts diff --git a/src/styles/shadow.tsx b/src/theme/shadow.tsx similarity index 96% rename from src/styles/shadow.tsx rename to src/theme/shadow.tsx index 32e056fa1f..58070cede8 100644 --- a/src/styles/shadow.tsx +++ b/src/theme/shadow.tsx @@ -1,6 +1,6 @@ import { Animated } from 'react-native'; -import { Palette } from './themes/tokens'; +import { Palette } from './tokens'; const SHADOW_OPACITY = 0.3; const SHADOW_COLOR = Palette.primary0; diff --git a/src/styles/themes/tokens.tsx b/src/theme/tokens/index.ts similarity index 100% rename from src/styles/themes/tokens.tsx rename to src/theme/tokens/index.ts diff --git a/src/theme/types.ts b/src/theme/types.ts new file mode 100644 index 0000000000..b424142b86 --- /dev/null +++ b/src/theme/types.ts @@ -0,0 +1,179 @@ +import type * as React from 'react'; + +import type { $DeepPartial } from '@callstack/react-theme-provider'; + +export type Font = { + fontFamily: string; + fontWeight?: + | 'normal' + | 'bold' + | '100' + | '200' + | '300' + | '400' + | '500' + | '600' + | '700' + | '800' + | '900'; + fontStyle?: 'normal' | 'italic' | undefined; +}; + +export type Fonts = { + regular: Font; + medium: Font; + light: Font; + thin: Font; +}; + +type Mode = 'adaptive' | 'exact'; + +export type ThemeColors = { + primary: string; + primaryContainer: string; + secondary: string; + secondaryContainer: string; + tertiary: string; + tertiaryContainer: string; + surface: string; + surfaceDim: string; + surfaceBright: string; + surfaceContainerLowest: string; + surfaceContainerLow: string; + surfaceContainer: string; + surfaceContainerHigh: string; + surfaceContainerHighest: string; + surfaceVariant: string; + background: string; + error: string; + errorContainer: string; + onPrimary: string; + onPrimaryContainer: string; + onSecondary: string; + onSecondaryContainer: string; + onTertiary: string; + onTertiaryContainer: string; + onSurface: string; + onSurfaceVariant: string; + onError: string; + onErrorContainer: string; + onBackground: string; + outline: string; + outlineVariant: string; + inverseSurface: string; + inverseOnSurface: string; + inversePrimary: string; + primaryFixed: string; + primaryFixedDim: string; + onPrimaryFixed: string; + onPrimaryFixedVariant: string; + secondaryFixed: string; + secondaryFixedDim: string; + onSecondaryFixed: string; + onSecondaryFixedVariant: string; + tertiaryFixed: string; + tertiaryFixedDim: string; + onTertiaryFixed: string; + onTertiaryFixedVariant: string; + shadow: string; + scrim: string; + /** Pre-computed state layer color at press opacity (0.10). + * Used for ripple effects. Avoids runtime alpha manipulation + * which is incompatible with PlatformColor on Android. + * TODO: revisit after https://github.com/facebook/react-native/pull/56395 + * @see https://m3.material.io/foundations/interaction/states/state-layers */ + stateLayerPressed: string; + elevation: ElevationColors; +}; + +export type ThemeProp = $DeepPartial; + +export type ThemeBase = { + dark: boolean; + mode?: Mode; + roundness: number; + animation: { + scale: number; + defaultAnimationDuration?: number; + }; +}; + +export type Theme = ThemeBase & { + colors: ThemeColors; + fonts: Typescale; +}; + +export type InternalTheme = Theme; + +export enum TypescaleKey { + displayLarge = 'displayLarge', + displayMedium = 'displayMedium', + displaySmall = 'displaySmall', + + headlineLarge = 'headlineLarge', + headlineMedium = 'headlineMedium', + headlineSmall = 'headlineSmall', + + titleLarge = 'titleLarge', + titleMedium = 'titleMedium', + titleSmall = 'titleSmall', + + labelLarge = 'labelLarge', + labelMedium = 'labelMedium', + labelSmall = 'labelSmall', + + bodyLarge = 'bodyLarge', + bodyMedium = 'bodyMedium', + bodySmall = 'bodySmall', +} + +export type TypescaleStyle = { + fontFamily: string; + letterSpacing: number; + fontWeight: Font['fontWeight']; + lineHeight: number; + fontSize: number; + fontStyle?: Font['fontStyle']; +}; + +export type Typescale = + | { + [key in TypescaleKey]: TypescaleStyle; + } & { + ['default']: Omit; + }; + +export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; + +export enum ElevationLevels { + 'level0', + 'level1', + 'level2', + 'level3', + 'level4', + 'level5', +} + +export type ElevationColors = { + [key in keyof typeof ElevationLevels]: string; +}; + +export type $Omit = Pick>; +export type $RemoveChildren> = $Omit< + React.ComponentPropsWithoutRef, + 'children' +>; + +export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; + +export type NavigationTheme = { + dark: boolean; + colors: { + primary: string; + background: string; + card: string; + text: string; + border: string; + notification: string; + }; +}; diff --git a/src/types.tsx b/src/types.tsx index b424142b86..261b55f0b3 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -1,179 +1 @@ -import type * as React from 'react'; - -import type { $DeepPartial } from '@callstack/react-theme-provider'; - -export type Font = { - fontFamily: string; - fontWeight?: - | 'normal' - | 'bold' - | '100' - | '200' - | '300' - | '400' - | '500' - | '600' - | '700' - | '800' - | '900'; - fontStyle?: 'normal' | 'italic' | undefined; -}; - -export type Fonts = { - regular: Font; - medium: Font; - light: Font; - thin: Font; -}; - -type Mode = 'adaptive' | 'exact'; - -export type ThemeColors = { - primary: string; - primaryContainer: string; - secondary: string; - secondaryContainer: string; - tertiary: string; - tertiaryContainer: string; - surface: string; - surfaceDim: string; - surfaceBright: string; - surfaceContainerLowest: string; - surfaceContainerLow: string; - surfaceContainer: string; - surfaceContainerHigh: string; - surfaceContainerHighest: string; - surfaceVariant: string; - background: string; - error: string; - errorContainer: string; - onPrimary: string; - onPrimaryContainer: string; - onSecondary: string; - onSecondaryContainer: string; - onTertiary: string; - onTertiaryContainer: string; - onSurface: string; - onSurfaceVariant: string; - onError: string; - onErrorContainer: string; - onBackground: string; - outline: string; - outlineVariant: string; - inverseSurface: string; - inverseOnSurface: string; - inversePrimary: string; - primaryFixed: string; - primaryFixedDim: string; - onPrimaryFixed: string; - onPrimaryFixedVariant: string; - secondaryFixed: string; - secondaryFixedDim: string; - onSecondaryFixed: string; - onSecondaryFixedVariant: string; - tertiaryFixed: string; - tertiaryFixedDim: string; - onTertiaryFixed: string; - onTertiaryFixedVariant: string; - shadow: string; - scrim: string; - /** Pre-computed state layer color at press opacity (0.10). - * Used for ripple effects. Avoids runtime alpha manipulation - * which is incompatible with PlatformColor on Android. - * TODO: revisit after https://github.com/facebook/react-native/pull/56395 - * @see https://m3.material.io/foundations/interaction/states/state-layers */ - stateLayerPressed: string; - elevation: ElevationColors; -}; - -export type ThemeProp = $DeepPartial; - -export type ThemeBase = { - dark: boolean; - mode?: Mode; - roundness: number; - animation: { - scale: number; - defaultAnimationDuration?: number; - }; -}; - -export type Theme = ThemeBase & { - colors: ThemeColors; - fonts: Typescale; -}; - -export type InternalTheme = Theme; - -export enum TypescaleKey { - displayLarge = 'displayLarge', - displayMedium = 'displayMedium', - displaySmall = 'displaySmall', - - headlineLarge = 'headlineLarge', - headlineMedium = 'headlineMedium', - headlineSmall = 'headlineSmall', - - titleLarge = 'titleLarge', - titleMedium = 'titleMedium', - titleSmall = 'titleSmall', - - labelLarge = 'labelLarge', - labelMedium = 'labelMedium', - labelSmall = 'labelSmall', - - bodyLarge = 'bodyLarge', - bodyMedium = 'bodyMedium', - bodySmall = 'bodySmall', -} - -export type TypescaleStyle = { - fontFamily: string; - letterSpacing: number; - fontWeight: Font['fontWeight']; - lineHeight: number; - fontSize: number; - fontStyle?: Font['fontStyle']; -}; - -export type Typescale = - | { - [key in TypescaleKey]: TypescaleStyle; - } & { - ['default']: Omit; - }; - -export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; - -export enum ElevationLevels { - 'level0', - 'level1', - 'level2', - 'level3', - 'level4', - 'level5', -} - -export type ElevationColors = { - [key in keyof typeof ElevationLevels]: string; -}; - -export type $Omit = Pick>; -export type $RemoveChildren> = $Omit< - React.ComponentPropsWithoutRef, - 'children' ->; - -export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; - -export type NavigationTheme = { - dark: boolean; - colors: { - primary: string; - background: string; - card: string; - text: string; - border: string; - notification: string; - }; -}; +export * from './theme/types'; From 1f1102dae775c5a60610e76b390b526bc4ecee04 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Mon, 11 May 2026 09:54:01 +0300 Subject: [PATCH 08/94] refactor: add color role table and buildScheme (#4916) Adds src/theme/tokens/sys/color/roles.ts with a roleToTone table keyed by (mode, contrast) and a buildScheme() function. LightTheme and DarkTheme replace their hand-duplicated color literals with a single buildScheme() call each. Output shape is identical; tone values match the existing hand-written spec. --- src/theme/fonts.tsx | 2 +- src/theme/schemes/DarkTheme.tsx | 74 +-------- src/theme/schemes/DynamicTheme.android.tsx | 2 +- src/theme/schemes/LightTheme.tsx | 78 +-------- src/theme/schemes/base.ts | 9 ++ src/theme/tokens/index.ts | 2 +- src/theme/tokens/sys/color/roles.ts | 180 +++++++++++++++++++++ 7 files changed, 202 insertions(+), 145 deletions(-) create mode 100644 src/theme/schemes/base.ts create mode 100644 src/theme/tokens/sys/color/roles.ts diff --git a/src/theme/fonts.tsx b/src/theme/fonts.tsx index 3a67d00d67..7808205474 100644 --- a/src/theme/fonts.tsx +++ b/src/theme/fonts.tsx @@ -1,5 +1,5 @@ -import type { TypescaleStyle, Typescale, TypescaleKey } from '../types'; import { typescale } from './tokens'; +import type { TypescaleStyle, Typescale, TypescaleKey } from './types'; type FontsConfig = | { diff --git a/src/theme/schemes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx index 830bfec4b1..85ca245e57 100644 --- a/src/theme/schemes/DarkTheme.tsx +++ b/src/theme/schemes/DarkTheme.tsx @@ -1,75 +1,11 @@ -import color from 'color'; - -import { LightTheme } from './LightTheme'; -import type { Theme } from '../../types'; +import { baseTheme } from './base'; import { tokens } from '../tokens'; - -const { palette, stateOpacity } = tokens.md.ref; +import { buildScheme } from '../tokens/sys/color/roles'; +import type { Theme } from '../types'; export const DarkTheme: Theme = { - ...LightTheme, + ...baseTheme, dark: true, mode: 'adaptive', - colors: { - primary: palette.primary80, - primaryContainer: palette.primary30, - secondary: palette.secondary80, - secondaryContainer: palette.secondary30, - tertiary: palette.tertiary80, - tertiaryContainer: palette.tertiary30, - surface: palette.neutral6, - surfaceDim: palette.neutral6, - surfaceBright: palette.neutral24, - surfaceContainerLowest: palette.neutral4, - surfaceContainerLow: palette.neutral10, - surfaceContainer: palette.neutral12, - surfaceContainerHigh: palette.neutral17, - surfaceContainerHighest: palette.neutral22, - surfaceVariant: palette.neutralVariant30, - background: palette.neutral6, - error: palette.error80, - errorContainer: palette.error30, - onPrimary: palette.primary20, - onPrimaryContainer: palette.primary90, - onSecondary: palette.secondary20, - onSecondaryContainer: palette.secondary90, - onTertiary: palette.tertiary20, - onTertiaryContainer: palette.tertiary90, - onSurface: palette.neutral90, - onSurfaceVariant: palette.neutralVariant80, - onError: palette.error20, - onErrorContainer: palette.error80, - onBackground: palette.neutral90, - outline: palette.neutralVariant60, - outlineVariant: palette.neutralVariant30, - inverseSurface: palette.neutral90, - inverseOnSurface: palette.neutral20, - inversePrimary: palette.primary40, - primaryFixed: palette.primary90, - primaryFixedDim: palette.primary80, - onPrimaryFixed: palette.primary10, - onPrimaryFixedVariant: palette.primary30, - secondaryFixed: palette.secondary90, - secondaryFixedDim: palette.secondary80, - onSecondaryFixed: palette.secondary10, - onSecondaryFixedVariant: palette.secondary30, - tertiaryFixed: palette.tertiary90, - tertiaryFixedDim: palette.tertiary80, - onTertiaryFixed: palette.tertiary10, - onTertiaryFixedVariant: palette.tertiary30, - shadow: palette.neutral0, - scrim: palette.neutral0, - stateLayerPressed: color(palette.neutral90) - .alpha(stateOpacity.pressed) - .rgb() - .string(), - elevation: { - level0: 'transparent', - level1: palette.neutral10, - level2: palette.neutral12, - level3: palette.neutral17, - level4: palette.neutral17, - level5: palette.neutral22, - }, - }, + colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'dark' }), }; diff --git a/src/theme/schemes/DynamicTheme.android.tsx b/src/theme/schemes/DynamicTheme.android.tsx index f2791670e8..84667f5b79 100644 --- a/src/theme/schemes/DynamicTheme.android.tsx +++ b/src/theme/schemes/DynamicTheme.android.tsx @@ -2,7 +2,7 @@ import { Platform, PlatformColor } from 'react-native'; import { DarkTheme } from './DarkTheme'; import { LightTheme } from './LightTheme'; -import type { Theme } from '../../types'; +import type { Theme } from '../types'; const isApi34 = (Platform.Version as number) >= 34; const isApi31 = (Platform.Version as number) >= 31; diff --git a/src/theme/schemes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx index 8d7f3854d9..d60f1a7709 100644 --- a/src/theme/schemes/LightTheme.tsx +++ b/src/theme/schemes/LightTheme.tsx @@ -1,78 +1,10 @@ -import color from 'color'; - -import type { Theme } from '../../types'; -import configureFonts from '../fonts'; +import { baseTheme } from './base'; import { tokens } from '../tokens'; - -const { palette, stateOpacity } = tokens.md.ref; +import { buildScheme } from '../tokens/sys/color/roles'; +import type { Theme } from '../types'; export const LightTheme: Theme = { + ...baseTheme, dark: false, - roundness: 4, - colors: { - primary: palette.primary40, - primaryContainer: palette.primary90, - secondary: palette.secondary40, - secondaryContainer: palette.secondary90, - tertiary: palette.tertiary40, - tertiaryContainer: palette.tertiary90, - surface: palette.neutral98, - surfaceDim: palette.neutral87, - surfaceBright: palette.neutral98, - surfaceContainerLowest: palette.neutral100, - surfaceContainerLow: palette.neutral96, - surfaceContainer: palette.neutral94, - surfaceContainerHigh: palette.neutral92, - surfaceContainerHighest: palette.neutral90, - surfaceVariant: palette.neutralVariant90, - background: palette.neutral98, - error: palette.error40, - errorContainer: palette.error90, - onPrimary: palette.primary100, - onPrimaryContainer: palette.primary10, - onSecondary: palette.secondary100, - onSecondaryContainer: palette.secondary10, - onTertiary: palette.tertiary100, - onTertiaryContainer: palette.tertiary10, - onSurface: palette.neutral10, - onSurfaceVariant: palette.neutralVariant30, - onError: palette.error100, - onErrorContainer: palette.error10, - onBackground: palette.neutral10, - outline: palette.neutralVariant50, - outlineVariant: palette.neutralVariant80, - inverseSurface: palette.neutral20, - inverseOnSurface: palette.neutral95, - inversePrimary: palette.primary80, - primaryFixed: palette.primary90, - primaryFixedDim: palette.primary80, - onPrimaryFixed: palette.primary10, - onPrimaryFixedVariant: palette.primary30, - secondaryFixed: palette.secondary90, - secondaryFixedDim: palette.secondary80, - onSecondaryFixed: palette.secondary10, - onSecondaryFixedVariant: palette.secondary30, - tertiaryFixed: palette.tertiary90, - tertiaryFixedDim: palette.tertiary80, - onTertiaryFixed: palette.tertiary10, - onTertiaryFixedVariant: palette.tertiary30, - shadow: palette.neutral0, - scrim: palette.neutral0, - stateLayerPressed: color(palette.neutral10) - .alpha(stateOpacity.pressed) - .rgb() - .string(), - elevation: { - level0: 'transparent', - level1: palette.neutral96, - level2: palette.neutral94, - level3: palette.neutral92, - level4: palette.neutral92, - level5: palette.neutral90, - }, - }, - fonts: configureFonts(), - animation: { - scale: 1.0, - }, + colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'light' }), }; diff --git a/src/theme/schemes/base.ts b/src/theme/schemes/base.ts new file mode 100644 index 0000000000..68cf999cee --- /dev/null +++ b/src/theme/schemes/base.ts @@ -0,0 +1,9 @@ +import configureFonts from '../fonts'; + +export const baseTheme = { + roundness: 4, + fonts: configureFonts(), + animation: { + scale: 1.0, + }, +}; diff --git a/src/theme/tokens/index.ts b/src/theme/tokens/index.ts index 987bca6ace..ecc35ffa24 100644 --- a/src/theme/tokens/index.ts +++ b/src/theme/tokens/index.ts @@ -1,6 +1,6 @@ import { Platform } from 'react-native'; -import type { Font } from '../../types'; +import type { Font } from '../types'; const ref = { palette: { diff --git a/src/theme/tokens/sys/color/roles.ts b/src/theme/tokens/sys/color/roles.ts new file mode 100644 index 0000000000..a3b3a49695 --- /dev/null +++ b/src/theme/tokens/sys/color/roles.ts @@ -0,0 +1,180 @@ +import color from 'color'; + +import type { ElevationColors, ThemeColors } from '../../../types'; +import { tokens } from '../../index'; + +type Palette = typeof tokens.md.ref.palette; +type PaletteKey = keyof Palette; +type Ref = typeof tokens.md.ref; + +/** Roles that map 1:1 to a palette key. Excludes the computed fields. */ +type MappedRoles = Omit; + +type Contrast = 'standard'; // extend with 'medium' | 'high' when those ship + +const roleToTone: Record< + 'light' | 'dark', + Record> +> = { + light: { + standard: { + primary: 'primary40', + onPrimary: 'primary100', + primaryContainer: 'primary90', + onPrimaryContainer: 'primary10', + secondary: 'secondary40', + onSecondary: 'secondary100', + secondaryContainer: 'secondary90', + onSecondaryContainer: 'secondary10', + tertiary: 'tertiary40', + onTertiary: 'tertiary100', + tertiaryContainer: 'tertiary90', + onTertiaryContainer: 'tertiary10', + error: 'error40', + onError: 'error100', + errorContainer: 'error90', + onErrorContainer: 'error10', + surface: 'neutral98', + surfaceDim: 'neutral87', + surfaceBright: 'neutral98', + surfaceContainerLowest: 'neutral100', + surfaceContainerLow: 'neutral96', + surfaceContainer: 'neutral94', + surfaceContainerHigh: 'neutral92', + surfaceContainerHighest: 'neutral90', + surfaceVariant: 'neutralVariant90', + background: 'neutral98', + onSurface: 'neutral10', + onSurfaceVariant: 'neutralVariant30', + onBackground: 'neutral10', + outline: 'neutralVariant50', + outlineVariant: 'neutralVariant80', + inverseSurface: 'neutral20', + inverseOnSurface: 'neutral95', + inversePrimary: 'primary80', + primaryFixed: 'primary90', + primaryFixedDim: 'primary80', + onPrimaryFixed: 'primary10', + onPrimaryFixedVariant: 'primary30', + secondaryFixed: 'secondary90', + secondaryFixedDim: 'secondary80', + onSecondaryFixed: 'secondary10', + onSecondaryFixedVariant: 'secondary30', + tertiaryFixed: 'tertiary90', + tertiaryFixedDim: 'tertiary80', + onTertiaryFixed: 'tertiary10', + onTertiaryFixedVariant: 'tertiary30', + shadow: 'neutral0', + scrim: 'neutral0', + }, + }, + dark: { + standard: { + primary: 'primary80', + onPrimary: 'primary20', + primaryContainer: 'primary30', + onPrimaryContainer: 'primary90', + secondary: 'secondary80', + onSecondary: 'secondary20', + secondaryContainer: 'secondary30', + onSecondaryContainer: 'secondary90', + tertiary: 'tertiary80', + onTertiary: 'tertiary20', + tertiaryContainer: 'tertiary30', + onTertiaryContainer: 'tertiary90', + error: 'error80', + onError: 'error20', + errorContainer: 'error30', + onErrorContainer: 'error80', + surface: 'neutral6', + surfaceDim: 'neutral6', + surfaceBright: 'neutral24', + surfaceContainerLowest: 'neutral4', + surfaceContainerLow: 'neutral10', + surfaceContainer: 'neutral12', + surfaceContainerHigh: 'neutral17', + surfaceContainerHighest: 'neutral22', + surfaceVariant: 'neutralVariant30', + background: 'neutral6', + onSurface: 'neutral90', + onSurfaceVariant: 'neutralVariant80', + onBackground: 'neutral90', + outline: 'neutralVariant60', + outlineVariant: 'neutralVariant30', + inverseSurface: 'neutral90', + inverseOnSurface: 'neutral20', + inversePrimary: 'primary40', + primaryFixed: 'primary90', + primaryFixedDim: 'primary80', + onPrimaryFixed: 'primary10', + onPrimaryFixedVariant: 'primary30', + secondaryFixed: 'secondary90', + secondaryFixedDim: 'secondary80', + onSecondaryFixed: 'secondary10', + onSecondaryFixedVariant: 'secondary30', + tertiaryFixed: 'tertiary90', + tertiaryFixedDim: 'tertiary80', + onTertiaryFixed: 'tertiary10', + onTertiaryFixedVariant: 'tertiary30', + shadow: 'neutral0', + scrim: 'neutral0', + }, + }, +}; + +const elevationToTone: Record< + 'light' | 'dark', + Record, PaletteKey>> +> = { + light: { + standard: { + level1: 'neutral96', + level2: 'neutral94', + level3: 'neutral92', + level4: 'neutral92', + level5: 'neutral90', + }, + }, + dark: { + standard: { + level1: 'neutral10', + level2: 'neutral12', + level3: 'neutral17', + level4: 'neutral17', + level5: 'neutral22', + }, + }, +}; + +export function buildScheme( + palette: Palette, + ref: Ref, + opts: { mode: 'light' | 'dark'; contrast?: Contrast } +): ThemeColors { + const contrast = opts.contrast ?? 'standard'; + const tones = roleToTone[opts.mode][contrast]; + const elevTones = elevationToTone[opts.mode][contrast]; + + const mapped = Object.fromEntries( + (Object.keys(tones) as Array).map((role) => [ + role, + palette[tones[role]], + ]) + ) as MappedRoles; + + return { + ...mapped, + stateLayerPressed: color(palette[tones.onSurface]) + .alpha(ref.stateOpacity.pressed) + .rgb() + .string(), + elevation: { + level0: 'transparent', + level1: palette[elevTones.level1], + level2: palette[elevTones.level2], + level3: palette[elevTones.level3], + level4: palette[elevTones.level4], + level5: palette[elevTones.level5], + }, + }; +} From f998dffe5f8f30a87059a0af03c5d3f822e4329b Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 12 May 2026 14:10:10 +0300 Subject: [PATCH 09/94] feat: add theme.state opacity tokens and split theme types (#4917) * feat: add theme.state opacity tokens and split theme types * feat: add disabled and enabled opacity to theme.state * fix: code review findings --- src/components/Card/CardTitle.tsx | 4 +- src/components/Checkbox/CheckboxItem.tsx | 2 +- src/components/Menu/Menu.tsx | 12 +- .../RadioButton/RadioButtonItem.tsx | 2 +- src/components/Typography/types.tsx | 2 +- src/deprecated.ts | 5 +- src/theme/fonts.tsx | 8 +- src/theme/tokens/index.ts | 4 +- src/theme/types.ts | 179 ------------------ src/theme/types/color.ts | 59 ++++++ src/theme/types/elevation.ts | 3 + src/theme/types/index.ts | 6 + src/theme/types/navigation.ts | 11 ++ src/theme/types/theme.ts | 25 +++ src/theme/types/typography.ts | 53 ++++++ src/theme/types/utils.ts | 9 + 16 files changed, 184 insertions(+), 200 deletions(-) delete mode 100644 src/theme/types.ts create mode 100644 src/theme/types/color.ts create mode 100644 src/theme/types/elevation.ts create mode 100644 src/theme/types/index.ts create mode 100644 src/theme/types/navigation.ts create mode 100644 src/theme/types/theme.ts create mode 100644 src/theme/types/typography.ts create mode 100644 src/theme/types/utils.ts diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index b37f903499..a8cda33fdc 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -40,7 +40,7 @@ export type Props = React.ComponentPropsWithRef & { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - titleVariant?: keyof typeof TypescaleKey; + titleVariant?: TypescaleKey; /** * Text for the subtitle. Note that this will only accept a string or ``-based node. */ @@ -69,7 +69,7 @@ export type Props = React.ComponentPropsWithRef & { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - subtitleVariant?: keyof typeof TypescaleKey; + subtitleVariant?: TypescaleKey; /** * Callback which returns a React element to display on the left side. */ diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 008f03640d..9037137e7a 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -86,7 +86,7 @@ export type Props = { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - labelVariant?: keyof typeof TypescaleKey; + labelVariant?: TypescaleKey; /** * @optional */ diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index a3f798d03a..4ba3d59a57 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -24,7 +24,6 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import MenuItem from './MenuItem'; import { useInternalTheme } from '../../core/theming'; import type { Elevation, Theme, ThemeProp } from '../../types'; -import { ElevationLevels } from '../../types'; import { addEventListener } from '../../utils/addEventListener'; import { BackHandler } from '../../utils/BackHandler/BackHandler'; import Portal from '../Portal/Portal'; @@ -105,9 +104,14 @@ const EASING = Easing.bezier(0.4, 0, 0.2, 1); const WINDOW_LAYOUT = Dimensions.get('window'); const DEFAULT_ELEVATION: Elevation = 2; -export const ELEVATION_LEVELS_MAP = Object.values( - ElevationLevels -) as ElevationLevels[]; +export const ELEVATION_LEVELS_MAP = [ + 'level0', + 'level1', + 'level2', + 'level3', + 'level4', + 'level5', +] as const; const DEFAULT_MODE = 'elevated'; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index fe9b6843a8..936a2cdf3d 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -88,7 +88,7 @@ export type Props = { * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ - labelVariant?: keyof typeof TypescaleKey; + labelVariant?: TypescaleKey; /** * Specifies the largest possible scale a label font can reach. */ diff --git a/src/components/Typography/types.tsx b/src/components/Typography/types.tsx index 3f1754521a..ef7de9891a 100644 --- a/src/components/Typography/types.tsx +++ b/src/components/Typography/types.tsx @@ -2,4 +2,4 @@ import type { TypescaleKey } from '../../types'; export type VariantProp = | (T extends string ? (string extends T ? never : T) : never) - | keyof typeof TypescaleKey; + | TypescaleKey; diff --git a/src/deprecated.ts b/src/deprecated.ts index e294f6a44d..ded0e3f10d 100644 --- a/src/deprecated.ts +++ b/src/deprecated.ts @@ -11,8 +11,7 @@ import { DarkTheme } from './theme/schemes/DarkTheme'; import { LightTheme } from './theme/schemes/LightTheme'; import { Palette } from './theme/tokens'; -import { TypescaleKey } from './types'; -import type { Theme, Elevation } from './types'; +import type { Theme, Elevation, TypescaleKey } from './types'; /** * @deprecated Use `LightTheme` instead. Will be removed in a future version. @@ -32,8 +31,6 @@ export const MD3Colors = Palette; /** * @deprecated Use `TypescaleKey` instead. Will be removed in a future version. */ -export const MD3TypescaleKey = TypescaleKey; -// eslint-disable-next-line no-redeclare export type MD3TypescaleKey = TypescaleKey; /** diff --git a/src/theme/fonts.tsx b/src/theme/fonts.tsx index 7808205474..e380655486 100644 --- a/src/theme/fonts.tsx +++ b/src/theme/fonts.tsx @@ -2,12 +2,8 @@ import { typescale } from './tokens'; import type { TypescaleStyle, Typescale, TypescaleKey } from './types'; type FontsConfig = - | { - [key in TypescaleKey]: Partial; - } - | { - [key: string]: TypescaleStyle; - } + | Record> + | Record | Partial; function configureFontsConfig( diff --git a/src/theme/tokens/index.ts b/src/theme/tokens/index.ts index ecc35ffa24..e8f041163e 100644 --- a/src/theme/tokens/index.ts +++ b/src/theme/tokens/index.ts @@ -122,8 +122,8 @@ const ref = { stateOpacity: { dragged: 0.16, pressed: 0.1, - focus: 0.1, - hover: 0.08, + focused: 0.1, + hovered: 0.08, disabled: 0.38, enabled: 1.0, }, diff --git a/src/theme/types.ts b/src/theme/types.ts deleted file mode 100644 index b424142b86..0000000000 --- a/src/theme/types.ts +++ /dev/null @@ -1,179 +0,0 @@ -import type * as React from 'react'; - -import type { $DeepPartial } from '@callstack/react-theme-provider'; - -export type Font = { - fontFamily: string; - fontWeight?: - | 'normal' - | 'bold' - | '100' - | '200' - | '300' - | '400' - | '500' - | '600' - | '700' - | '800' - | '900'; - fontStyle?: 'normal' | 'italic' | undefined; -}; - -export type Fonts = { - regular: Font; - medium: Font; - light: Font; - thin: Font; -}; - -type Mode = 'adaptive' | 'exact'; - -export type ThemeColors = { - primary: string; - primaryContainer: string; - secondary: string; - secondaryContainer: string; - tertiary: string; - tertiaryContainer: string; - surface: string; - surfaceDim: string; - surfaceBright: string; - surfaceContainerLowest: string; - surfaceContainerLow: string; - surfaceContainer: string; - surfaceContainerHigh: string; - surfaceContainerHighest: string; - surfaceVariant: string; - background: string; - error: string; - errorContainer: string; - onPrimary: string; - onPrimaryContainer: string; - onSecondary: string; - onSecondaryContainer: string; - onTertiary: string; - onTertiaryContainer: string; - onSurface: string; - onSurfaceVariant: string; - onError: string; - onErrorContainer: string; - onBackground: string; - outline: string; - outlineVariant: string; - inverseSurface: string; - inverseOnSurface: string; - inversePrimary: string; - primaryFixed: string; - primaryFixedDim: string; - onPrimaryFixed: string; - onPrimaryFixedVariant: string; - secondaryFixed: string; - secondaryFixedDim: string; - onSecondaryFixed: string; - onSecondaryFixedVariant: string; - tertiaryFixed: string; - tertiaryFixedDim: string; - onTertiaryFixed: string; - onTertiaryFixedVariant: string; - shadow: string; - scrim: string; - /** Pre-computed state layer color at press opacity (0.10). - * Used for ripple effects. Avoids runtime alpha manipulation - * which is incompatible with PlatformColor on Android. - * TODO: revisit after https://github.com/facebook/react-native/pull/56395 - * @see https://m3.material.io/foundations/interaction/states/state-layers */ - stateLayerPressed: string; - elevation: ElevationColors; -}; - -export type ThemeProp = $DeepPartial; - -export type ThemeBase = { - dark: boolean; - mode?: Mode; - roundness: number; - animation: { - scale: number; - defaultAnimationDuration?: number; - }; -}; - -export type Theme = ThemeBase & { - colors: ThemeColors; - fonts: Typescale; -}; - -export type InternalTheme = Theme; - -export enum TypescaleKey { - displayLarge = 'displayLarge', - displayMedium = 'displayMedium', - displaySmall = 'displaySmall', - - headlineLarge = 'headlineLarge', - headlineMedium = 'headlineMedium', - headlineSmall = 'headlineSmall', - - titleLarge = 'titleLarge', - titleMedium = 'titleMedium', - titleSmall = 'titleSmall', - - labelLarge = 'labelLarge', - labelMedium = 'labelMedium', - labelSmall = 'labelSmall', - - bodyLarge = 'bodyLarge', - bodyMedium = 'bodyMedium', - bodySmall = 'bodySmall', -} - -export type TypescaleStyle = { - fontFamily: string; - letterSpacing: number; - fontWeight: Font['fontWeight']; - lineHeight: number; - fontSize: number; - fontStyle?: Font['fontStyle']; -}; - -export type Typescale = - | { - [key in TypescaleKey]: TypescaleStyle; - } & { - ['default']: Omit; - }; - -export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; - -export enum ElevationLevels { - 'level0', - 'level1', - 'level2', - 'level3', - 'level4', - 'level5', -} - -export type ElevationColors = { - [key in keyof typeof ElevationLevels]: string; -}; - -export type $Omit = Pick>; -export type $RemoveChildren> = $Omit< - React.ComponentPropsWithoutRef, - 'children' ->; - -export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; - -export type NavigationTheme = { - dark: boolean; - colors: { - primary: string; - background: string; - card: string; - text: string; - border: string; - notification: string; - }; -}; diff --git a/src/theme/types/color.ts b/src/theme/types/color.ts new file mode 100644 index 0000000000..cc7ed49b86 --- /dev/null +++ b/src/theme/types/color.ts @@ -0,0 +1,59 @@ +import type { ElevationColors } from './elevation'; + +export type ThemeColors = { + primary: string; + primaryContainer: string; + secondary: string; + secondaryContainer: string; + tertiary: string; + tertiaryContainer: string; + surface: string; + surfaceDim: string; + surfaceBright: string; + surfaceContainerLowest: string; + surfaceContainerLow: string; + surfaceContainer: string; + surfaceContainerHigh: string; + surfaceContainerHighest: string; + surfaceVariant: string; + background: string; + error: string; + errorContainer: string; + onPrimary: string; + onPrimaryContainer: string; + onSecondary: string; + onSecondaryContainer: string; + onTertiary: string; + onTertiaryContainer: string; + onSurface: string; + onSurfaceVariant: string; + onError: string; + onErrorContainer: string; + onBackground: string; + outline: string; + outlineVariant: string; + inverseSurface: string; + inverseOnSurface: string; + inversePrimary: string; + primaryFixed: string; + primaryFixedDim: string; + onPrimaryFixed: string; + onPrimaryFixedVariant: string; + secondaryFixed: string; + secondaryFixedDim: string; + onSecondaryFixed: string; + onSecondaryFixedVariant: string; + tertiaryFixed: string; + tertiaryFixedDim: string; + onTertiaryFixed: string; + onTertiaryFixedVariant: string; + shadow: string; + scrim: string; + /** Pre-computed state layer color at press opacity (0.10). + * Used for ripple effects. Avoids runtime alpha manipulation + * which is incompatible with PlatformColor on Android. + * TODO: revisit after https://github.com/facebook/react-native/pull/56395 + * @see https://m3.material.io/foundations/interaction/states/state-layers */ + stateLayerPressed: string; + elevation: ElevationColors; +}; diff --git a/src/theme/types/elevation.ts b/src/theme/types/elevation.ts new file mode 100644 index 0000000000..839112f58c --- /dev/null +++ b/src/theme/types/elevation.ts @@ -0,0 +1,3 @@ +export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; + +export type ElevationColors = Record<`level${Elevation}`, string>; diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts new file mode 100644 index 0000000000..2207ed0526 --- /dev/null +++ b/src/theme/types/index.ts @@ -0,0 +1,6 @@ +export * from './color'; +export * from './elevation'; +export * from './navigation'; +export * from './theme'; +export * from './typography'; +export * from './utils'; diff --git a/src/theme/types/navigation.ts b/src/theme/types/navigation.ts new file mode 100644 index 0000000000..51d4a6f763 --- /dev/null +++ b/src/theme/types/navigation.ts @@ -0,0 +1,11 @@ +export type NavigationTheme = { + dark: boolean; + colors: { + primary: string; + background: string; + card: string; + text: string; + border: string; + notification: string; + }; +}; diff --git a/src/theme/types/theme.ts b/src/theme/types/theme.ts new file mode 100644 index 0000000000..431b4ecfcf --- /dev/null +++ b/src/theme/types/theme.ts @@ -0,0 +1,25 @@ +import type { $DeepPartial } from '@callstack/react-theme-provider'; + +import type { ThemeColors } from './color'; +import type { Typescale } from './typography'; + +type Mode = 'adaptive' | 'exact'; + +export type ThemeBase = { + dark: boolean; + mode?: Mode; + roundness: number; + animation: { + scale: number; + defaultAnimationDuration?: number; + }; +}; + +export type Theme = ThemeBase & { + colors: ThemeColors; + fonts: Typescale; +}; + +export type InternalTheme = Theme; + +export type ThemeProp = $DeepPartial; diff --git a/src/theme/types/typography.ts b/src/theme/types/typography.ts new file mode 100644 index 0000000000..57fcf3f67e --- /dev/null +++ b/src/theme/types/typography.ts @@ -0,0 +1,53 @@ +export type Font = { + fontFamily: string; + fontWeight?: + | 'normal' + | 'bold' + | '100' + | '200' + | '300' + | '400' + | '500' + | '600' + | '700' + | '800' + | '900'; + fontStyle?: 'normal' | 'italic' | undefined; +}; + +export type Fonts = { + regular: Font; + medium: Font; + light: Font; + thin: Font; +}; + +export type TypescaleKey = + | 'displayLarge' + | 'displayMedium' + | 'displaySmall' + | 'headlineLarge' + | 'headlineMedium' + | 'headlineSmall' + | 'titleLarge' + | 'titleMedium' + | 'titleSmall' + | 'labelLarge' + | 'labelMedium' + | 'labelSmall' + | 'bodyLarge' + | 'bodyMedium' + | 'bodySmall'; + +export type TypescaleStyle = { + fontFamily: string; + letterSpacing: number; + fontWeight: Font['fontWeight']; + lineHeight: number; + fontSize: number; + fontStyle?: Font['fontStyle']; +}; + +export type Typescale = Record & { + default: Omit; +}; diff --git a/src/theme/types/utils.ts b/src/theme/types/utils.ts new file mode 100644 index 0000000000..0ea6b262b9 --- /dev/null +++ b/src/theme/types/utils.ts @@ -0,0 +1,9 @@ +import type * as React from 'react'; + +export type $Omit = Pick>; +export type $RemoveChildren> = $Omit< + React.ComponentPropsWithoutRef, + 'children' +>; + +export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; From b88ec191cc6b43dfbae10aa3657a5cfb0b6486f0 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 12 May 2026 14:27:39 +0300 Subject: [PATCH 10/94] feat: add theme.shapes and cornersToStyle (#4919) --- example/src/Examples/TextInputExample.tsx | 4 +- src/components/Button/Button.tsx | 4 +- src/components/Card/Card.tsx | 4 +- src/components/Card/utils.tsx | 6 +-- src/components/Chip/Chip.tsx | 3 +- src/components/Dialog/Dialog.tsx | 3 +- src/components/Drawer/DrawerItem.tsx | 3 +- src/components/FAB/utils.ts | 14 +++---- src/components/Menu/Menu.tsx | 2 +- src/components/Searchbar.tsx | 9 +++-- .../SegmentedButtons/SegmentedButtonItem.tsx | 3 +- src/components/Snackbar.tsx | 4 +- src/components/TextInput/TextInputFlat.tsx | 7 ++-- .../TextInput/TextInputOutlined.tsx | 3 +- src/components/ToggleButton/ToggleButton.tsx | 2 +- src/components/Tooltip/Tooltip.tsx | 2 +- src/components/__tests__/Card/Card.test.tsx | 2 +- src/components/__tests__/Chip.test.tsx | 2 +- src/components/__tests__/FAB.test.tsx | 7 +++- .../__snapshots__/ListSection.test.tsx.snap | 39 +++++++++++++++++-- src/theme/schemes/DarkTheme.tsx | 2 + src/theme/schemes/LightTheme.tsx | 2 + src/theme/schemes/base.ts | 1 - src/theme/tokens/sys/shape.ts | 17 ++++++++ src/theme/types/index.ts | 1 + src/theme/types/shape.ts | 14 +++++++ src/theme/types/theme.ts | 3 +- 27 files changed, 117 insertions(+), 46 deletions(-) create mode 100644 src/theme/tokens/sys/shape.ts create mode 100644 src/theme/types/shape.ts diff --git a/example/src/Examples/TextInputExample.tsx b/example/src/Examples/TextInputExample.tsx index caecdc49b8..fbe11afd0a 100644 --- a/example/src/Examples/TextInputExample.tsx +++ b/example/src/Examples/TextInputExample.tsx @@ -672,7 +672,7 @@ const TextInputExample = () => { @@ -681,7 +681,7 @@ const TextInputExample = () => { diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 1cb2ca6bed..59cebe3a93 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -210,7 +210,7 @@ const Button = ( }, [mode] ); - const { roundness, animation } = theme; + const { animation } = theme; const uppercase = uppercaseProp ?? false; const isWeb = Platform.OS === 'web'; @@ -271,7 +271,7 @@ const Button = ( (style) => style.startsWith('border') && style.endsWith('Radius') ); - const borderRadius = 5 * roundness; + const borderRadius = theme.shapes.corner.largeIncreased; const iconSize = 18; const { diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 422101b4e8..c1522e5bf5 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -181,7 +181,7 @@ const Card = ( const { current: elevationDarkAdaptive } = React.useRef( new Animated.Value(cardElevation) ); - const { animation, dark, mode, roundness } = theme; + const { animation, dark, mode } = theme; const prevDarkRef = React.useRef(dark); React.useEffect(() => { @@ -267,7 +267,7 @@ const Card = ( ); const borderRadiusCombinedStyles = { - borderRadius: 3 * roundness, + borderRadius: theme.shapes.corner.medium, ...borderRadiusStyles, }; diff --git a/src/components/Card/utils.tsx b/src/components/Card/utils.tsx index b3d776662d..785a23c820 100644 --- a/src/components/Card/utils.tsx +++ b/src/components/Card/utils.tsx @@ -26,17 +26,15 @@ export const getCardCoverStyle = ({ index?: number; total?: number; }) => { - const { roundness } = theme; - if (Object.keys(borderRadiusStyles).length > 0) { return { - borderRadius: 3 * roundness, + borderRadius: theme.shapes.corner.medium, ...borderRadiusStyles, }; } return { - borderRadius: 3 * roundness, + borderRadius: theme.shapes.corner.medium, }; }; diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index fe3b30ab49..76d12d5ae1 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -205,7 +205,6 @@ const Chip = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { roundness } = theme; const isWeb = Platform.OS === 'web'; const { current: elevation } = React.useRef( @@ -244,7 +243,7 @@ const Chip = ({ }); const opacity = 0.38; - const defaultBorderRadius = roundness * 2; + const defaultBorderRadius = theme.shapes.corner.small; const iconSize = 18; const { diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index fd6597c34e..717445aed7 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -105,8 +105,7 @@ const Dialog = ({ }: Props) => { const { right, left } = useSafeAreaInsets(); const theme = useInternalTheme(themeOverrides); - const { roundness } = theme as Theme; - const borderRadius = 7 * roundness; + const borderRadius = (theme as Theme).shapes.corner.extraLarge; const backgroundColor = theme.colors.surfaceContainerHigh; diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 6902b4b8cb..9a68471474 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -100,7 +100,6 @@ const DrawerItem = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { roundness } = theme; const backgroundColor = active ? theme.colors.secondaryContainer : undefined; const contentColor = active @@ -108,7 +107,7 @@ const DrawerItem = ({ : theme.colors.onSurfaceVariant; const labelMargin = icon ? 12 : 0; - const borderRadius = 7 * roundness; + const borderRadius = theme.shapes.corner.extraLarge; const font = theme.fonts.labelLarge; return ( diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index ad49fa13be..93485aecb3 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -284,10 +284,10 @@ const v3LargeSize = { width: 96, }; -const getCustomFabSize = (customSize: number, roundness: number) => ({ +const getCustomFabSize = (customSize: number) => ({ height: customSize, width: customSize, - borderRadius: roundness === 0 ? 0 : customSize / roundness, + borderRadius: customSize / 4, }); export const getFabStyle = ({ @@ -299,17 +299,15 @@ export const getFabStyle = ({ size: 'small' | 'medium' | 'large'; theme: InternalTheme; }) => { - const { roundness } = theme; - - if (customSize) return getCustomFabSize(customSize, roundness); + if (customSize) return getCustomFabSize(customSize); switch (size) { case 'small': - return { ...v3SmallSize, borderRadius: 3 * roundness }; + return { ...v3SmallSize, borderRadius: theme.shapes.corner.medium }; case 'medium': - return { ...v3MediumSize, borderRadius: 4 * roundness }; + return { ...v3MediumSize, borderRadius: theme.shapes.corner.large }; case 'large': - return { ...v3LargeSize, borderRadius: 7 * roundness }; + return { ...v3LargeSize, borderRadius: theme.shapes.corner.extraLarge }; } }; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 4ba3d59a57..ebbd94e057 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -622,7 +622,7 @@ const Menu = ({ }), }, ], - borderRadius: theme.roundness, + borderRadius: theme.shapes.corner.extraSmall, ...(scrollableMenuHeight ? { height: scrollableMenuHeight } : {}), }; diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 76c9e03218..69fbfb572d 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -20,6 +20,7 @@ import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; import { useInternalTheme } from '../core/theming'; +import { cornerNone } from '../theme/tokens/sys/shape'; import type { Theme, ThemeProp } from '../types'; import { forwardRef } from '../utils/forwardRef'; @@ -213,7 +214,7 @@ const Searchbar = forwardRef( onClearIconPress?.(e); }; - const { roundness, dark } = theme; + const { dark } = theme; const placeholderTextColor = theme.colors.onSurface; const textColor = theme.colors.onSurfaceVariant; @@ -237,10 +238,12 @@ const Searchbar = forwardRef( return ( { const isAndroid = Platform.OS === 'android'; - const { colors, roundness } = theme; + const { colors } = theme; + const roundness = theme.shapes.corner.extraSmall; const font = theme.fonts.bodyLarge; const hasActiveOutline = parentState.focused || error; @@ -156,8 +157,8 @@ const TextInputFlat = ({ const containerStyle = { backgroundColor, - borderTopLeftRadius: theme.roundness, - borderTopRightRadius: theme.roundness, + borderTopLeftRadius: roundness, + borderTopRightRadius: roundness, }; const labelScale = MINIMIZED_LABEL_FONT_SIZE / fontSize; diff --git a/src/components/TextInput/TextInputOutlined.tsx b/src/components/TextInput/TextInputOutlined.tsx index 63efb7f14b..aa563cf55f 100644 --- a/src/components/TextInput/TextInputOutlined.tsx +++ b/src/components/TextInput/TextInputOutlined.tsx @@ -81,7 +81,8 @@ const TextInputOutlined = ({ }: ChildTextInputProps) => { const adornmentConfig = getAdornmentConfig({ left, right }); - const { colors, roundness } = theme; + const { colors } = theme; + const roundness = theme.shapes.corner.extraSmall; const font = theme.fonts.bodyLarge; const hasActiveOutline = parentState.focused || error; diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index ba4bd138a3..71bc5bad9c 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -108,7 +108,7 @@ const ToggleButton = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); - const borderRadius = theme.roundness; + const borderRadius = theme.shapes.corner.extraSmall; return ( diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index fdfef8571f..a3d199e879 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -209,7 +209,7 @@ const Tooltip = ({ measurement as Measurement, children as React.ReactElement ), - borderRadius: theme.roundness, + borderRadius: theme.shapes.corner.extraSmall, ...(measurement.measured ? styles.visible : styles.hidden), }, ]} diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index fb61ca9602..ac4eabfa11 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -220,7 +220,7 @@ describe('getCardCoverStyle - border radius', () => { theme: getTheme(), borderRadiusStyles: {}, }) - ).toMatchObject({ borderRadius: 3 * getTheme().roundness }); + ).toMatchObject({ borderRadius: getTheme().shapes.corner.medium }); }); }); diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 82841001ff..4ed5cad4ea 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -83,7 +83,7 @@ it('renders active chip if only onLongPress handler is passed', () => { it('renders chip with zero border radius', () => { const { getByTestId } = render( - + Active chip ); diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index 9fc9c12e98..91b54e22be 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -131,7 +131,12 @@ it('renders FAB with custom border radius', () => { it('renders FAB with zero border radius', () => { const { getByTestId } = render( - {}} icon="plus" testID="fab" /> + {}} + icon="plus" + testID="fab" + /> ); expect(getByTestId('fab-container')).toHaveStyle({ borderRadius: 0 }); diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index 80b6cc6c20..fe96dc80e6 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -187,7 +187,18 @@ exports[`renders list section with custom title style 1`] = ` "lineHeight": 20, }, }, - "roundness": 4, + "shapes": { + "corner": { + "extraExtraLarge": 48, + "extraLarge": 28, + "extraLargeIncreased": 32, + "extraSmall": 4, + "large": 16, + "largeIncreased": 20, + "medium": 12, + "small": 8, + }, + }, } } > @@ -729,7 +740,18 @@ exports[`renders list section with subheader 1`] = ` "lineHeight": 20, }, }, - "roundness": 4, + "shapes": { + "corner": { + "extraExtraLarge": 48, + "extraLarge": 28, + "extraLargeIncreased": 32, + "extraSmall": 4, + "large": 16, + "largeIncreased": 20, + "medium": 12, + "small": 8, + }, + }, } } > @@ -1269,7 +1291,18 @@ exports[`renders list section without subheader 1`] = ` "lineHeight": 20, }, }, - "roundness": 4, + "shapes": { + "corner": { + "extraExtraLarge": 48, + "extraLarge": 28, + "extraLargeIncreased": 32, + "extraSmall": 4, + "large": 16, + "largeIncreased": 20, + "medium": 12, + "small": 8, + }, + }, } } > diff --git a/src/theme/schemes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx index 85ca245e57..ab49b62400 100644 --- a/src/theme/schemes/DarkTheme.tsx +++ b/src/theme/schemes/DarkTheme.tsx @@ -1,6 +1,7 @@ import { baseTheme } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; +import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const DarkTheme: Theme = { @@ -8,4 +9,5 @@ export const DarkTheme: Theme = { dark: true, mode: 'adaptive', colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'dark' }), + shapes: defaultShapes, }; diff --git a/src/theme/schemes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx index d60f1a7709..b5d97a9cae 100644 --- a/src/theme/schemes/LightTheme.tsx +++ b/src/theme/schemes/LightTheme.tsx @@ -1,10 +1,12 @@ import { baseTheme } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; +import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const LightTheme: Theme = { ...baseTheme, dark: false, colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'light' }), + shapes: defaultShapes, }; diff --git a/src/theme/schemes/base.ts b/src/theme/schemes/base.ts index 68cf999cee..4601d7dc83 100644 --- a/src/theme/schemes/base.ts +++ b/src/theme/schemes/base.ts @@ -1,7 +1,6 @@ import configureFonts from '../fonts'; export const baseTheme = { - roundness: 4, fonts: configureFonts(), animation: { scale: 1.0, diff --git a/src/theme/tokens/sys/shape.ts b/src/theme/tokens/sys/shape.ts new file mode 100644 index 0000000000..6610004481 --- /dev/null +++ b/src/theme/tokens/sys/shape.ts @@ -0,0 +1,17 @@ +import type { ThemeShapes } from '../../types'; + +export const cornerNone = 0; +export const cornerFull = 9999; + +export const defaultShapes: ThemeShapes = { + corner: { + extraSmall: 4, + small: 8, + medium: 12, + large: 16, + largeIncreased: 20, + extraLarge: 28, + extraLargeIncreased: 32, + extraExtraLarge: 48, + }, +}; diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts index 2207ed0526..e62ef4ad45 100644 --- a/src/theme/types/index.ts +++ b/src/theme/types/index.ts @@ -1,6 +1,7 @@ export * from './color'; export * from './elevation'; export * from './navigation'; +export * from './shape'; export * from './theme'; export * from './typography'; export * from './utils'; diff --git a/src/theme/types/shape.ts b/src/theme/types/shape.ts new file mode 100644 index 0000000000..78d16d2285 --- /dev/null +++ b/src/theme/types/shape.ts @@ -0,0 +1,14 @@ +export type ThemeShapeCorners = { + extraSmall: number; + small: number; + medium: number; + large: number; + largeIncreased: number; + extraLarge: number; + extraLargeIncreased: number; + extraExtraLarge: number; +}; + +export type ThemeShapes = { + corner: ThemeShapeCorners; +}; diff --git a/src/theme/types/theme.ts b/src/theme/types/theme.ts index 431b4ecfcf..dac2946cc9 100644 --- a/src/theme/types/theme.ts +++ b/src/theme/types/theme.ts @@ -1,6 +1,7 @@ import type { $DeepPartial } from '@callstack/react-theme-provider'; import type { ThemeColors } from './color'; +import type { ThemeShapes } from './shape'; import type { Typescale } from './typography'; type Mode = 'adaptive' | 'exact'; @@ -8,7 +9,6 @@ type Mode = 'adaptive' | 'exact'; export type ThemeBase = { dark: boolean; mode?: Mode; - roundness: number; animation: { scale: number; defaultAnimationDuration?: number; @@ -18,6 +18,7 @@ export type ThemeBase = { export type Theme = ThemeBase & { colors: ThemeColors; fonts: Typescale; + shapes: ThemeShapes; }; export type InternalTheme = Theme; From c9f40e24e3b04a71dcefcdbdaffe8e135e84be37 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 12 May 2026 15:14:58 +0300 Subject: [PATCH 11/94] feat: add emphasized typescale and fix letter-spacing spec bugs (#4920) Adds 15 *Emphasized entries per M3 Design Kit: Display/Headline/TitleLarge/Body use Medium (500), TitleMedium/TitleSmall/Label use Bold (700). Adds weightBold to ref.typeface. Fixes displayLarge letterSpacing (0 -> -0.25) and bodyLarge letterSpacing (0.15 -> 0.5). --- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 2 +- .../__snapshots__/CheckboxItem.test.tsx.snap | 8 +- .../RadioButtonItem.test.tsx.snap | 8 +- .../__snapshots__/Text.test.tsx.snap | 4 +- .../__snapshots__/ListSection.test.tsx.snap | 327 +++++++++++++++++- .../__snapshots__/Menu.test.tsx.snap | 16 +- .../__snapshots__/MenuItem.test.tsx.snap | 20 +- .../__snapshots__/Searchbar.test.tsx.snap | 6 +- .../__snapshots__/TextInput.test.tsx.snap | 62 ++-- src/theme/__tests__/fonts.test.js | 111 +++++- src/theme/schemes/DarkTheme.tsx | 4 +- src/theme/schemes/LightTheme.tsx | 4 +- src/theme/schemes/base.ts | 11 +- src/theme/tokens/index.ts | 105 ++++++ src/theme/tokens/sys/typography.ts | 4 + 15 files changed, 613 insertions(+), 79 deletions(-) create mode 100644 src/theme/tokens/sys/typography.ts diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 82c623d863..c7b786a718 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -243,7 +243,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 0, }, { diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index 5c416c9720..2615640570 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -155,7 +155,7 @@ exports[`can render leading checkbox control 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -253,7 +253,7 @@ exports[`can render the Android checkbox on different platforms 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -471,7 +471,7 @@ exports[`can render the iOS checkbox on different platforms 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -652,7 +652,7 @@ exports[`renders unchecked 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ diff --git a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap index f44e395c55..39405cd07a 100644 --- a/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap +++ b/src/components/__tests__/RadioButton/__snapshots__/RadioButtonItem.test.tsx.snap @@ -153,7 +153,7 @@ exports[`can render leading radio button control 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -248,7 +248,7 @@ exports[`can render the Android radio button on different platforms 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -400,7 +400,7 @@ exports[`can render the iOS radio button on different platforms 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -578,7 +578,7 @@ exports[`renders unchecked 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ diff --git a/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap b/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap index 5b87989124..21130dee07 100644 --- a/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap +++ b/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap @@ -17,7 +17,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "fontFamily": "System", "fontSize": 57, "fontWeight": "400", - "letterSpacing": 0, + "letterSpacing": -0.25, "lineHeight": 64, }, undefined, @@ -242,7 +242,7 @@ exports[`renders every variant of Text with children as content 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index fe96dc80e6..f49e1240c8 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -80,7 +80,14 @@ exports[`renders list section with custom title style 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, + "lineHeight": 24, + }, + "bodyLargeEmphasized": { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.5, "lineHeight": 24, }, "bodyMedium": { @@ -90,6 +97,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0.25, "lineHeight": 20, }, + "bodyMediumEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.25, + "lineHeight": 20, + }, "bodySmall": { "fontFamily": "System", "fontSize": 12, @@ -97,6 +111,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0.4, "lineHeight": 16, }, + "bodySmallEmphasized": { + "fontFamily": "System", + "fontSize": 12, + "fontWeight": "500", + "letterSpacing": 0.4, + "lineHeight": 16, + }, "default": { "fontFamily": "System", "fontWeight": "400", @@ -106,7 +127,14 @@ exports[`renders list section with custom title style 1`] = ` "fontFamily": "System", "fontSize": 57, "fontWeight": "400", - "letterSpacing": 0, + "letterSpacing": -0.25, + "lineHeight": 64, + }, + "displayLargeEmphasized": { + "fontFamily": "System", + "fontSize": 57, + "fontWeight": "500", + "letterSpacing": -0.25, "lineHeight": 64, }, "displayMedium": { @@ -116,6 +144,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0, "lineHeight": 52, }, + "displayMediumEmphasized": { + "fontFamily": "System", + "fontSize": 45, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 52, + }, "displaySmall": { "fontFamily": "System", "fontSize": 36, @@ -123,6 +158,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0, "lineHeight": 44, }, + "displaySmallEmphasized": { + "fontFamily": "System", + "fontSize": 36, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 44, + }, "headlineLarge": { "fontFamily": "System", "fontSize": 32, @@ -130,6 +172,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0, "lineHeight": 40, }, + "headlineLargeEmphasized": { + "fontFamily": "System", + "fontSize": 32, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 40, + }, "headlineMedium": { "fontFamily": "System", "fontSize": 28, @@ -137,6 +186,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0, "lineHeight": 36, }, + "headlineMediumEmphasized": { + "fontFamily": "System", + "fontSize": 28, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 36, + }, "headlineSmall": { "fontFamily": "System", "fontSize": 24, @@ -144,6 +200,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0, "lineHeight": 32, }, + "headlineSmallEmphasized": { + "fontFamily": "System", + "fontSize": 24, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 32, + }, "labelLarge": { "fontFamily": "System", "fontSize": 14, @@ -151,6 +214,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0.1, "lineHeight": 20, }, + "labelLargeEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "700", + "letterSpacing": 0.1, + "lineHeight": 20, + }, "labelMedium": { "fontFamily": "System", "fontSize": 12, @@ -158,6 +228,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0.5, "lineHeight": 16, }, + "labelMediumEmphasized": { + "fontFamily": "System", + "fontSize": 12, + "fontWeight": "700", + "letterSpacing": 0.5, + "lineHeight": 16, + }, "labelSmall": { "fontFamily": "System", "fontSize": 11, @@ -165,6 +242,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0.5, "lineHeight": 16, }, + "labelSmallEmphasized": { + "fontFamily": "System", + "fontSize": 11, + "fontWeight": "700", + "letterSpacing": 0.5, + "lineHeight": 16, + }, "titleLarge": { "fontFamily": "System", "fontSize": 22, @@ -172,6 +256,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0, "lineHeight": 28, }, + "titleLargeEmphasized": { + "fontFamily": "System", + "fontSize": 22, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 28, + }, "titleMedium": { "fontFamily": "System", "fontSize": 16, @@ -179,6 +270,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0.15, "lineHeight": 24, }, + "titleMediumEmphasized": { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "700", + "letterSpacing": 0.15, + "lineHeight": 24, + }, "titleSmall": { "fontFamily": "System", "fontSize": 14, @@ -186,6 +284,13 @@ exports[`renders list section with custom title style 1`] = ` "letterSpacing": 0.1, "lineHeight": 20, }, + "titleSmallEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "700", + "letterSpacing": 0.1, + "lineHeight": 20, + }, }, "shapes": { "corner": { @@ -633,7 +738,14 @@ exports[`renders list section with subheader 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, + "lineHeight": 24, + }, + "bodyLargeEmphasized": { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.5, "lineHeight": 24, }, "bodyMedium": { @@ -643,6 +755,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0.25, "lineHeight": 20, }, + "bodyMediumEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.25, + "lineHeight": 20, + }, "bodySmall": { "fontFamily": "System", "fontSize": 12, @@ -650,6 +769,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0.4, "lineHeight": 16, }, + "bodySmallEmphasized": { + "fontFamily": "System", + "fontSize": 12, + "fontWeight": "500", + "letterSpacing": 0.4, + "lineHeight": 16, + }, "default": { "fontFamily": "System", "fontWeight": "400", @@ -659,7 +785,14 @@ exports[`renders list section with subheader 1`] = ` "fontFamily": "System", "fontSize": 57, "fontWeight": "400", - "letterSpacing": 0, + "letterSpacing": -0.25, + "lineHeight": 64, + }, + "displayLargeEmphasized": { + "fontFamily": "System", + "fontSize": 57, + "fontWeight": "500", + "letterSpacing": -0.25, "lineHeight": 64, }, "displayMedium": { @@ -669,6 +802,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0, "lineHeight": 52, }, + "displayMediumEmphasized": { + "fontFamily": "System", + "fontSize": 45, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 52, + }, "displaySmall": { "fontFamily": "System", "fontSize": 36, @@ -676,6 +816,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0, "lineHeight": 44, }, + "displaySmallEmphasized": { + "fontFamily": "System", + "fontSize": 36, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 44, + }, "headlineLarge": { "fontFamily": "System", "fontSize": 32, @@ -683,6 +830,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0, "lineHeight": 40, }, + "headlineLargeEmphasized": { + "fontFamily": "System", + "fontSize": 32, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 40, + }, "headlineMedium": { "fontFamily": "System", "fontSize": 28, @@ -690,6 +844,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0, "lineHeight": 36, }, + "headlineMediumEmphasized": { + "fontFamily": "System", + "fontSize": 28, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 36, + }, "headlineSmall": { "fontFamily": "System", "fontSize": 24, @@ -697,6 +858,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0, "lineHeight": 32, }, + "headlineSmallEmphasized": { + "fontFamily": "System", + "fontSize": 24, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 32, + }, "labelLarge": { "fontFamily": "System", "fontSize": 14, @@ -704,6 +872,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0.1, "lineHeight": 20, }, + "labelLargeEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "700", + "letterSpacing": 0.1, + "lineHeight": 20, + }, "labelMedium": { "fontFamily": "System", "fontSize": 12, @@ -711,6 +886,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0.5, "lineHeight": 16, }, + "labelMediumEmphasized": { + "fontFamily": "System", + "fontSize": 12, + "fontWeight": "700", + "letterSpacing": 0.5, + "lineHeight": 16, + }, "labelSmall": { "fontFamily": "System", "fontSize": 11, @@ -718,6 +900,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0.5, "lineHeight": 16, }, + "labelSmallEmphasized": { + "fontFamily": "System", + "fontSize": 11, + "fontWeight": "700", + "letterSpacing": 0.5, + "lineHeight": 16, + }, "titleLarge": { "fontFamily": "System", "fontSize": 22, @@ -725,6 +914,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0, "lineHeight": 28, }, + "titleLargeEmphasized": { + "fontFamily": "System", + "fontSize": 22, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 28, + }, "titleMedium": { "fontFamily": "System", "fontSize": 16, @@ -732,6 +928,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0.15, "lineHeight": 24, }, + "titleMediumEmphasized": { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "700", + "letterSpacing": 0.15, + "lineHeight": 24, + }, "titleSmall": { "fontFamily": "System", "fontSize": 14, @@ -739,6 +942,13 @@ exports[`renders list section with subheader 1`] = ` "letterSpacing": 0.1, "lineHeight": 20, }, + "titleSmallEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "700", + "letterSpacing": 0.1, + "lineHeight": 20, + }, }, "shapes": { "corner": { @@ -1184,7 +1394,14 @@ exports[`renders list section without subheader 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, + "lineHeight": 24, + }, + "bodyLargeEmphasized": { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.5, "lineHeight": 24, }, "bodyMedium": { @@ -1194,6 +1411,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0.25, "lineHeight": 20, }, + "bodyMediumEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.25, + "lineHeight": 20, + }, "bodySmall": { "fontFamily": "System", "fontSize": 12, @@ -1201,6 +1425,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0.4, "lineHeight": 16, }, + "bodySmallEmphasized": { + "fontFamily": "System", + "fontSize": 12, + "fontWeight": "500", + "letterSpacing": 0.4, + "lineHeight": 16, + }, "default": { "fontFamily": "System", "fontWeight": "400", @@ -1210,7 +1441,14 @@ exports[`renders list section without subheader 1`] = ` "fontFamily": "System", "fontSize": 57, "fontWeight": "400", - "letterSpacing": 0, + "letterSpacing": -0.25, + "lineHeight": 64, + }, + "displayLargeEmphasized": { + "fontFamily": "System", + "fontSize": 57, + "fontWeight": "500", + "letterSpacing": -0.25, "lineHeight": 64, }, "displayMedium": { @@ -1220,6 +1458,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0, "lineHeight": 52, }, + "displayMediumEmphasized": { + "fontFamily": "System", + "fontSize": 45, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 52, + }, "displaySmall": { "fontFamily": "System", "fontSize": 36, @@ -1227,6 +1472,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0, "lineHeight": 44, }, + "displaySmallEmphasized": { + "fontFamily": "System", + "fontSize": 36, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 44, + }, "headlineLarge": { "fontFamily": "System", "fontSize": 32, @@ -1234,6 +1486,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0, "lineHeight": 40, }, + "headlineLargeEmphasized": { + "fontFamily": "System", + "fontSize": 32, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 40, + }, "headlineMedium": { "fontFamily": "System", "fontSize": 28, @@ -1241,6 +1500,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0, "lineHeight": 36, }, + "headlineMediumEmphasized": { + "fontFamily": "System", + "fontSize": 28, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 36, + }, "headlineSmall": { "fontFamily": "System", "fontSize": 24, @@ -1248,6 +1514,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0, "lineHeight": 32, }, + "headlineSmallEmphasized": { + "fontFamily": "System", + "fontSize": 24, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 32, + }, "labelLarge": { "fontFamily": "System", "fontSize": 14, @@ -1255,6 +1528,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0.1, "lineHeight": 20, }, + "labelLargeEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "700", + "letterSpacing": 0.1, + "lineHeight": 20, + }, "labelMedium": { "fontFamily": "System", "fontSize": 12, @@ -1262,6 +1542,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0.5, "lineHeight": 16, }, + "labelMediumEmphasized": { + "fontFamily": "System", + "fontSize": 12, + "fontWeight": "700", + "letterSpacing": 0.5, + "lineHeight": 16, + }, "labelSmall": { "fontFamily": "System", "fontSize": 11, @@ -1269,6 +1556,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0.5, "lineHeight": 16, }, + "labelSmallEmphasized": { + "fontFamily": "System", + "fontSize": 11, + "fontWeight": "700", + "letterSpacing": 0.5, + "lineHeight": 16, + }, "titleLarge": { "fontFamily": "System", "fontSize": 22, @@ -1276,6 +1570,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0, "lineHeight": 28, }, + "titleLargeEmphasized": { + "fontFamily": "System", + "fontSize": 22, + "fontWeight": "500", + "letterSpacing": 0, + "lineHeight": 28, + }, "titleMedium": { "fontFamily": "System", "fontSize": 16, @@ -1283,6 +1584,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0.15, "lineHeight": 24, }, + "titleMediumEmphasized": { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "700", + "letterSpacing": 0.15, + "lineHeight": 24, + }, "titleSmall": { "fontFamily": "System", "fontSize": 14, @@ -1290,6 +1598,13 @@ exports[`renders list section without subheader 1`] = ` "letterSpacing": 0.1, "lineHeight": 20, }, + "titleSmallEmphasized": { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "700", + "letterSpacing": 0.1, + "lineHeight": 20, + }, }, "shapes": { "corner": { diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index 2a09d12a10..9d426f228c 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -408,7 +408,7 @@ exports[`renders menu with content styles 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -417,7 +417,7 @@ exports[`renders menu with content styles 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, @@ -532,7 +532,7 @@ exports[`renders menu with content styles 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -541,7 +541,7 @@ exports[`renders menu with content styles 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, @@ -1135,7 +1135,7 @@ exports[`renders visible menu 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -1144,7 +1144,7 @@ exports[`renders visible menu 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, @@ -1259,7 +1259,7 @@ exports[`renders visible menu 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -1268,7 +1268,7 @@ exports[`renders visible menu 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, diff --git a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap index e879443bf9..9b1babef34 100644 --- a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap @@ -142,7 +142,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -151,7 +151,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, @@ -306,7 +306,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -315,7 +315,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, @@ -470,7 +470,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -479,7 +479,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, @@ -634,7 +634,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -643,7 +643,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, @@ -758,7 +758,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, [ @@ -767,7 +767,7 @@ exports[`Menu Item renders menu item 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, }, undefined, diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap index 1954a404ef..278bd963a4 100644 --- a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap @@ -202,7 +202,7 @@ exports[`activity indicator snapshot test 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 0, }, { @@ -620,7 +620,7 @@ exports[`renders with placeholder 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 0, }, { @@ -997,7 +997,7 @@ exports[`renders with text 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 0, }, { diff --git a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap index 0a7334fa12..dbe10b45cc 100644 --- a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap @@ -112,7 +112,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 73, "opacity": 0, @@ -150,7 +150,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 73, "opacity": 0, @@ -207,7 +207,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -289,7 +289,7 @@ exports[`call onPress when affix adornment pressed 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, }, undefined, @@ -416,7 +416,7 @@ exports[`correctly applies a component as the text label 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -462,7 +462,7 @@ exports[`correctly applies a component as the text label 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -527,7 +527,7 @@ exports[`correctly applies a component as the text label 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -663,7 +663,7 @@ exports[`correctly applies cursorColor prop 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -701,7 +701,7 @@ exports[`correctly applies cursorColor prop 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -758,7 +758,7 @@ exports[`correctly applies cursorColor prop 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -894,7 +894,7 @@ exports[`correctly applies default textAlign based on default RTL 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -932,7 +932,7 @@ exports[`correctly applies default textAlign based on default RTL 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -989,7 +989,7 @@ exports[`correctly applies default textAlign based on default RTL 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -1117,7 +1117,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` "fontWeight": undefined, "height": 0, "left": 8, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "opacity": 1, "paddingHorizontal": 0, @@ -1157,7 +1157,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1194,7 +1194,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1253,7 +1253,7 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -1388,7 +1388,7 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1426,7 +1426,7 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1483,7 +1483,7 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -1621,7 +1621,7 @@ exports[`correctly applies textAlign center 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1659,7 +1659,7 @@ exports[`correctly applies textAlign center 1`] = ` "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1716,7 +1716,7 @@ exports[`correctly applies textAlign center 1`] = ` "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -1852,7 +1852,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1890,7 +1890,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -1947,7 +1947,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -1998,7 +1998,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, }, { @@ -2288,7 +2288,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -2326,7 +2326,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "fontSize": 16, "fontWeight": undefined, "left": 0, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "maxWidth": 97.33333333333333, "opacity": 0, @@ -2383,7 +2383,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, "minWidth": 65, "opacity": 1, @@ -2596,7 +2596,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "fontFamily": "System", "fontSize": 16, "fontWeight": undefined, - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": undefined, }, { diff --git a/src/theme/__tests__/fonts.test.js b/src/theme/__tests__/fonts.test.js index 9aafe31616..6650c4f098 100644 --- a/src/theme/__tests__/fonts.test.js +++ b/src/theme/__tests__/fonts.test.js @@ -1,4 +1,4 @@ -const customFontV3 = { +const customFont = { displayLarge: { fontFamily: 'NotoSans', letterSpacing: 0, @@ -104,6 +104,111 @@ const customFontV3 = { lineHeight: 16, fontSize: 12, }, + displayLargeEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 64, + fontSize: 57, + }, + displayMediumEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 52, + fontSize: 45, + }, + displaySmallEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 44, + fontSize: 36, + }, + headlineLargeEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 40, + fontSize: 32, + }, + headlineMediumEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 36, + fontSize: 28, + }, + headlineSmallEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 32, + fontSize: 24, + }, + titleLargeEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 28, + fontSize: 22, + }, + titleMediumEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '700', + lineHeight: 24, + fontSize: 16, + }, + titleSmallEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '700', + lineHeight: 20, + fontSize: 14, + }, + labelLargeEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '700', + lineHeight: 20, + fontSize: 14, + }, + labelMediumEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '700', + lineHeight: 16, + fontSize: 12, + }, + labelSmallEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '700', + lineHeight: 16, + fontSize: 11, + }, + bodyLargeEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 24, + fontSize: 16, + }, + bodyMediumEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 20, + fontSize: 14, + }, + bodySmallEmphasized: { + fontFamily: 'NotoSans', + letterSpacing: 0, + fontWeight: '500', + lineHeight: 16, + fontSize: 12, + }, default: { fontFamily: 'NotoSans', letterSpacing: 0, @@ -152,7 +257,7 @@ describe('configureFonts', () => { letterSpacing: 0, }, }) - ).toEqual(customFontV3); + ).toEqual(customFont); }); it('overrides properties passed in config for several variants', () => { @@ -179,7 +284,7 @@ describe('configureFonts', () => { ...typescale, bodyLarge: { fontFamily: 'NotoSans', - letterSpacing: 0.15, + letterSpacing: 0.5, fontWeight: '400', fontStyle: 'italic', lineHeight: 24, diff --git a/src/theme/schemes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx index ab49b62400..100910c4ab 100644 --- a/src/theme/schemes/DarkTheme.tsx +++ b/src/theme/schemes/DarkTheme.tsx @@ -1,11 +1,11 @@ -import { baseTheme } from './base'; +import { themeDefaults } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const DarkTheme: Theme = { - ...baseTheme, + ...themeDefaults, dark: true, mode: 'adaptive', colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'dark' }), diff --git a/src/theme/schemes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx index b5d97a9cae..35926afde9 100644 --- a/src/theme/schemes/LightTheme.tsx +++ b/src/theme/schemes/LightTheme.tsx @@ -1,11 +1,11 @@ -import { baseTheme } from './base'; +import { themeDefaults } from './base'; import { tokens } from '../tokens'; import { buildScheme } from '../tokens/sys/color/roles'; import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const LightTheme: Theme = { - ...baseTheme, + ...themeDefaults, dark: false, colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'light' }), shapes: defaultShapes, diff --git a/src/theme/schemes/base.ts b/src/theme/schemes/base.ts index 4601d7dc83..44d3a017e9 100644 --- a/src/theme/schemes/base.ts +++ b/src/theme/schemes/base.ts @@ -1,8 +1,13 @@ -import configureFonts from '../fonts'; +import { defaultShapes } from '../tokens/sys/shape'; +import { defaultFonts } from '../tokens/sys/typography'; +import type { Theme } from '../types'; -export const baseTheme = { - fonts: configureFonts(), +type ThemeDefaults = Omit; + +export const themeDefaults: ThemeDefaults = { animation: { scale: 1.0, }, + fonts: defaultFonts, + shapes: defaultShapes, }; diff --git a/src/theme/tokens/index.ts b/src/theme/tokens/index.ts index e8f041163e..b0b160b080 100644 --- a/src/theme/tokens/index.ts +++ b/src/theme/tokens/index.ts @@ -114,6 +114,8 @@ const ref = { default: 'sans-serif-medium', }), weightMedium: '500' as Font['fontWeight'], + + weightBold: '700' as Font['fontWeight'], }, /** State layers opacity @@ -143,9 +145,22 @@ const mediumType = { fontWeight: ref.typeface.weightMedium, }; +const emphasizedMediumType = { + fontFamily: ref.typeface.plainMedium, + letterSpacing: 0, + fontWeight: ref.typeface.weightMedium, +}; + +const emphasizedBoldType = { + fontFamily: ref.typeface.plainMedium, + letterSpacing: 0, + fontWeight: ref.typeface.weightBold, +}; + export const typescale = { displayLarge: { ...regularType, + letterSpacing: -0.25, lineHeight: 64, fontSize: 57, }, @@ -216,6 +231,7 @@ export const typescale = { ...mediumType, fontWeight: ref.typeface.weightRegular, fontFamily: ref.typeface.brandRegular, + letterSpacing: 0.5, lineHeight: 24, fontSize: 16, }, @@ -236,6 +252,95 @@ export const typescale = { fontSize: 12, }, + displayLargeEmphasized: { + ...emphasizedMediumType, + letterSpacing: -0.25, + lineHeight: 64, + fontSize: 57, + }, + displayMediumEmphasized: { + ...emphasizedMediumType, + lineHeight: 52, + fontSize: 45, + }, + displaySmallEmphasized: { + ...emphasizedMediumType, + lineHeight: 44, + fontSize: 36, + }, + + headlineLargeEmphasized: { + ...emphasizedMediumType, + lineHeight: 40, + fontSize: 32, + }, + headlineMediumEmphasized: { + ...emphasizedMediumType, + lineHeight: 36, + fontSize: 28, + }, + headlineSmallEmphasized: { + ...emphasizedMediumType, + lineHeight: 32, + fontSize: 24, + }, + + titleLargeEmphasized: { + ...emphasizedMediumType, + lineHeight: 28, + fontSize: 22, + }, + titleMediumEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.15, + lineHeight: 24, + fontSize: 16, + }, + titleSmallEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.1, + lineHeight: 20, + fontSize: 14, + }, + + labelLargeEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.1, + lineHeight: 20, + fontSize: 14, + }, + labelMediumEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.5, + lineHeight: 16, + fontSize: 12, + }, + labelSmallEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.5, + lineHeight: 16, + fontSize: 11, + }, + + bodyLargeEmphasized: { + ...emphasizedMediumType, + letterSpacing: 0.5, + lineHeight: 24, + fontSize: 16, + }, + bodyMediumEmphasized: { + ...emphasizedMediumType, + letterSpacing: 0.25, + lineHeight: 20, + fontSize: 14, + }, + bodySmallEmphasized: { + ...emphasizedMediumType, + letterSpacing: 0.4, + lineHeight: 16, + fontSize: 12, + }, + default: { ...regularType, }, diff --git a/src/theme/tokens/sys/typography.ts b/src/theme/tokens/sys/typography.ts new file mode 100644 index 0000000000..2ea3e08c6c --- /dev/null +++ b/src/theme/tokens/sys/typography.ts @@ -0,0 +1,4 @@ +import type { Typescale } from '../../types'; +import { typescale } from '../index'; + +export const defaultFonts: Typescale = typescale; From 01b1982f18278ece6284364784d81e93bd50606b Mon Sep 17 00:00:00 2001 From: Hristo Totov <47380018+hristototov@users.noreply.github.com> Date: Wed, 13 May 2026 18:51:02 +0300 Subject: [PATCH 12/94] feat: add direction prop to PaperProvider and useLocale hook for RTL support (#4921) --- docs/docs/guides/10-rtl.md | 82 +++++++++++++++++++ src/components/Appbar/AppbarBackIcon.tsx | 9 +- .../DataTable/DataTablePagination.tsx | 18 ++-- src/components/DataTable/DataTableTitle.tsx | 7 +- src/components/FAB/AnimatedFAB.tsx | 6 +- src/components/FAB/utils.ts | 12 +-- src/components/Icon.tsx | 13 +-- src/components/List/ListAccordion.tsx | 5 +- src/components/Menu/Menu.tsx | 5 +- src/components/Portal/Portal.tsx | 29 ++++--- src/components/ProgressBar.tsx | 5 +- src/components/Searchbar.tsx | 10 ++- src/components/Snackbar.tsx | 7 +- src/components/TextInput/TextInputFlat.tsx | 25 ++---- .../TextInput/TextInputOutlined.tsx | 15 ++-- src/components/Typography/AnimatedText.tsx | 12 +-- src/components/Typography/Text.tsx | 4 +- .../__tests__/ActivityIndicator.test.tsx | 3 +- src/components/__tests__/AnimatedFAB.test.tsx | 3 +- .../__tests__/Appbar/Appbar.test.tsx | 3 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 2 +- src/components/__tests__/Avatar.test.tsx | 3 +- src/components/__tests__/Badge.test.tsx | 3 +- src/components/__tests__/Banner.test.tsx | 3 +- .../__tests__/BottomNavigation.test.tsx | 3 +- src/components/__tests__/Button.test.tsx | 3 +- src/components/__tests__/Card/Card.test.tsx | 3 +- .../__tests__/Checkbox/Checkbox.test.tsx | 3 +- .../__tests__/Checkbox/CheckboxItem.test.tsx | 3 +- src/components/__tests__/Chip.test.tsx | 3 +- src/components/__tests__/DataTable.test.tsx | 3 +- src/components/__tests__/Dialog.test.tsx | 3 +- .../Drawer/DrawerCollapsedItem.test.tsx | 3 +- .../__tests__/Drawer/DrawerSection.test.tsx | 3 +- src/components/__tests__/DrawerItem.test.tsx | 3 +- src/components/__tests__/FAB.test.tsx | 3 +- src/components/__tests__/FABGroup.test.tsx | 3 +- src/components/__tests__/HelperText.test.tsx | 3 +- src/components/__tests__/Icon.test.tsx | 3 +- src/components/__tests__/IconButton.test.tsx | 3 +- .../__tests__/ListAccordion.test.tsx | 3 +- src/components/__tests__/ListImage.test.tsx | 5 +- src/components/__tests__/ListItem.test.tsx | 3 +- src/components/__tests__/ListSection.test.tsx | 3 +- src/components/__tests__/Menu.test.tsx | 3 +- src/components/__tests__/MenuItem.test.tsx | 3 +- src/components/__tests__/Modal.test.tsx | 3 +- src/components/__tests__/Portal.test.tsx | 3 +- src/components/__tests__/ProgressBar.test.tsx | 5 +- .../RadioButton/RadioButton.test.tsx | 3 +- .../RadioButton/RadioButtonGroup.test.tsx | 3 +- .../RadioButton/RadioButtonItem.test.tsx | 3 +- src/components/__tests__/Searchbar.test.tsx | 3 +- .../__tests__/SegmentedButton.test.tsx | 3 +- src/components/__tests__/Snackbar.test.tsx | 3 +- src/components/__tests__/Surface.test.tsx | 3 +- src/components/__tests__/Switch.test.tsx | 2 +- src/components/__tests__/TextInput.test.tsx | 33 ++++---- .../__tests__/ToggleButton.test.tsx | 3 +- src/components/__tests__/Tooltip.test.tsx | 3 +- .../__tests__/TouchableRipple.test.tsx | 3 +- .../__tests__/Typography/Text.test.tsx | 3 +- .../__snapshots__/Searchbar.test.tsx.snap | 6 +- src/core/PaperProvider.tsx | 8 +- src/core/locale.tsx | 45 ++++++++++ src/index.tsx | 3 + src/react-navigation/__tests__/index.test.tsx | 3 +- .../views/MaterialBottomTabView.tsx | 6 +- src/test-utils.tsx | 24 ++++++ tsconfig.build.json | 2 +- tsconfig.json | 3 +- 71 files changed, 342 insertions(+), 189 deletions(-) create mode 100644 docs/docs/guides/10-rtl.md create mode 100644 src/core/locale.tsx create mode 100644 src/test-utils.tsx diff --git a/docs/docs/guides/10-rtl.md b/docs/docs/guides/10-rtl.md new file mode 100644 index 0000000000..796ec3595c --- /dev/null +++ b/docs/docs/guides/10-rtl.md @@ -0,0 +1,82 @@ +--- +title: RTL Support +--- + +# RTL Support + +React Native Paper supports right-to-left (RTL) layouts for languages such as Arabic and Hebrew. + +## How it works + +By default, React Native Paper reads the writing direction from `I18nManager.getConstants().isRTL` on native platforms. So it will use your existing RTL setup on initial render. + +See [I18nManager](http://reactnative.dev/docs/i18nmanager) docs and [Enabling RTL support in Expo](https://docs.expo.dev/guides/localization/#enabling-rtl-support) to configure your app properly. + +On the Web, the RTL value is not set globally, unlike native platforms. `I18nManager.getConstants().isRTL` is a no-op on [React Native Web](https://necolas.github.io/react-native-web/). To enable RTL globally, you can specify `dir` attribute on the `html` element: + +```html + + + +``` + +Then, let `react-native-paper` know about it by using the `direction` prop on `PaperProvider` or the `LocaleProvider` component to match the writing direction in your app. + +:::note +The `direction` prop informs React Native Paper about the text direction in the app i.e. it doesn't change the text direction by itself. If you intend to support RTL languages, it's important to set this prop to the correct value that's configured in the app. If it doesn't match the actual text direction, the layout might be incorrect. +::: + +## Setting direction for the whole app + +Pass the `direction` prop to `PaperProvider`. Defaults to `'rtl'` when `I18nManager.getConstants().isRTL` returns `true`, otherwise `'ltr'`. + +Supported values: + +- `'ltr'`: Left-to-right text direction for languages like English, French etc. +- `'rtl'`: Right-to-left text direction for languages like Arabic, Hebrew etc. + +```js +import * as React from 'react'; +import { PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +export default function Main() { + return ( + + + + ); +} +``` + +## Overriding direction for a subtree + +Use `LocaleProvider` to override the direction for a specific part of the tree without affecting the rest of the app: + +```js +import * as React from 'react'; +import { LocaleProvider } from 'react-native-paper'; + +export default function ArabicSection() { + return ( + + {/* Components here will use RTL layout */} + + ); +} +``` + +## Reading the current direction + +The direction is available in your own components via the `useLocale` hook: + +```js +import * as React from 'react'; +import { useLocale } from 'react-native-paper'; + +function MyComponent() { + const { direction } = useLocale(); + + // Use the direction +} +``` diff --git a/src/components/Appbar/AppbarBackIcon.tsx b/src/components/Appbar/AppbarBackIcon.tsx index 7579a3f1a6..35eb98d18f 100644 --- a/src/components/Appbar/AppbarBackIcon.tsx +++ b/src/components/Appbar/AppbarBackIcon.tsx @@ -1,9 +1,12 @@ import * as React from 'react'; -import { I18nManager, Image, Platform, StyleSheet, View } from 'react-native'; +import { Image, Platform, StyleSheet, View } from 'react-native'; +import { useLocale } from '../../core/locale'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; const AppbarBackIcon = ({ size, color }: { size: number; color: string }) => { + const { direction } = useLocale(); + const isRTL = direction === 'rtl'; const iosIconSize = size - 3; return Platform.OS === 'ios' ? ( @@ -13,7 +16,7 @@ const AppbarBackIcon = ({ size, color }: { size: number; color: string }) => { { width: size, height: size, - transform: [{ scaleX: I18nManager.getConstants().isRTL ? -1 : 1 }], + transform: [{ scaleX: isRTL ? -1 : 1 }], }, ]} > @@ -31,7 +34,7 @@ const AppbarBackIcon = ({ size, color }: { size: number; color: string }) => { name="arrow-left" color={color} size={size} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> ); }; diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 6a3fc52b3d..056bb432b9 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -1,14 +1,9 @@ import * as React from 'react'; -import { - I18nManager, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import type { ThemeProp } from 'src/types'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import Button from '../Button/Button'; import IconButton from '../IconButton/IconButton'; @@ -92,6 +87,7 @@ const PaginationControls = ({ theme: themeOverrides, }: PaginationControlsProps) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const textColor = theme.colors.onSurface; @@ -104,7 +100,7 @@ const PaginationControls = ({ name="page-first" color={color} size={size} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> )} iconColor={textColor} @@ -120,7 +116,7 @@ const PaginationControls = ({ name="chevron-left" color={color} size={size} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> )} iconColor={textColor} @@ -135,7 +131,7 @@ const PaginationControls = ({ name="chevron-right" color={color} size={size} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> )} iconColor={textColor} @@ -151,7 +147,7 @@ const PaginationControls = ({ name="page-last" color={color} size={size} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> )} iconColor={textColor} diff --git a/src/components/DataTable/DataTableTitle.tsx b/src/components/DataTable/DataTableTitle.tsx index 3f911123b4..b31ec8b899 100644 --- a/src/components/DataTable/DataTableTitle.tsx +++ b/src/components/DataTable/DataTableTitle.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Animated, GestureResponderEvent, - I18nManager, PixelRatio, Pressable, StyleProp, @@ -11,6 +10,7 @@ import { ViewStyle, } from 'react-native'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; @@ -91,6 +91,7 @@ const DataTableTitle = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const { current: spinAnim } = React.useRef( new Animated.Value(sortDirection === 'ascending' ? 0 : 1) ); @@ -118,7 +119,7 @@ const DataTableTitle = ({ name="arrow-up" size={16} color={textColor} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> ) : null; @@ -140,7 +141,7 @@ const DataTableTitle = ({ // if numberOfLines causes wrap, center is lost. Align directly, sensitive to numeric and RTL numberOfLines > 1 ? numeric - ? I18nManager.getConstants().isRTL + ? direction === 'rtl' ? styles.leftText : styles.rightText : styles.centerText diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index af3222a4a2..84b14a6a92 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -9,7 +9,6 @@ import { Animated, Easing, GestureResponderEvent, - I18nManager, Platform, ScrollView, StyleProp, @@ -20,6 +19,7 @@ import { } from 'react-native'; import { getCombinedStyles, getFABColors, getLabelSizeWeb } from './utils'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { $Omit, $RemoveChildren, ThemeProp } from '../../types'; import type { IconSource } from '../Icon'; @@ -219,12 +219,13 @@ const AnimatedFAB = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const uppercase: boolean = uppercaseProp ?? false; const isIOS = Platform.OS === 'ios'; const isWeb = Platform.OS === 'web'; const isAnimatedFromRight = animateFrom === 'right'; const isIconStatic = iconMode === 'static'; - const { isRTL } = I18nManager; + const isRTL = direction === 'rtl'; const labelRef = React.useRef(null); const { current: visibility } = React.useRef( new Animated.Value(visible ? 1 : 0) @@ -342,6 +343,7 @@ const AnimatedFAB = ({ const combinedStyles = getCombinedStyles({ isAnimatedFromRight, isIconStatic, + isRTL, distance, animFAB, }); diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index 93485aecb3..1d0dbb68e7 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -1,17 +1,12 @@ import { MutableRefObject } from 'react'; -import { - Animated, - ColorValue, - I18nManager, - Platform, - ViewStyle, -} from 'react-native'; +import { Animated, ColorValue, Platform, ViewStyle } from 'react-native'; import type { InternalTheme } from '../../types'; type GetCombinedStylesProps = { isAnimatedFromRight: boolean; isIconStatic: boolean; + isRTL: boolean; distance: number; animFAB: Animated.Value; }; @@ -32,11 +27,10 @@ type BaseProps = { export const getCombinedStyles = ({ isAnimatedFromRight, isIconStatic, + isRTL, distance, animFAB, }: GetCombinedStylesProps): CombinedStyles => { - const { isRTL } = I18nManager; - const defaultPositionStyles = { left: -distance, right: undefined }; const combinedStyles: CombinedStyles = { diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index e792e2670e..eee770f6b7 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -1,12 +1,8 @@ import * as React from 'react'; -import { - I18nManager, - Image, - ImageSourcePropType, - Platform, -} from 'react-native'; +import { Image, ImageSourcePropType, Platform } from 'react-native'; import { accessibilityProps } from './MaterialCommunityIcon'; +import { useLocale } from '../core/locale'; import { Consumer as SettingsConsumer } from '../core/settings'; import { useInternalTheme } from '../core/theming'; import type { ThemeProp } from '../types'; @@ -109,12 +105,11 @@ const Icon = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction: layoutDirection } = useLocale(); const direction = typeof source === 'object' && source.direction && source.source ? source.direction === 'auto' - ? I18nManager.getConstants().isRTL - ? 'rtl' - : 'ltr' + ? layoutDirection : source.direction : null; diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 3eaee97d61..d79634d984 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { GestureResponderEvent, - I18nManager, NativeSyntheticEvent, StyleProp, StyleSheet, @@ -16,6 +15,7 @@ import { import { ListAccordionGroupContext } from './ListAccordionGroup'; import type { ListChildProps, Style } from './utils'; import { getAccordionColors, getLeftStyles } from './utils'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; @@ -198,6 +198,7 @@ const ListAccordion = ({ hitSlop, }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const [expanded, setExpanded] = React.useState( expandedProp || false ); @@ -316,7 +317,7 @@ const ListAccordion = ({ name={isExpanded ? 'chevron-up' : 'chevron-down'} color={descriptionColor} size={24} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> )} diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index ebbd94e057..1c56b7b574 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -4,7 +4,6 @@ import { Dimensions, Easing, EmitterSubscription, - I18nManager, Keyboard, KeyboardEvent as RNKeyboardEvent, LayoutRectangle, @@ -22,6 +21,7 @@ import { import { useSafeAreaInsets } from 'react-native-safe-area-context'; import MenuItem from './MenuItem'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { Elevation, Theme, ThemeProp } from '../../types'; import { addEventListener } from '../../utils/addEventListener'; @@ -200,6 +200,7 @@ const Menu = ({ keyboardShouldPersistTaps, }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const { colors: md3Colors } = theme as Theme; const insets = useSafeAreaInsets(); const [rendered, setRendered] = React.useState(visible); @@ -630,7 +631,7 @@ const Menu = ({ top: isCoordinate(anchor) ? topTransformation : topTransformation + additionalVerticalValue, - ...(I18nManager.getConstants().isRTL + ...(direction === 'rtl' ? { right: leftTransformation } : { left: leftTransformation }), }; diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index d44caf96d6..39aedc7d08 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -4,6 +4,7 @@ import type { InternalTheme } from 'src/types'; import PortalConsumer from './PortalConsumer'; import PortalHost, { PortalContext, PortalMethods } from './PortalHost'; +import { LocaleContext, LocaleProvider } from '../../core/locale'; import { Consumer as SettingsConsumer, Provider as SettingsProvider, @@ -49,19 +50,25 @@ class Portal extends React.Component { const { children, theme } = this.props; return ( - - {(settings) => ( - - {(manager) => ( - - - {children} - - + + {(locale) => ( + + {(settings) => ( + + {(manager) => ( + + + + {children} + + + + )} + )} - + )} - + ); } } diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index eefed6e50d..bf1941490f 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Animated, - I18nManager, LayoutChangeEvent, Platform, StyleProp, @@ -10,6 +9,7 @@ import { ViewStyle, } from 'react-native'; +import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import type { ThemeProp } from '../types'; @@ -53,7 +53,6 @@ export type Props = React.ComponentPropsWithRef & { const INDETERMINATE_DURATION = 2000; const INDETERMINATE_MAX_WIDTH = 0.6; -const { isRTL } = I18nManager; /** * Progress bar is an indicator used to present progress of some activity in the app. @@ -84,6 +83,8 @@ const ProgressBar = ({ }: Props) => { const isWeb = Platform.OS === 'web'; const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); + const isRTL = direction === 'rtl'; const { current: timer } = React.useRef( new Animated.Value(0) ); diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 69fbfb572d..ba169fd9ba 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Animated, GestureResponderEvent, - I18nManager, Platform, StyleProp, StyleSheet, @@ -19,6 +18,7 @@ import type { IconSource } from './Icon'; import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; +import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import { cornerNone } from '../theme/tokens/sys/shape'; import type { Theme, ThemeProp } from '../types'; @@ -194,6 +194,7 @@ const Searchbar = forwardRef( ref ) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const { colors, fonts } = theme as Theme; const root = React.useRef(null); @@ -229,6 +230,7 @@ const Searchbar = forwardRef( }; const isBarMode = mode === 'bar'; + const inputTextAlign = direction === 'rtl' ? 'right' : 'left'; const shouldRenderTraileringIcon = isBarMode && traileringIcon && @@ -265,7 +267,7 @@ const Searchbar = forwardRef( name="magnify" color={color} size={size} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> )) } @@ -280,6 +282,7 @@ const Searchbar = forwardRef( color: textColor, ...font, ...Platform.select({ web: { outline: 'none' } }), + textAlign: inputTextAlign, }, isBarMode ? styles.barModeInput : styles.viewModeInput, inputStyle, @@ -326,7 +329,7 @@ const Searchbar = forwardRef( name="close" color={color} size={size} - direction={I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'} + direction={direction} /> )) } @@ -376,7 +379,6 @@ const styles = StyleSheet.create({ fontSize: 18, paddingLeft: 8, alignSelf: 'stretch', - textAlign: I18nManager.getConstants().isRTL ? 'right' : 'left', minWidth: 0, }, barModeInput: { diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index c1f66bf402..f6eb590e84 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Animated, Easing, - I18nManager, StyleProp, StyleSheet, View, @@ -18,6 +17,7 @@ import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; import Text from './Typography/Text'; +import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import type { $Omit, $RemoveChildren, Theme, ThemeProp } from '../types'; @@ -158,6 +158,7 @@ const Snackbar = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const { bottom, right, left } = useSafeAreaInsets(); const { current: opacity } = React.useRef( @@ -349,9 +350,7 @@ const Snackbar = ({ name="close" color={color} size={size} - direction={ - I18nManager.getConstants().isRTL ? 'rtl' : 'ltr' - } + direction={direction} /> ); }) diff --git a/src/components/TextInput/TextInputFlat.tsx b/src/components/TextInput/TextInputFlat.tsx index 221d000a71..f7235ea5d2 100644 --- a/src/components/TextInput/TextInputFlat.tsx +++ b/src/components/TextInput/TextInputFlat.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { - I18nManager, Platform, StyleSheet, TextInput as NativeTextInput, @@ -41,6 +40,7 @@ import { } from './helpers'; import InputLabel from './Label/InputLabel'; import type { ChildTextInputProps, RenderProps } from './types'; +import { useLocale } from '../../core/locale'; const TextInputFlat = ({ disabled = false, @@ -78,6 +78,8 @@ const TextInputFlat = ({ ...rest }: ChildTextInputProps) => { const isAndroid = Platform.OS === 'android'; + const { direction } = useLocale(); + const isRTL = direction === 'rtl'; const { colors } = theme; const roundness = theme.shapes.corner.extraSmall; const font = theme.fonts.bodyLarge; @@ -170,11 +172,8 @@ const TextInputFlat = ({ const labelHalfHeight = labelHeight / 2; const baseLabelTranslateX = - (I18nManager.getConstants().isRTL ? 1 : -1) * - (labelHalfWidth - (labelScale * labelWidth) / 2) + - (1 - labelScale) * - (I18nManager.getConstants().isRTL ? -1 : 1) * - paddingLeft; + (isRTL ? 1 : -1) * (labelHalfWidth - (labelScale * labelWidth) / 2) + + (1 - labelScale) * (isRTL ? -1 : 1) * paddingLeft; const minInputHeight = dense ? (label ? MIN_DENSE_HEIGHT_WL : MIN_DENSE_HEIGHT) - LABEL_PADDING_TOP_DENSE @@ -278,13 +277,9 @@ const TextInputFlat = ({ labelScale, wiggleOffsetX: LABEL_WIGGLE_X_OFFSET, topPosition, - paddingLeft: isAndroid - ? I18nManager.isRTL - ? paddingRight - : paddingLeft - : paddingLeft, + paddingLeft: isAndroid ? (isRTL ? paddingRight : paddingLeft) : paddingLeft, paddingRight: isAndroid - ? I18nManager.isRTL + ? isRTL ? paddingLeft : paddingRight : paddingRight, @@ -420,11 +415,7 @@ const TextInputFlat = ({ color: inputTextColor, opacity: disabledOpacity, textAlignVertical: multiline ? 'top' : 'center', - textAlign: textAlign - ? textAlign - : I18nManager.getConstants().isRTL - ? 'right' - : 'left', + textAlign: textAlign ? textAlign : isRTL ? 'right' : 'left', minWidth: Math.min( parentState.labelTextLayout.width + 2 * FLAT_INPUT_OFFSET, MIN_WIDTH diff --git a/src/components/TextInput/TextInputOutlined.tsx b/src/components/TextInput/TextInputOutlined.tsx index aa563cf55f..be4fae7487 100644 --- a/src/components/TextInput/TextInputOutlined.tsx +++ b/src/components/TextInput/TextInputOutlined.tsx @@ -4,7 +4,6 @@ import { View, TextInput as NativeTextInput, StyleSheet, - I18nManager, Platform, TextStyle, ColorValue, @@ -41,6 +40,7 @@ import { import InputLabel from './Label/InputLabel'; import LabelBackground from './Label/LabelBackground'; import type { RenderProps, ChildTextInputProps } from './types'; +import { useLocale } from '../../core/locale'; const TextInputOutlined = ({ disabled = false, @@ -80,6 +80,8 @@ const TextInputOutlined = ({ ...rest }: ChildTextInputProps) => { const adornmentConfig = getAdornmentConfig({ left, right }); + const { direction } = useLocale(); + const isRTL = direction === 'rtl'; const { colors } = theme; const roundness = theme.shapes.corner.extraSmall; @@ -133,7 +135,7 @@ const TextInputOutlined = ({ const labelHalfHeight = labelHeight / 2; const baseLabelTranslateX = - (I18nManager.getConstants().isRTL ? 1 : -1) * + (isRTL ? 1 : -1) * (labelHalfWidth - (labelScale * labelWidth) / 2 - (fontSize - MINIMIZED_LABEL_FONT_SIZE) * labelScale); @@ -150,8 +152,7 @@ const TextInputOutlined = ({ if (isAdornmentLeftIcon) { labelTranslationXOffset = - (I18nManager.getConstants().isRTL ? -1 : 1) * ADORNMENT_SIZE + - ADORNMENT_OFFSET; + (isRTL ? -1 : 1) * ADORNMENT_SIZE + ADORNMENT_OFFSET; } const minInputHeight = @@ -404,11 +405,7 @@ const TextInputOutlined = ({ color: inputTextColor, opacity: disabledOpacity, textAlignVertical: multiline ? 'top' : 'center', - textAlign: textAlign - ? textAlign - : I18nManager.getConstants().isRTL - ? 'right' - : 'left', + textAlign: textAlign ? textAlign : isRTL ? 'right' : 'left', paddingHorizontal: INPUT_PADDING_HORIZONTAL, minWidth: Math.min( parentState.labelTextLayout.width + diff --git a/src/components/Typography/AnimatedText.tsx b/src/components/Typography/AnimatedText.tsx index 19872e2d93..5da34b474d 100644 --- a/src/components/Typography/AnimatedText.tsx +++ b/src/components/Typography/AnimatedText.tsx @@ -1,15 +1,9 @@ import * as React from 'react'; import { ReactNode } from 'react'; -import { - Animated, - I18nManager, - StyleProp, - StyleSheet, - TextStyle, - Text, -} from 'react-native'; +import { Animated, StyleProp, StyleSheet, TextStyle, Text } from 'react-native'; import type { VariantProp } from './types'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; @@ -48,7 +42,7 @@ const AnimatedText = forwardRef>( ref ) { const theme = useInternalTheme(themeOverrides); - const writingDirection = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'; + const { direction: writingDirection } = useLocale(); if (variant) { const font = theme.fonts[variant]; diff --git a/src/components/Typography/Text.tsx b/src/components/Typography/Text.tsx index c80d4399e4..4ed4ddd529 100644 --- a/src/components/Typography/Text.tsx +++ b/src/components/Typography/Text.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { ReactNode } from 'react'; import { - I18nManager, StyleProp, StyleSheet, Text as NativeText, @@ -10,6 +9,7 @@ import { import AnimatedText from './AnimatedText'; import type { VariantProp } from './types'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; @@ -87,7 +87,7 @@ const Text = ( const root = React.useRef(null); // FIXME: destructure it in TS 4.6+ const theme = useInternalTheme(initialTheme); - const writingDirection = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'; + const { direction: writingDirection } = useLocale(); React.useImperativeHandle(ref, () => ({ setNativeProps: (args: Object) => root.current?.setNativeProps(args), diff --git a/src/components/__tests__/ActivityIndicator.test.tsx b/src/components/__tests__/ActivityIndicator.test.tsx index f787fe9967..1b276f9234 100644 --- a/src/components/__tests__/ActivityIndicator.test.tsx +++ b/src/components/__tests__/ActivityIndicator.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../test-utils'; import ActivityIndicator from '../ActivityIndicator'; it('renders indicator', () => { diff --git a/src/components/__tests__/AnimatedFAB.test.tsx b/src/components/__tests__/AnimatedFAB.test.tsx index 4017f019fc..318f59c3b9 100644 --- a/src/components/__tests__/AnimatedFAB.test.tsx +++ b/src/components/__tests__/AnimatedFAB.test.tsx @@ -3,9 +3,10 @@ import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; -import { fireEvent, render } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; import { act } from 'react-test-renderer'; +import { render } from '../../test-utils'; import { Palette } from '../../theme/tokens'; import AnimatedFAB from '../FAB/AnimatedFAB'; diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 0419e18e57..69a6ad13e5 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { Animated } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'; import { getTheme } from '../../../core/theming'; +import { render } from '../../../test-utils'; import { tokens } from '../../../theme/tokens'; import Appbar from '../../Appbar'; import { diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index c7b786a718..d90d5553cd 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -236,7 +236,6 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "fontSize": 18, "minWidth": 0, "paddingLeft": 8, - "textAlign": "left", }, { "color": "rgba(73, 69, 79, 1)", @@ -245,6 +244,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "fontWeight": "400", "letterSpacing": 0.5, "lineHeight": 0, + "textAlign": "left", }, { "minHeight": 56, diff --git a/src/components/__tests__/Avatar.test.tsx b/src/components/__tests__/Avatar.test.tsx index d21100c0db..19842d435a 100644 --- a/src/components/__tests__/Avatar.test.tsx +++ b/src/components/__tests__/Avatar.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import { fireEvent, render } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; import * as Avatar from '../Avatar/Avatar'; diff --git a/src/components/__tests__/Badge.test.tsx b/src/components/__tests__/Badge.test.tsx index 2beabe9eae..3f7e6c8fcc 100644 --- a/src/components/__tests__/Badge.test.tsx +++ b/src/components/__tests__/Badge.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; import Badge from '../Badge'; diff --git a/src/components/__tests__/Banner.test.tsx b/src/components/__tests__/Banner.test.tsx index 048bfa7275..8066555354 100644 --- a/src/components/__tests__/Banner.test.tsx +++ b/src/components/__tests__/Banner.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { Animated, Image } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import Banner from '../Banner'; it('renders hidden banner, without action buttons and without image', () => { diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index 496987e254..0ebc9458ed 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { Animated, Easing, Platform, StyleSheet } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { Palette } from '../../theme/tokens'; import BottomNavigation from '../BottomNavigation/BottomNavigation'; import BottomNavigationRouteScreen from '../BottomNavigation/BottomNavigationRouteScreen'; diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index a203e1d237..71107089e7 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { pink500, white } from '../../theme/colors'; import { tokens } from '../../theme/tokens'; import Button from '../Button/Button'; diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index ac4eabfa11..b11e006445 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -1,9 +1,10 @@ import React from 'react'; import { Animated, StyleSheet, Text } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; import { getTheme } from '../../../core/theming'; +import { render } from '../../../test-utils'; import { Palette } from '../../../theme/tokens'; import Button from '../../Button/Button'; import Card from '../../Card/Card'; diff --git a/src/components/__tests__/Checkbox/Checkbox.test.tsx b/src/components/__tests__/Checkbox/Checkbox.test.tsx index efa819007e..1a4d618387 100644 --- a/src/components/__tests__/Checkbox/Checkbox.test.tsx +++ b/src/components/__tests__/Checkbox/Checkbox.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../../test-utils'; import Checkbox from '../../Checkbox'; it('renders checked Checkbox with onPress', () => { diff --git a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx index cfb6007b5c..28546d16e5 100644 --- a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx +++ b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { Platform } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; +import { render } from '../../../test-utils'; import Checkbox from '../../Checkbox'; it('renders unchecked', () => { diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 4ed5cad4ea..cf0f89ddba 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; import { Animated } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; import color from 'color'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { tokens } from '../../theme/tokens'; import Chip from '../Chip/Chip'; import { getChipColors } from '../Chip/helpers'; diff --git a/src/components/__tests__/DataTable.test.tsx b/src/components/__tests__/DataTable.test.tsx index ae212d281f..01c70031ec 100644 --- a/src/components/__tests__/DataTable.test.tsx +++ b/src/components/__tests__/DataTable.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../test-utils'; import Checkbox from '../Checkbox'; import DataTable from '../DataTable/DataTable'; diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 8f7e327a72..742f44b941 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -7,9 +7,10 @@ import { BackHandlerStatic as RNBackHandlerStatic, } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; import Dialog from '../../components/Dialog/Dialog'; +import { render } from '../../test-utils'; import Button from '../Button/Button'; interface BackHandlerStatic extends RNBackHandlerStatic { diff --git a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx index bc3b32885a..18908acc8a 100644 --- a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx +++ b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../../test-utils'; import DrawerCollapsedItem from '../../Drawer/DrawerCollapsedItem'; describe('DrawerCollapsedItem', () => { diff --git a/src/components/__tests__/Drawer/DrawerSection.test.tsx b/src/components/__tests__/Drawer/DrawerSection.test.tsx index e74cf362c6..feb8c2aad7 100644 --- a/src/components/__tests__/Drawer/DrawerSection.test.tsx +++ b/src/components/__tests__/Drawer/DrawerSection.test.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { View } from 'react-native'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../../test-utils'; import DrawerSection from '../../Drawer/DrawerSection'; describe('DrawerSection', () => { diff --git a/src/components/__tests__/DrawerItem.test.tsx b/src/components/__tests__/DrawerItem.test.tsx index ec4a36efcb..a12478d51c 100644 --- a/src/components/__tests__/DrawerItem.test.tsx +++ b/src/components/__tests__/DrawerItem.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../test-utils'; import DrawerItem from '../Drawer/DrawerItem'; it('renders basic DrawerItem', () => { diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index 91b54e22be..e99c694885 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; -import { fireEvent, render } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; import { act } from 'react-test-renderer'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import FAB from '../FAB'; import { getFABColors } from '../FAB/utils'; diff --git a/src/components/__tests__/FABGroup.test.tsx b/src/components/__tests__/FABGroup.test.tsx index 5f25e46c6f..f90e534910 100644 --- a/src/components/__tests__/FABGroup.test.tsx +++ b/src/components/__tests__/FABGroup.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { Animated } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import FAB from '../FAB'; import { getFABGroupColors } from '../FAB/utils'; diff --git a/src/components/__tests__/HelperText.test.tsx b/src/components/__tests__/HelperText.test.tsx index ce16526776..22ffc025b9 100644 --- a/src/components/__tests__/HelperText.test.tsx +++ b/src/components/__tests__/HelperText.test.tsx @@ -1,8 +1,7 @@ import React from 'react'; -import { render } from '@testing-library/react-native'; - import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import HelperText from '../HelperText/HelperText'; describe('HelperText', () => { diff --git a/src/components/__tests__/Icon.test.tsx b/src/components/__tests__/Icon.test.tsx index 29944da5ac..3c1d55af19 100644 --- a/src/components/__tests__/Icon.test.tsx +++ b/src/components/__tests__/Icon.test.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { Image } from 'react-native'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../test-utils'; import Icon from '../Icon'; const ICON_SIZE = 24; diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index 229b8b462e..9702fe499c 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { pink500 } from '../../theme/colors'; import { tokens } from '../../theme/tokens'; import IconButton from '../IconButton/IconButton'; diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index 93e3c62bad..96b057e5b1 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; -import { render } from '@testing-library/react-native'; - import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; import ListAccordion from '../List/ListAccordion'; import ListAccordionGroup from '../List/ListAccordionGroup'; diff --git a/src/components/__tests__/ListImage.test.tsx b/src/components/__tests__/ListImage.test.tsx index 5248019277..2277eb324a 100644 --- a/src/components/__tests__/ListImage.test.tsx +++ b/src/components/__tests__/ListImage.test.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../test-utils'; import ListImage from '../List/ListImage'; const styles = StyleSheet.create({ @@ -52,7 +51,6 @@ it('renders ListImage with `image` variant', () => { /> ); - expect(tree.container.props['variant']).toBe('image'); expect(tree.getByTestId(testID)).toHaveStyle(styles.image); }); @@ -64,6 +62,5 @@ it('renders ListImage with `video` variant', () => { /> ); - expect(tree.container.props['variant']).toBe('video'); expect(tree.getByTestId(testID)).toHaveStyle(styles.video); }); diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index c2b7f0d88d..e1c2f20357 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -2,8 +2,9 @@ import * as React from 'react'; import { Platform, StyleSheet } from 'react-native'; import { Text, View } from 'react-native'; -import { fireEvent, render } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; import Chip from '../Chip/Chip'; import IconButton from '../IconButton/IconButton'; diff --git a/src/components/__tests__/ListSection.test.tsx b/src/components/__tests__/ListSection.test.tsx index 91efbfc008..27a48f748a 100644 --- a/src/components/__tests__/ListSection.test.tsx +++ b/src/components/__tests__/ListSection.test.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; import ListIcon from '../List/ListIcon'; import ListItem from '../List/ListItem'; diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 753124e478..5737f4d313 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { Animated, Dimensions, StyleSheet, View } from 'react-native'; -import { act, render, screen, waitFor } from '@testing-library/react-native'; +import { act, screen, waitFor } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { Elevation } from '../../types'; import Button from '../Button/Button'; import Menu, { ELEVATION_LEVELS_MAP } from '../Menu/Menu'; diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index 98d433723d..8b525a5844 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { tokens } from '../../theme/tokens'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 9fd9eef832..f4b37bc564 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -6,8 +6,9 @@ import { Text, } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import Modal from '../Modal'; diff --git a/src/components/__tests__/Portal.test.tsx b/src/components/__tests__/Portal.test.tsx index ed87dce678..c92f74f8ed 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { Text } from 'react-native'; -import { render, waitFor } from '@testing-library/react-native'; +import { waitFor } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import Portal from '../Portal/Portal'; jest.useRealTimers(); diff --git a/src/components/__tests__/ProgressBar.test.tsx b/src/components/__tests__/ProgressBar.test.tsx index 9aac20ac91..0a3445b601 100644 --- a/src/components/__tests__/ProgressBar.test.tsx +++ b/src/components/__tests__/ProgressBar.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { Animated, Platform, StyleSheet } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import ProgressBar, { Props } from '../ProgressBar'; const layoutEvent = { @@ -46,7 +47,7 @@ it('renders progress bar with animated value', async () => { tree.update(); - expect(tree.container.props['animatedValue']).toBe(0.4); + expect(tree.getByRole(a11yRole)).toBeTruthy(); }); it('renders progress bar with specific progress', async () => { diff --git a/src/components/__tests__/RadioButton/RadioButton.test.tsx b/src/components/__tests__/RadioButton/RadioButton.test.tsx index ecba6bf851..89e24261d1 100644 --- a/src/components/__tests__/RadioButton/RadioButton.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButton.test.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../../test-utils'; import RadioButton from '../../RadioButton'; import { RadioButtonContext } from '../../RadioButton/RadioButtonGroup'; diff --git a/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx b/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx index 2b431102cb..ce1ae61c70 100644 --- a/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import { render } from '@testing-library/react-native'; - +import { render } from '../../../test-utils'; import RadioButton from '../../RadioButton'; describe('RadioButtonGroup', () => { diff --git a/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx b/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx index 84e7b52971..6afd21a48a 100644 --- a/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { Platform } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; +import { render } from '../../../test-utils'; import RadioButton from '../../RadioButton'; it('renders unchecked', () => { diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index eb44e85481..255225f7ae 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { Animated } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import * as Avatar from '../Avatar/Avatar'; import Searchbar from '../Searchbar'; diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 06a7390626..94ab9828b1 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { tokens } from '../../theme/tokens'; import SegmentedButtons from '../SegmentedButtons/SegmentedButtons'; import { diff --git a/src/components/__tests__/Snackbar.test.tsx b/src/components/__tests__/Snackbar.test.tsx index a5938d516b..334a9bca0a 100644 --- a/src/components/__tests__/Snackbar.test.tsx +++ b/src/components/__tests__/Snackbar.test.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { Animated, StyleSheet, Text, View } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import { red200, white } from '../../theme/colors'; import Snackbar from '../Snackbar'; diff --git a/src/components/__tests__/Surface.test.tsx b/src/components/__tests__/Surface.test.tsx index 045a63a3e6..c58f173945 100644 --- a/src/components/__tests__/Surface.test.tsx +++ b/src/components/__tests__/Surface.test.tsx @@ -2,9 +2,8 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; import { Platform } from 'react-native'; -import { render } from '@testing-library/react-native'; - import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import Surface from '../Surface'; describe('Surface', () => { diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index 2022439d76..27637c085b 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import { Platform } from 'react-native'; -import { render } from '@testing-library/react-native'; import color from 'color'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import { white, black, diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index a9f06c7a8e..3b4c860e1f 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -1,10 +1,12 @@ /* eslint-disable react-native/no-inline-styles */ import * as React from 'react'; -import { I18nManager, Platform, StyleSheet, Text, View } from 'react-native'; +import { Platform, StyleSheet, Text, View } from 'react-native'; -import { fireEvent, render } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; +import PaperProvider from '../../core/PaperProvider'; import { DefaultTheme, getTheme, ThemeProvider } from '../../core/theming'; +import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; import { tokens } from '../../theme/tokens'; import { @@ -259,28 +261,27 @@ it('renders input placeholder initially with transparent placeholderTextColor', it('correctly applies padding offset to input label on Android when RTL', () => { Platform.OS = 'android'; - I18nManager.isRTL = true; const { getByTestId } = render( - - } - right={ - - } - /> + + + } + right={ + + } + /> + ); expect(getByTestId('text-input-flat-label-active')).toHaveStyle({ paddingLeft: 56, paddingRight: 16, }); - - I18nManager.isRTL = false; }); it('correctly applies padding offset to input label on Android when LTR', () => { diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx index a830558681..b9af5d3b3c 100644 --- a/src/components/__tests__/ToggleButton.test.tsx +++ b/src/components/__tests__/ToggleButton.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { Animated } from 'react-native'; -import { act, render } from '@testing-library/react-native'; +import { act } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; +import { render } from '../../test-utils'; import ToggleButton from '../ToggleButton'; import { getToggleButtonColor } from '../ToggleButton/utils'; diff --git a/src/components/__tests__/Tooltip.test.tsx b/src/components/__tests__/Tooltip.test.tsx index 075b534ba6..6c59eaa60b 100644 --- a/src/components/__tests__/Tooltip.test.tsx +++ b/src/components/__tests__/Tooltip.test.tsx @@ -1,10 +1,11 @@ import React, { RefObject } from 'react'; import { Dimensions, Text, View, Platform } from 'react-native'; -import { act, fireEvent, render } from '@testing-library/react-native'; +import { act, fireEvent } from '@testing-library/react-native'; import type { ReactTestInstance } from 'react-test-renderer'; import PaperProvider from '../../core/PaperProvider'; +import { render } from '../../test-utils'; import Tooltip from '../Tooltip/Tooltip'; const mockedRemoveEventListener = jest.fn(); diff --git a/src/components/__tests__/TouchableRipple.test.tsx b/src/components/__tests__/TouchableRipple.test.tsx index c578605b3a..89bebfa7fa 100644 --- a/src/components/__tests__/TouchableRipple.test.tsx +++ b/src/components/__tests__/TouchableRipple.test.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { Platform, Text } from 'react-native'; -import { render, fireEvent } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; +import { render } from '../../test-utils'; import TouchableRipple from '../TouchableRipple/TouchableRipple.native'; describe('TouchableRipple', () => { diff --git a/src/components/__tests__/Typography/Text.test.tsx b/src/components/__tests__/Typography/Text.test.tsx index 67f99ae94b..3fc4b37361 100644 --- a/src/components/__tests__/Typography/Text.test.tsx +++ b/src/components/__tests__/Typography/Text.test.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; -import { render } from '@testing-library/react-native'; - import PaperProvider from '../../../core/PaperProvider'; +import { render } from '../../../test-utils'; import configureFonts from '../../../theme/fonts'; import { LightTheme } from '../../../theme/schemes'; import { tokens } from '../../../theme/tokens'; diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap index 278bd963a4..5d247103ff 100644 --- a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap @@ -195,7 +195,6 @@ exports[`activity indicator snapshot test 1`] = ` "fontSize": 18, "minWidth": 0, "paddingLeft": 8, - "textAlign": "left", }, { "color": "rgba(73, 69, 79, 1)", @@ -204,6 +203,7 @@ exports[`activity indicator snapshot test 1`] = ` "fontWeight": "400", "letterSpacing": 0.5, "lineHeight": 0, + "textAlign": "left", }, { "minHeight": 56, @@ -613,7 +613,6 @@ exports[`renders with placeholder 1`] = ` "fontSize": 18, "minWidth": 0, "paddingLeft": 8, - "textAlign": "left", }, { "color": "rgba(73, 69, 79, 1)", @@ -622,6 +621,7 @@ exports[`renders with placeholder 1`] = ` "fontWeight": "400", "letterSpacing": 0.5, "lineHeight": 0, + "textAlign": "left", }, { "minHeight": 56, @@ -990,7 +990,6 @@ exports[`renders with text 1`] = ` "fontSize": 18, "minWidth": 0, "paddingLeft": 8, - "textAlign": "left", }, { "color": "rgba(73, 69, 79, 1)", @@ -999,6 +998,7 @@ exports[`renders with text 1`] = ` "fontWeight": "400", "letterSpacing": 0.5, "lineHeight": 0, + "textAlign": "left", }, { "minHeight": 56, diff --git a/src/core/PaperProvider.tsx b/src/core/PaperProvider.tsx index 01a80fdcd9..cdb2d5d8a9 100644 --- a/src/core/PaperProvider.tsx +++ b/src/core/PaperProvider.tsx @@ -6,6 +6,7 @@ import { NativeEventSubscription, } from 'react-native'; +import { getDefaultDirection, LocaleProvider, type Direction } from './locale'; import SafeAreaProviderCompat from './SafeAreaProviderCompat'; import { Provider as SettingsProvider, Settings } from './settings'; import { defaultThemes, ThemeProvider } from './theming'; @@ -18,6 +19,7 @@ export type Props = { children: React.ReactNode; theme?: ThemeProp; settings?: Settings; + direction?: Direction; }; const PaperProvider = (props: Props) => { @@ -88,6 +90,8 @@ const PaperProvider = (props: Props) => { const { children, settings } = props; + const direction = props.direction ?? getDefaultDirection(); + const settingsValue = React.useMemo( () => ({ icon: MaterialCommunityIcon, @@ -101,7 +105,9 @@ const PaperProvider = (props: Props) => { - {children} + + {children} + diff --git a/src/core/locale.tsx b/src/core/locale.tsx new file mode 100644 index 0000000000..11433f736f --- /dev/null +++ b/src/core/locale.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { I18nManager } from 'react-native'; + +/** + * Writing direction of the app. + * Use this to override RTL/LTR on platforms where `I18nManager` is a no-op (e.g. React Native Web). + */ +export type Direction = 'ltr' | 'rtl'; + +export type LocaleContextValue = { + direction: Direction; +}; + +export const LocaleContext = React.createContext( + null +); + +export type LocaleProviderProps = { + direction: Direction; + children: React.ReactNode; +}; + +/** + * Provider component for locale configuration. + */ +export function LocaleProvider({ direction, children }: LocaleProviderProps) { + const value = React.useMemo(() => ({ direction }), [direction]); + return ( + {children} + ); +} + +/** + * Returns the locale context value. Must be used inside a `PaperProvider` (or `LocaleProvider`). + */ +export function useLocale(): LocaleContextValue { + const context = React.useContext(LocaleContext); + if (context === null) { + throw new Error('useLocale must be used within a LocaleProvider'); + } + return context; +} + +export const getDefaultDirection = (): Direction => + I18nManager.getConstants?.().isRTL ? 'rtl' : 'ltr'; diff --git a/src/index.tsx b/src/index.tsx index 298658cf50..ce2d6f98dc 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -8,10 +8,13 @@ export { adaptNavigationTheme, } from './core/theming'; +export { useLocale, LocaleProvider } from './core/locale'; + export * from './theme/schemes'; export { default as Provider } from './core/PaperProvider'; export { default as PaperProvider } from './core/PaperProvider'; + export { default as shadow } from './theme/shadow'; export { default as configureFonts } from './theme/fonts'; diff --git a/src/react-navigation/__tests__/index.test.tsx b/src/react-navigation/__tests__/index.test.tsx index a04495639d..ead428dcf3 100644 --- a/src/react-navigation/__tests__/index.test.tsx +++ b/src/react-navigation/__tests__/index.test.tsx @@ -2,9 +2,10 @@ import * as React from 'react'; import { Button, Text, View } from 'react-native'; import { NavigationContainer, ParamListBase } from '@react-navigation/native'; -import { fireEvent, render } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; import PaperProvider from '../../core/PaperProvider'; +import { render } from '../../test-utils'; import { createMaterialBottomTabNavigator, MaterialBottomTabScreenProps, diff --git a/src/react-navigation/views/MaterialBottomTabView.tsx b/src/react-navigation/views/MaterialBottomTabView.tsx index 98378e0c79..07d3644d16 100644 --- a/src/react-navigation/views/MaterialBottomTabView.tsx +++ b/src/react-navigation/views/MaterialBottomTabView.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { I18nManager, Platform, StyleSheet } from 'react-native'; +import { Platform, StyleSheet } from 'react-native'; import { CommonActions, @@ -12,6 +12,7 @@ import { import BottomNavigation from '../../components/BottomNavigation/BottomNavigation'; import MaterialCommunityIcon from '../../components/MaterialCommunityIcon'; +import { useLocale } from '../../core/locale'; import type { MaterialBottomTabDescriptorMap, MaterialBottomTabNavigationConfig, @@ -30,6 +31,7 @@ export default function MaterialBottomTabView({ ...rest }: Props) { const buildLink = useLinkBuilder(); + const { direction } = useLocale(); return ( ( + + + {children} + + +); + +const customRender = (ui: React.ReactElement, options?: RenderOptions) => + render(ui, { wrapper: Wrapper, ...options }); + +export * from '@testing-library/react-native'; +export { customRender as render }; diff --git a/tsconfig.build.json b/tsconfig.build.json index 06de95f291..e7485e3e40 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,4 @@ { "extends": "./tsconfig", - "exclude": ["example", "docs", "**/__tests__/*"] + "exclude": ["example", "docs", "**/__tests__/*", "src/test-utils.tsx"] } diff --git a/tsconfig.json b/tsconfig.json index c0b7c9cd69..56d776d859 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,8 @@ "resolveJsonModule": true, "skipLibCheck": true, "strict": true, - "target": "esnext" + "target": "esnext", + "types": ["jest", "node"] }, "exclude": [ "lib/**/*" From 57c06e17f0a1ea7cdf9829b46621f22e7cd83427 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 5 May 2026 14:23:53 +0300 Subject: [PATCH 13/94] feat: add motion tokens (spring, easing, duration) Adds theme.motion with two preset schemes (expressiveMotion default, standardMotion), M3 easing curves, and duration constants verified against the material.io spec. Includes toRawSpring() helper to convert the spec damping ratio to the raw coefficient expected by Animated.spring and Reanimated --- .../__snapshots__/ListSection.test.tsx.snap | 288 ++++++++++++++++++ src/core/__tests__/PaperProvider.test.tsx | 14 - src/theme/schemes/base.ts | 2 + src/theme/tokens/sys/motion.ts | 99 ++++++ src/theme/types/index.ts | 1 + src/theme/types/motion.ts | 47 +++ src/theme/types/theme.ts | 3 +- 7 files changed, 439 insertions(+), 15 deletions(-) create mode 100644 src/theme/tokens/sys/motion.ts create mode 100644 src/theme/types/motion.ts diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index f49e1240c8..957f5f71f4 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -292,6 +292,102 @@ exports[`renders list section with custom title style 1`] = ` "lineHeight": 20, }, }, + "motion": { + "duration": { + "extraLong1": 700, + "extraLong2": 800, + "extraLong3": 900, + "extraLong4": 1000, + "long1": 450, + "long2": 500, + "long3": 550, + "long4": 600, + "medium1": 250, + "medium2": 300, + "medium3": 350, + "medium4": 400, + "short1": 50, + "short2": 100, + "short3": 150, + "short4": 200, + }, + "easing": { + "emphasized": [ + 0.2, + 0, + 0, + 1, + ], + "emphasizedAccelerate": [ + 0.3, + 0, + 0.8, + 0.15, + ], + "emphasizedDecelerate": [ + 0.05, + 0.7, + 0.1, + 1, + ], + "linear": [ + 0, + 0, + 1, + 1, + ], + "standard": [ + 0.2, + 0, + 0, + 1, + ], + "standardAccelerate": [ + 0.3, + 0, + 1, + 1, + ], + "standardDecelerate": [ + 0, + 0, + 0, + 1, + ], + }, + "spring": { + "default": { + "effects": { + "damping": 1, + "stiffness": 1600, + }, + "spatial": { + "damping": 0.8, + "stiffness": 380, + }, + }, + "fast": { + "effects": { + "damping": 1, + "stiffness": 3800, + }, + "spatial": { + "damping": 0.6, + "stiffness": 800, + }, + }, + "slow": { + "effects": { + "damping": 1, + "stiffness": 800, + }, + "spatial": { + "damping": 0.8, + "stiffness": 200, + }, + }, + }, + }, "shapes": { "corner": { "extraExtraLarge": 48, @@ -950,6 +1046,102 @@ exports[`renders list section with subheader 1`] = ` "lineHeight": 20, }, }, + "motion": { + "duration": { + "extraLong1": 700, + "extraLong2": 800, + "extraLong3": 900, + "extraLong4": 1000, + "long1": 450, + "long2": 500, + "long3": 550, + "long4": 600, + "medium1": 250, + "medium2": 300, + "medium3": 350, + "medium4": 400, + "short1": 50, + "short2": 100, + "short3": 150, + "short4": 200, + }, + "easing": { + "emphasized": [ + 0.2, + 0, + 0, + 1, + ], + "emphasizedAccelerate": [ + 0.3, + 0, + 0.8, + 0.15, + ], + "emphasizedDecelerate": [ + 0.05, + 0.7, + 0.1, + 1, + ], + "linear": [ + 0, + 0, + 1, + 1, + ], + "standard": [ + 0.2, + 0, + 0, + 1, + ], + "standardAccelerate": [ + 0.3, + 0, + 1, + 1, + ], + "standardDecelerate": [ + 0, + 0, + 0, + 1, + ], + }, + "spring": { + "default": { + "effects": { + "damping": 1, + "stiffness": 1600, + }, + "spatial": { + "damping": 0.8, + "stiffness": 380, + }, + }, + "fast": { + "effects": { + "damping": 1, + "stiffness": 3800, + }, + "spatial": { + "damping": 0.6, + "stiffness": 800, + }, + }, + "slow": { + "effects": { + "damping": 1, + "stiffness": 800, + }, + "spatial": { + "damping": 0.8, + "stiffness": 200, + }, + }, + }, + }, "shapes": { "corner": { "extraExtraLarge": 48, @@ -1606,6 +1798,102 @@ exports[`renders list section without subheader 1`] = ` "lineHeight": 20, }, }, + "motion": { + "duration": { + "extraLong1": 700, + "extraLong2": 800, + "extraLong3": 900, + "extraLong4": 1000, + "long1": 450, + "long2": 500, + "long3": 550, + "long4": 600, + "medium1": 250, + "medium2": 300, + "medium3": 350, + "medium4": 400, + "short1": 50, + "short2": 100, + "short3": 150, + "short4": 200, + }, + "easing": { + "emphasized": [ + 0.2, + 0, + 0, + 1, + ], + "emphasizedAccelerate": [ + 0.3, + 0, + 0.8, + 0.15, + ], + "emphasizedDecelerate": [ + 0.05, + 0.7, + 0.1, + 1, + ], + "linear": [ + 0, + 0, + 1, + 1, + ], + "standard": [ + 0.2, + 0, + 0, + 1, + ], + "standardAccelerate": [ + 0.3, + 0, + 1, + 1, + ], + "standardDecelerate": [ + 0, + 0, + 0, + 1, + ], + }, + "spring": { + "default": { + "effects": { + "damping": 1, + "stiffness": 1600, + }, + "spatial": { + "damping": 0.8, + "stiffness": 380, + }, + }, + "fast": { + "effects": { + "damping": 1, + "stiffness": 3800, + }, + "spatial": { + "damping": 0.6, + "stiffness": 800, + }, + }, + "slow": { + "effects": { + "damping": 1, + "stiffness": 800, + }, + "spatial": { + "damping": 0.8, + "stiffness": 200, + }, + }, + }, + }, "shapes": { "corner": { "extraExtraLarge": 48, diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 1d04ec00ff..7b9b554ff7 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -122,20 +122,6 @@ describe('PaperProvider', () => { ); }); - it('handles overriding animation with the custom one', () => { - const { getByTestId } = render( - createProvider({ - ...LightTheme, - animation: { defaultAnimationDuration: 250 }, - }) - ); - - expect(getByTestId('provider-child-view').props.theme).toStrictEqual({ - ...LightTheme, - animation: { scale: 1, defaultAnimationDuration: 250 }, - }); - }); - it('should set AccessibilityInfo listeners, if there is no theme', async () => { mockAppearance(); mockAccessibilityInfo(); diff --git a/src/theme/schemes/base.ts b/src/theme/schemes/base.ts index 44d3a017e9..ba8ebdd15d 100644 --- a/src/theme/schemes/base.ts +++ b/src/theme/schemes/base.ts @@ -1,3 +1,4 @@ +import { expressiveMotion } from '../tokens/sys/motion'; import { defaultShapes } from '../tokens/sys/shape'; import { defaultFonts } from '../tokens/sys/typography'; import type { Theme } from '../types'; @@ -10,4 +11,5 @@ export const themeDefaults: ThemeDefaults = { }, fonts: defaultFonts, shapes: defaultShapes, + motion: expressiveMotion, }; diff --git a/src/theme/tokens/sys/motion.ts b/src/theme/tokens/sys/motion.ts new file mode 100644 index 0000000000..a7c96454f2 --- /dev/null +++ b/src/theme/tokens/sys/motion.ts @@ -0,0 +1,99 @@ +import type { + MotionConfig, + MotionDuration, + MotionEasing, + SpringConfig, +} from '../../types'; + +// Spring, easing curves and duration constants per the M3 spec: +// https://m3.material.io/styles/motion/easing-and-duration/tokens-specs + +const expressiveSpring = { + spring: { + fast: { + spatial: { stiffness: 800, damping: 0.6 }, + effects: { stiffness: 3800, damping: 1 }, + }, + default: { + spatial: { stiffness: 380, damping: 0.8 }, + effects: { stiffness: 1600, damping: 1 }, + }, + slow: { + spatial: { stiffness: 200, damping: 0.8 }, + effects: { stiffness: 800, damping: 1 }, + }, + }, +}; + +const standardSpring = { + spring: { + fast: { + spatial: { stiffness: 1400, damping: 0.9 }, + effects: { stiffness: 3800, damping: 1 }, + }, + default: { + spatial: { stiffness: 700, damping: 0.9 }, + effects: { stiffness: 1600, damping: 1 }, + }, + slow: { + spatial: { stiffness: 300, damping: 0.9 }, + effects: { stiffness: 800, damping: 1 }, + }, + }, +}; + +export const motionEasing: MotionEasing = { + emphasized: [0.2, 0, 0, 1], + emphasizedAccelerate: [0.3, 0, 0.8, 0.15], + emphasizedDecelerate: [0.05, 0.7, 0.1, 1], + standard: [0.2, 0, 0, 1], + standardAccelerate: [0.3, 0, 1, 1], + standardDecelerate: [0, 0, 0, 1], + linear: [0, 0, 1, 1], +}; + +export const motionDuration: MotionDuration = { + short1: 50, + short2: 100, + short3: 150, + short4: 200, + medium1: 250, + medium2: 300, + medium3: 350, + medium4: 400, + long1: 450, + long2: 500, + long3: 550, + long4: 600, + extraLong1: 700, + extraLong2: 800, + extraLong3: 900, + extraLong4: 1000, +}; + +export const expressiveMotion: MotionConfig = { + ...expressiveSpring, + easing: motionEasing, + duration: motionDuration, +}; + +export const standardMotion: MotionConfig = { + ...standardSpring, + easing: motionEasing, + duration: motionDuration, +}; + +/** + * Converts a `SpringConfig` (spec damping ratio 0–1) to the raw damping + * coefficient expected by `Animated.spring` and Reanimated's `withSpring`. + * + * @example + * Animated.spring(value, { + * toValue: 0.85, + * ...toRawSpring(theme.motion.spring.fast.spatial), + * useNativeDriver: true, + * }); + */ +export function toRawSpring({ stiffness, damping }: SpringConfig) { + return { stiffness, damping: damping * 2 * Math.sqrt(stiffness) }; +} diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts index e62ef4ad45..cc1ffe21ef 100644 --- a/src/theme/types/index.ts +++ b/src/theme/types/index.ts @@ -1,5 +1,6 @@ export * from './color'; export * from './elevation'; +export * from './motion'; export * from './navigation'; export * from './shape'; export * from './theme'; diff --git a/src/theme/types/motion.ts b/src/theme/types/motion.ts new file mode 100644 index 0000000000..a0e037f524 --- /dev/null +++ b/src/theme/types/motion.ts @@ -0,0 +1,47 @@ +export type SpringConfig = { + stiffness: number; + damping: number; // damping ratio 0–1; matches md.sys.motion.spring.*.*.damping +}; + +export type MotionSpring = { + fast: { spatial: SpringConfig; effects: SpringConfig }; + default: { spatial: SpringConfig; effects: SpringConfig }; + slow: { spatial: SpringConfig; effects: SpringConfig }; +}; + +export type EasingConfig = readonly [number, number, number, number]; + +export type MotionEasing = { + emphasized: EasingConfig; + emphasizedAccelerate: EasingConfig; + emphasizedDecelerate: EasingConfig; + standard: EasingConfig; + standardAccelerate: EasingConfig; + standardDecelerate: EasingConfig; + linear: EasingConfig; +}; + +export type MotionDuration = { + short1: number; + short2: number; + short3: number; + short4: number; + medium1: number; + medium2: number; + medium3: number; + medium4: number; + long1: number; + long2: number; + long3: number; + long4: number; + extraLong1: number; + extraLong2: number; + extraLong3: number; + extraLong4: number; +}; + +export type MotionConfig = { + spring: MotionSpring; + easing: MotionEasing; + duration: MotionDuration; +}; diff --git a/src/theme/types/theme.ts b/src/theme/types/theme.ts index dac2946cc9..a067fdec12 100644 --- a/src/theme/types/theme.ts +++ b/src/theme/types/theme.ts @@ -1,6 +1,7 @@ import type { $DeepPartial } from '@callstack/react-theme-provider'; import type { ThemeColors } from './color'; +import type { MotionConfig } from './motion'; import type { ThemeShapes } from './shape'; import type { Typescale } from './typography'; @@ -11,7 +12,6 @@ export type ThemeBase = { mode?: Mode; animation: { scale: number; - defaultAnimationDuration?: number; }; }; @@ -19,6 +19,7 @@ export type Theme = ThemeBase & { colors: ThemeColors; fonts: Typescale; shapes: ThemeShapes; + motion: MotionConfig; }; export type InternalTheme = Theme; From 2f8b8400517f36949aa07ad17ba7070e5576ac7c Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Thu, 14 May 2026 08:40:31 +0300 Subject: [PATCH 14/94] feat: add elevation tokens and fix Android dp spec bug (#4923) --- src/components/Appbar/Appbar.tsx | 4 +- src/components/Appbar/AppbarHeader.tsx | 4 +- src/components/Menu/Menu.tsx | 12 +--- src/components/Surface.tsx | 69 ++++++++---------- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 28 ++++---- .../Card/__snapshots__/Card.test.tsx.snap | 4 +- src/components/__tests__/Menu.test.tsx | 4 +- .../__snapshots__/AnimatedFAB.test.tsx.snap | 12 ++-- .../__snapshots__/Banner.test.tsx.snap | 52 +++++++------- .../BottomNavigation.test.tsx.snap | 44 ++++++------ .../__snapshots__/Button.test.tsx.snap | 52 +++++++------- .../__snapshots__/Chip.test.tsx.snap | 24 +++---- .../__snapshots__/DataTable.test.tsx.snap | 52 +++++++------- .../__tests__/__snapshots__/FAB.test.tsx.snap | 44 ++++++------ .../__snapshots__/IconButton.test.tsx.snap | 20 +++--- .../__snapshots__/ListItem.test.tsx.snap | 4 +- .../__snapshots__/ListSection.test.tsx.snap | 24 +++++++ .../__snapshots__/Menu.test.tsx.snap | 20 +++--- .../__snapshots__/Searchbar.test.tsx.snap | 32 ++++----- .../__snapshots__/Snackbar.test.tsx.snap | 20 +++--- .../__snapshots__/TextInput.test.tsx.snap | 8 +-- .../__snapshots__/ToggleButton.test.tsx.snap | 12 ++-- src/index.tsx | 2 - src/theme/schemes/base.ts | 2 + src/theme/shadow.tsx | 44 ------------ src/theme/tokens/sys/elevation.ts | 70 +++++++++++++++++++ src/theme/types/elevation.ts | 6 +- src/theme/types/theme.ts | 2 + 28 files changed, 355 insertions(+), 316 deletions(-) delete mode 100644 src/theme/shadow.tsx create mode 100644 src/theme/tokens/sys/elevation.ts diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index a9de112054..27948ad5ee 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -167,7 +167,7 @@ const Appbar = ({ elevation = elevated ? 2 : 0, ...restStyle } = (flattenedStyle || {}) as Exclude & { - elevation?: number; + elevation?: Elevation; backgroundColor?: ColorValue; }; @@ -234,7 +234,7 @@ const Appbar = ({ insets, restStyle, ]} - elevation={elevation as Elevation} + elevation={elevation} container {...rest} > diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index ed4ecce48d..a51b0a3e69 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -18,7 +18,7 @@ import { getAppbarBorders, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import shadow from '../../theme/shadow'; +import { shadow } from '../../theme/tokens/sys/elevation'; import type { ThemeProp } from '../../types'; export type Props = Omit< @@ -141,7 +141,7 @@ const AppbarHeader = ({ paddingHorizontal: Math.max(left, right), }, borderRadius, - shadow(elevation) as ViewStyle, + shadow(elevation, theme.colors.shadow) as ViewStyle, ]} > { @@ -679,8 +670,7 @@ const Menu = ({ styles.shadowMenuContainer, shadowMenuContainerStyle, { - backgroundColor: - md3Colors.elevation[ELEVATION_LEVELS_MAP[elevation]], + backgroundColor: md3Colors.elevation[`level${elevation}`], }, contentStyle, ]} diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index 7dcfd0a6cd..9c11790687 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -10,7 +10,12 @@ import { } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import shadow from '../theme/shadow'; +import { + androidElevationLevels, + elevationInputRange, + shadow, + shadowLayers, +} from '../theme/tokens/sys/elevation'; import type { Elevation, Theme, ThemeProp } from '../types'; import { isAnimatedValue } from '../utils/animations'; import { forwardRef } from '../utils/forwardRef'; @@ -75,54 +80,41 @@ const outerLayerStyleProperties: (keyof ViewStyle)[] = [ 'opacity', ]; -const shadowColor = '#000'; -const iOSShadowOutputRanges = [ - { - shadowOpacity: 0.15, - height: [0, 1, 2, 4, 6, 8], - shadowRadius: [0, 3, 6, 8, 10, 12], - }, - { - shadowOpacity: 0.3, - height: [0, 1, 1, 1, 2, 4], - shadowRadius: [0, 1, 2, 3, 3, 4], - }, -]; -const inputRange = [0, 1, 2, 3, 4, 5]; function getStyleForShadowLayer( elevation: SurfaceElevation, - layer: 0 | 1 + layer: 0 | 1, + shadowColor: string ): Animated.WithAnimatedValue { if (isAnimatedValue(elevation)) { return { shadowColor, shadowOpacity: elevation.interpolate({ inputRange: [0, 1], - outputRange: [0, iOSShadowOutputRanges[layer].shadowOpacity], + outputRange: [0, shadowLayers[layer].shadowOpacity], extrapolate: 'clamp', }), shadowOffset: { width: 0, height: elevation.interpolate({ - inputRange, - outputRange: iOSShadowOutputRanges[layer].height, + inputRange: elevationInputRange, + outputRange: shadowLayers[layer].height, }), }, shadowRadius: elevation.interpolate({ - inputRange, - outputRange: iOSShadowOutputRanges[layer].shadowRadius, + inputRange: elevationInputRange, + outputRange: shadowLayers[layer].shadowRadius, }), }; } return { shadowColor, - shadowOpacity: elevation ? iOSShadowOutputRanges[layer].shadowOpacity : 0, + shadowOpacity: elevation ? shadowLayers[layer].shadowOpacity : 0, shadowOffset: { width: 0, - height: iOSShadowOutputRanges[layer].height[elevation], + height: shadowLayers[layer].height[elevation], }, - shadowRadius: iOSShadowOutputRanges[layer].shadowRadius[elevation], + shadowRadius: shadowLayers[layer].shadowRadius[elevation], }; } @@ -131,6 +123,7 @@ const SurfaceIOS = forwardRef< Omit & { elevation: SurfaceElevation; backgroundColor?: string | Animated.AnimatedInterpolation; + shadowColor: string; } >( ( @@ -138,6 +131,7 @@ const SurfaceIOS = forwardRef< elevation, style, backgroundColor, + shadowColor, testID, children, mode = 'elevated', @@ -173,14 +167,14 @@ const SurfaceIOS = forwardRef< const isElevated = mode === 'elevated'; const outerLayerViewStyles = { - ...(isElevated && getStyleForShadowLayer(elevation, 0)), + ...(isElevated && getStyleForShadowLayer(elevation, 0, shadowColor)), ...outerLayerStyles, ...borderRadiusStyles, backgroundColor: bgColor, }; const innerLayerViewStyles = { - ...(isElevated && getStyleForShadowLayer(elevation, 1)), + ...(isElevated && getStyleForShadowLayer(elevation, 1, shadowColor)), ...filteredStyles, ...borderRadiusStyles, flex: @@ -191,7 +185,7 @@ const SurfaceIOS = forwardRef< }; return [outerLayerViewStyles, innerLayerViewStyles]; - }, [style, elevation, backgroundColor, mode, container]); + }, [style, elevation, backgroundColor, shadowColor, mode, container]); return ( ( const { colors } = theme as Theme; - const inputRange = [0, 1, 2, 3, 4, 5]; - const backgroundColor = (() => { if (isAnimatedValue(elevation)) { return elevation.interpolate({ - inputRange, - outputRange: inputRange.map((elevation) => { - return colors.elevation?.[`level${elevation as Elevation}`]; + inputRange: elevationInputRange, + outputRange: elevationInputRange.map((elevation) => { + return colors.elevation?.[`level${elevation}`]; }), }); } @@ -282,7 +274,9 @@ const Surface = forwardRef( testID={testID} style={[ { backgroundColor }, - elevation && isElevated ? shadow(elevation) : null, + elevation && isElevated + ? shadow(elevation, theme.colors.shadow) + : null, style, ]} > @@ -292,17 +286,15 @@ const Surface = forwardRef( } if (Platform.OS === 'android') { - const elevationLevel = [0, 3, 6, 9, 12, 15]; - const getElevationAndroid = () => { if (isAnimatedValue(elevation)) { return elevation.interpolate({ - inputRange, - outputRange: elevationLevel, + inputRange: elevationInputRange, + outputRange: androidElevationLevels, }); } - return elevationLevel[elevation]; + return androidElevationLevels[elevation]; }; const { margin, padding, transform, borderRadius } = (StyleSheet.flatten( @@ -340,6 +332,7 @@ const Surface = forwardRef( ref={ref} elevation={elevation} backgroundColor={backgroundColor} + shadowColor={theme.colors.shadow} style={style} testID={testID} mode={mode} diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index d90d5553cd..9880395ec2 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -7,7 +7,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` { "backgroundColor": "rgba(254, 247, 255, 1)", "height": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -31,7 +31,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "paddingLeft": undefined, "paddingRight": undefined, "paddingTop": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -48,7 +48,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` { "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -68,7 +68,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "borderRadius": 28, "flex": undefined, "flexDirection": "row", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -87,7 +87,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -110,7 +110,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -279,7 +279,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -302,7 +302,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -427,7 +427,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A { "backgroundColor": "rgba(254, 247, 255, 1)", "height": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -451,7 +451,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A "paddingLeft": undefined, "paddingRight": undefined, "paddingTop": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -470,7 +470,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -493,7 +493,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -721,7 +721,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -744,7 +744,7 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap index a5a8abd1ac..d1492cc475 100644 --- a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap +++ b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap @@ -7,7 +7,7 @@ exports[`Card renders an outlined card 1`] = ` { "backgroundColor": "rgba(254, 247, 255, 1)", "borderRadius": 12, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -25,7 +25,7 @@ exports[`Card renders an outlined card 1`] = ` "backgroundColor": "rgba(254, 247, 255, 1)", "borderRadius": 12, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 5737f4d313..9147ad456a 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -7,7 +7,7 @@ import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; import { Elevation } from '../../types'; import Button from '../Button/Button'; -import Menu, { ELEVATION_LEVELS_MAP } from '../Menu/Menu'; +import Menu from '../Menu/Menu'; import Portal from '../Portal/Portal'; const styles = StyleSheet.create({ @@ -88,7 +88,7 @@ it('renders menu with content styles', () => { ); expect(getByTestId('menu-surface')).toHaveStyle({ - backgroundColor: theme.colors.elevation[ELEVATION_LEVELS_MAP[elevation]], + backgroundColor: theme.colors.elevation[`level${elevation}`], }); }) ); diff --git a/src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap b/src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap index eea7087336..fad6addacc 100644 --- a/src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap @@ -9,7 +9,7 @@ exports[`renders animated fab 1`] = ` "borderRadius": 16, "opacity": 1, "position": "absolute", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -32,7 +32,7 @@ exports[`renders animated fab 1`] = ` "backgroundColor": "transparent", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -296,7 +296,7 @@ exports[`renders animated fab with label on the left 1`] = ` "borderRadius": 16, "opacity": 1, "position": "absolute", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -319,7 +319,7 @@ exports[`renders animated fab with label on the left 1`] = ` "backgroundColor": "transparent", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -585,7 +585,7 @@ exports[`renders animated fab with label on the right by default 1`] = ` "borderRadius": 16, "opacity": 1, "position": "absolute", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -608,7 +608,7 @@ exports[`renders animated fab with label on the right by default 1`] = ` "backgroundColor": "transparent", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, diff --git a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap index 6be6a1afa3..2b7d4fd813 100644 --- a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap @@ -7,7 +7,7 @@ exports[`render visible banner, with custom theme 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -24,13 +24,13 @@ exports[`render visible banner, with custom theme 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, }, "shadowOpacity": 0.3, - "shadowRadius": 1, + "shadowRadius": 2, } } testID="surface" @@ -124,7 +124,7 @@ exports[`render visible banner, with custom theme 1`] = ` "backgroundColor": "transparent", "borderRadius": 20, "margin": 4, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -146,7 +146,7 @@ exports[`render visible banner, with custom theme 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": "auto", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -283,7 +283,7 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -300,13 +300,13 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, }, "shadowOpacity": 0.3, - "shadowRadius": 1, + "shadowRadius": 2, } } testID="surface" @@ -418,7 +418,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -435,13 +435,13 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, }, "shadowOpacity": 0.3, - "shadowRadius": 1, + "shadowRadius": 2, } } testID="surface" @@ -557,7 +557,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "backgroundColor": "transparent", "borderRadius": 20, "margin": 4, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -579,7 +579,7 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": "auto", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -716,7 +716,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -733,13 +733,13 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, }, "shadowOpacity": 0.3, - "shadowRadius": 1, + "shadowRadius": 2, } } testID="surface" @@ -833,7 +833,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "backgroundColor": "transparent", "borderRadius": 20, "margin": 4, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -855,7 +855,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": "auto", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -985,7 +985,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "backgroundColor": "transparent", "borderRadius": 20, "margin": 4, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1007,7 +1007,7 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": "auto", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1144,7 +1144,7 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -1161,13 +1161,13 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` { "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, }, "shadowOpacity": 0.3, - "shadowRadius": 1, + "shadowRadius": 2, } } testID="surface" @@ -1289,7 +1289,7 @@ exports[`renders visible banner, without action buttons and without image 1`] = { "backgroundColor": "rgba(247, 242, 250, 1)", "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -1306,13 +1306,13 @@ exports[`renders visible banner, without action buttons and without image 1`] = { "backgroundColor": "rgba(247, 242, 250, 1)", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, }, "shadowOpacity": 0.3, - "shadowRadius": 1, + "shadowRadius": 2, } } testID="surface" diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index 587006fa3d..7b5d8ad45e 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -78,7 +78,7 @@ exports[`allows customizing Route's type via generics 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -97,7 +97,7 @@ exports[`allows customizing Route's type via generics 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -745,7 +745,7 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -764,7 +764,7 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1486,7 +1486,7 @@ exports[`hides labels in shifting bottom navigation 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1505,7 +1505,7 @@ exports[`hides labels in shifting bottom navigation 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2359,7 +2359,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2378,7 +2378,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -4088,7 +4088,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -4107,7 +4107,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -5562,7 +5562,7 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -5581,7 +5581,7 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -6264,7 +6264,7 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -6283,7 +6283,7 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -7258,7 +7258,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -7277,7 +7277,7 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -8359,7 +8359,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -8378,7 +8378,7 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -9307,7 +9307,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -9326,7 +9326,7 @@ exports[`renders non-shifting bottom navigation 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -10408,7 +10408,7 @@ exports[`renders shifting bottom navigation 1`] = ` "bottom": 0, "left": 0, "right": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -10427,7 +10427,7 @@ exports[`renders shifting bottom navigation 1`] = ` { "backgroundColor": "transparent", "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/__tests__/__snapshots__/Button.test.tsx.snap index 6c34864a74..992205a103 100644 --- a/src/components/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Button.test.tsx.snap @@ -7,7 +7,7 @@ exports[`renders button with an accessibility hint 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -29,7 +29,7 @@ exports[`renders button with an accessibility hint 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -160,7 +160,7 @@ exports[`renders button with an accessibility label 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -182,7 +182,7 @@ exports[`renders button with an accessibility label 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -313,7 +313,7 @@ exports[`renders button with button color 1`] = ` { "backgroundColor": "#e91e63", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -335,7 +335,7 @@ exports[`renders button with button color 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -465,7 +465,7 @@ exports[`renders button with color 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -487,7 +487,7 @@ exports[`renders button with color 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -617,7 +617,7 @@ exports[`renders button with custom testID 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -639,7 +639,7 @@ exports[`renders button with custom testID 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -769,7 +769,7 @@ exports[`renders button with icon 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -791,7 +791,7 @@ exports[`renders button with icon 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -970,7 +970,7 @@ exports[`renders button with icon in reverse order 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -992,7 +992,7 @@ exports[`renders button with icon in reverse order 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1173,7 +1173,7 @@ exports[`renders contained contained with mode 1`] = ` { "backgroundColor": "rgba(103, 80, 164, 1)", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1195,7 +1195,7 @@ exports[`renders contained contained with mode 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1326,7 +1326,7 @@ exports[`renders disabled button 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1348,7 +1348,7 @@ exports[`renders disabled button 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1478,7 +1478,7 @@ exports[`renders loading button 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1500,7 +1500,7 @@ exports[`renders loading button 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1838,7 +1838,7 @@ exports[`renders outlined button with mode 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1860,7 +1860,7 @@ exports[`renders outlined button with mode 1`] = ` "borderWidth": 1, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1991,7 +1991,7 @@ exports[`renders text button by default 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2013,7 +2013,7 @@ exports[`renders text button by default 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2143,7 +2143,7 @@ exports[`renders text button with mode 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2165,7 +2165,7 @@ exports[`renders text button with mode 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap index 4c6358aa1d..736f79b6fe 100644 --- a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap @@ -7,7 +7,7 @@ exports[`renders chip with close button 1`] = ` { "backgroundColor": "rgba(232, 222, 248, 1)", "borderRadius": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -29,7 +29,7 @@ exports[`renders chip with close button 1`] = ` "borderWidth": 1, "flex": undefined, "flexDirection": "column", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -305,7 +305,7 @@ exports[`renders chip with custom close button 1`] = ` { "backgroundColor": "rgba(232, 222, 248, 1)", "borderRadius": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -327,7 +327,7 @@ exports[`renders chip with custom close button 1`] = ` "borderWidth": 1, "flex": undefined, "flexDirection": "column", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -603,7 +603,7 @@ exports[`renders chip with icon 1`] = ` { "backgroundColor": "rgba(232, 222, 248, 1)", "borderRadius": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -625,7 +625,7 @@ exports[`renders chip with icon 1`] = ` "borderWidth": 1, "flex": undefined, "flexDirection": "column", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -808,7 +808,7 @@ exports[`renders chip with onPress 1`] = ` { "backgroundColor": "rgba(232, 222, 248, 1)", "borderRadius": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -830,7 +830,7 @@ exports[`renders chip with onPress 1`] = ` "borderWidth": 1, "flex": undefined, "flexDirection": "column", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -968,7 +968,7 @@ exports[`renders outlined disabled chip 1`] = ` { "backgroundColor": "transparent", "borderRadius": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -990,7 +990,7 @@ exports[`renders outlined disabled chip 1`] = ` "borderWidth": 1, "flex": undefined, "flexDirection": "column", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1128,7 +1128,7 @@ exports[`renders selected chip 1`] = ` { "backgroundColor": "rgba(232, 222, 248, 1)", "borderRadius": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1150,7 +1150,7 @@ exports[`renders selected chip 1`] = ` "borderWidth": 1, "flex": undefined, "flexDirection": "column", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index f6071c245c..0f997739ba 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -387,7 +387,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -410,7 +410,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -529,7 +529,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -552,7 +552,7 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -729,7 +729,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -752,7 +752,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -871,7 +871,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -894,7 +894,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1013,7 +1013,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1036,7 +1036,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1155,7 +1155,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1178,7 +1178,7 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1355,7 +1355,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1378,7 +1378,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1497,7 +1497,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1520,7 +1520,7 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1702,7 +1702,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "backgroundColor": "transparent", "borderRadius": 20, "marginRight": 16, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1724,7 +1724,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "borderWidth": 1, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1943,7 +1943,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1966,7 +1966,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2085,7 +2085,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2108,7 +2108,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2227,7 +2227,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2250,7 +2250,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2369,7 +2369,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2392,7 +2392,7 @@ exports[`DataTable.Pagination renders data table pagination with options select "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap index 7dbef02469..386ce68373 100644 --- a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap @@ -8,7 +8,7 @@ exports[`renders FAB with custom size prop 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 25, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -32,7 +32,7 @@ exports[`renders FAB with custom size prop 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 25, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -182,7 +182,7 @@ exports[`renders custom color for the icon and label of the FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -206,7 +206,7 @@ exports[`renders custom color for the icon and label of the FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -356,7 +356,7 @@ exports[`renders default FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -380,7 +380,7 @@ exports[`renders default FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -530,7 +530,7 @@ exports[`renders extended FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -554,7 +554,7 @@ exports[`renders extended FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -745,7 +745,7 @@ exports[`renders extended FAB with custom size prop 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 25, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -769,7 +769,7 @@ exports[`renders extended FAB with custom size prop 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 25, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -959,7 +959,7 @@ exports[`renders large FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 28, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -983,7 +983,7 @@ exports[`renders large FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 28, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -1133,7 +1133,7 @@ exports[`renders loading FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -1157,7 +1157,7 @@ exports[`renders loading FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -1438,7 +1438,7 @@ exports[`renders loading FAB with custom size prop 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 25, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -1462,7 +1462,7 @@ exports[`renders loading FAB with custom size prop 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 25, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -1743,7 +1743,7 @@ exports[`renders not visible FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -1767,7 +1767,7 @@ exports[`renders not visible FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -1917,7 +1917,7 @@ exports[`renders small FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 12, "opacity": 1, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -1941,7 +1941,7 @@ exports[`renders small FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 12, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -2091,7 +2091,7 @@ exports[`renders visible FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 4, "width": 0, @@ -2115,7 +2115,7 @@ exports[`renders visible FAB 1`] = ` "backgroundColor": "rgba(234, 221, 255, 1)", "borderRadius": 16, "flex": undefined, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, diff --git a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap index 2836d107fd..d80a56d27c 100644 --- a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap @@ -9,7 +9,7 @@ exports[`renders disabled icon button 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -32,7 +32,7 @@ exports[`renders disabled icon button 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -158,7 +158,7 @@ exports[`renders icon button by default 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -181,7 +181,7 @@ exports[`renders icon button by default 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -302,7 +302,7 @@ exports[`renders icon button with color 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -325,7 +325,7 @@ exports[`renders icon button with color 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -446,7 +446,7 @@ exports[`renders icon button with size 1`] = ` "borderRadius": 23, "height": 46, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -469,7 +469,7 @@ exports[`renders icon button with size 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -590,7 +590,7 @@ exports[`renders icon change animated 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -613,7 +613,7 @@ exports[`renders icon change animated 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap index d194b59877..f0dae5994d 100644 --- a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap @@ -123,7 +123,7 @@ exports[`renders list item with custom description 1`] = ` { "backgroundColor": "rgba(232, 222, 248, 1)", "borderRadius": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -145,7 +145,7 @@ exports[`renders list item with custom description 1`] = ` "borderWidth": 1, "flex": undefined, "flexDirection": "column", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap index 957f5f71f4..7e7dcc158c 100644 --- a/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListSection.test.tsx.snap @@ -75,6 +75,14 @@ exports[`renders list section with custom title style 1`] = ` "tertiaryFixedDim": "rgba(239, 184, 200, 1)", }, "dark": false, + "elevation": { + "level0": 0, + "level1": 1, + "level2": 2, + "level3": 3, + "level4": 4, + "level5": 5, + }, "fonts": { "bodyLarge": { "fontFamily": "System", @@ -829,6 +837,14 @@ exports[`renders list section with subheader 1`] = ` "tertiaryFixedDim": "rgba(239, 184, 200, 1)", }, "dark": false, + "elevation": { + "level0": 0, + "level1": 1, + "level2": 2, + "level3": 3, + "level4": 4, + "level5": 5, + }, "fonts": { "bodyLarge": { "fontFamily": "System", @@ -1581,6 +1597,14 @@ exports[`renders list section without subheader 1`] = ` "tertiaryFixedDim": "rgba(239, 184, 200, 1)", }, "dark": false, + "elevation": { + "level0": 0, + "level1": 1, + "level2": 2, + "level3": 3, + "level4": 4, + "level5": 5, + }, "fonts": { "bodyLarge": { "fontFamily": "System", diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index 9d426f228c..ee711c93f8 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -20,7 +20,7 @@ exports[`renders menu with content styles 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -42,7 +42,7 @@ exports[`renders menu with content styles 1`] = ` "borderWidth": 1, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -267,7 +267,7 @@ exports[`renders menu with content styles 1`] = ` "borderTopLeftRadius": 0, "borderTopRightRadius": 0, "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 2, "width": 0, @@ -297,7 +297,7 @@ exports[`renders menu with content styles 1`] = ` "borderTopRightRadius": 0, "flex": undefined, "paddingVertical": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -583,7 +583,7 @@ exports[`renders not visible menu 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -605,7 +605,7 @@ exports[`renders not visible menu 1`] = ` "borderWidth": 1, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -751,7 +751,7 @@ exports[`renders visible menu 1`] = ` { "backgroundColor": "transparent", "borderRadius": 20, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -773,7 +773,7 @@ exports[`renders visible menu 1`] = ` "borderWidth": 1, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -996,7 +996,7 @@ exports[`renders visible menu 1`] = ` "backgroundColor": "rgba(243, 237, 247, 1)", "borderRadius": 4, "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 2, "width": 0, @@ -1024,7 +1024,7 @@ exports[`renders visible menu 1`] = ` "borderRadius": 4, "flex": undefined, "paddingVertical": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap index 5d247103ff..bbe1779a66 100644 --- a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap @@ -7,7 +7,7 @@ exports[`activity indicator snapshot test 1`] = ` { "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -27,7 +27,7 @@ exports[`activity indicator snapshot test 1`] = ` "borderRadius": 28, "flex": undefined, "flexDirection": "row", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -46,7 +46,7 @@ exports[`activity indicator snapshot test 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -69,7 +69,7 @@ exports[`activity indicator snapshot test 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -425,7 +425,7 @@ exports[`renders with placeholder 1`] = ` { "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -445,7 +445,7 @@ exports[`renders with placeholder 1`] = ` "borderRadius": 28, "flex": undefined, "flexDirection": "row", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -464,7 +464,7 @@ exports[`renders with placeholder 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -487,7 +487,7 @@ exports[`renders with placeholder 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -656,7 +656,7 @@ exports[`renders with placeholder 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -679,7 +679,7 @@ exports[`renders with placeholder 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -802,7 +802,7 @@ exports[`renders with text 1`] = ` { "backgroundColor": "rgba(236, 230, 240, 1)", "borderRadius": 28, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -822,7 +822,7 @@ exports[`renders with text 1`] = ` "borderRadius": 28, "flex": undefined, "flexDirection": "row", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -841,7 +841,7 @@ exports[`renders with text 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -864,7 +864,7 @@ exports[`renders with text 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1029,7 +1029,7 @@ exports[`renders with text 1`] = ` "borderRadius": 20, "height": 40, "margin": 6, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -1052,7 +1052,7 @@ exports[`renders with text 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap index ce2a9c8637..bc4642cc3f 100644 --- a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap @@ -28,7 +28,7 @@ exports[`renders snackbar with Text as a child 1`] = ` "borderRadius": 4, "margin": 8, "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 2, "width": 0, @@ -56,7 +56,7 @@ exports[`renders snackbar with Text as a child 1`] = ` "flexDirection": "row", "justifyContent": "space-between", "minHeight": 48, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -116,7 +116,7 @@ exports[`renders snackbar with View & Text as a child 1`] = ` "borderRadius": 4, "margin": 8, "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 2, "width": 0, @@ -144,7 +144,7 @@ exports[`renders snackbar with View & Text as a child 1`] = ` "flexDirection": "row", "justifyContent": "space-between", "minHeight": 48, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -230,7 +230,7 @@ exports[`renders snackbar with action button 1`] = ` "borderRadius": 4, "margin": 8, "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 2, "width": 0, @@ -258,7 +258,7 @@ exports[`renders snackbar with action button 1`] = ` "flexDirection": "row", "justifyContent": "space-between", "minHeight": 48, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, @@ -326,7 +326,7 @@ exports[`renders snackbar with action button 1`] = ` "borderRadius": 20, "marginLeft": 4, "marginRight": 8, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -348,7 +348,7 @@ exports[`renders snackbar with action button 1`] = ` "borderWidth": 0, "flex": undefined, "minWidth": 64, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -501,7 +501,7 @@ exports[`renders snackbar with content 1`] = ` "borderRadius": 4, "margin": 8, "opacity": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 2, "width": 0, @@ -529,7 +529,7 @@ exports[`renders snackbar with content 1`] = ` "flexDirection": "row", "justifyContent": "space-between", "minHeight": 48, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 1, "width": 0, diff --git a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap index dbe10b45cc..facd344c1e 100644 --- a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap @@ -2039,7 +2039,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "borderRadius": 20, "height": 40, "margin": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2062,7 +2062,7 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2435,7 +2435,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "borderRadius": 20, "height": 40, "margin": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -2458,7 +2458,7 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap index a8feb453e3..28e6269c7b 100644 --- a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap @@ -9,7 +9,7 @@ exports[`renders disabled toggle button 1`] = ` "borderRadius": 4, "height": 42, "margin": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -32,7 +32,7 @@ exports[`renders disabled toggle button 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -156,7 +156,7 @@ exports[`renders toggle button 1`] = ` "borderRadius": 4, "height": 42, "margin": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -179,7 +179,7 @@ exports[`renders toggle button 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -298,7 +298,7 @@ exports[`renders unchecked toggle button 1`] = ` "borderRadius": 4, "height": 42, "margin": 0, - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, @@ -321,7 +321,7 @@ exports[`renders unchecked toggle button 1`] = ` "elevation": 0, "flex": 1, "overflow": "hidden", - "shadowColor": "#000", + "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { "height": 0, "width": 0, diff --git a/src/index.tsx b/src/index.tsx index ce2d6f98dc..1bb4cbdf9e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -14,8 +14,6 @@ export * from './theme/schemes'; export { default as Provider } from './core/PaperProvider'; export { default as PaperProvider } from './core/PaperProvider'; - -export { default as shadow } from './theme/shadow'; export { default as configureFonts } from './theme/fonts'; import * as Avatar from './components/Avatar/Avatar'; diff --git a/src/theme/schemes/base.ts b/src/theme/schemes/base.ts index ba8ebdd15d..ebe5a6b0ce 100644 --- a/src/theme/schemes/base.ts +++ b/src/theme/schemes/base.ts @@ -1,3 +1,4 @@ +import { defaultElevation } from '../tokens/sys/elevation'; import { expressiveMotion } from '../tokens/sys/motion'; import { defaultShapes } from '../tokens/sys/shape'; import { defaultFonts } from '../tokens/sys/typography'; @@ -12,4 +13,5 @@ export const themeDefaults: ThemeDefaults = { fonts: defaultFonts, shapes: defaultShapes, motion: expressiveMotion, + elevation: defaultElevation, }; diff --git a/src/theme/shadow.tsx b/src/theme/shadow.tsx deleted file mode 100644 index 58070cede8..0000000000 --- a/src/theme/shadow.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Animated } from 'react-native'; - -import { Palette } from './tokens'; - -const SHADOW_OPACITY = 0.3; -const SHADOW_COLOR = Palette.primary0; - -export default function shadow(elevation: number | Animated.Value = 0) { - const inputRange = [0, 1, 2, 3, 4, 5]; - const shadowHeight = [0, 1, 2, 4, 6, 8]; - const shadowRadius = [0, 3, 6, 8, 10, 12]; - - if (elevation instanceof Animated.Value) { - return { - shadowColor: SHADOW_COLOR, - shadowOffset: { - width: new Animated.Value(0), - height: elevation.interpolate({ - inputRange, - outputRange: shadowHeight, - }), - }, - shadowOpacity: elevation.interpolate({ - inputRange: [0, 1], - outputRange: [0, SHADOW_OPACITY], - extrapolate: 'clamp', - }), - shadowRadius: elevation.interpolate({ - inputRange, - outputRange: shadowRadius, - }), - }; - } else { - return { - shadowColor: SHADOW_COLOR, - shadowOpacity: elevation ? SHADOW_OPACITY : 0, - shadowOffset: { - width: 0, - height: shadowHeight[elevation], - }, - shadowRadius: shadowRadius[elevation], - }; - } -} diff --git a/src/theme/tokens/sys/elevation.ts b/src/theme/tokens/sys/elevation.ts new file mode 100644 index 0000000000..82887d4d2d --- /dev/null +++ b/src/theme/tokens/sys/elevation.ts @@ -0,0 +1,70 @@ +// M3 elevation tokens and shadow builder per spec: +// https://m3.material.io/styles/elevation/tokens + +import { Animated } from 'react-native'; + +import { isAnimatedValue } from '../../../utils/animations'; +import type { Elevation, ThemeElevation } from '../../types'; + +export const defaultElevation: ThemeElevation = { + level0: 0, + level1: 1, + level2: 2, + level3: 3, + level4: 4, + level5: 5, +}; + +export const elevationInputRange: Elevation[] = Object.values(defaultElevation); + +export const androidElevationLevels = [0, 1, 3, 6, 8, 12]; + +export const shadowLayers = [ + { + shadowOpacity: 0.15, + height: [0, 1, 2, 4, 6, 8], + shadowRadius: [0, 3, 6, 8, 10, 12], + }, + { + shadowOpacity: 0.3, + height: [0, 1, 1, 1, 2, 4], + shadowRadius: [0, 2, 2, 3, 3, 4], + }, +]; + +export function shadow( + elevation: number | Animated.Value = 0, + shadowColor: string +) { + if (isAnimatedValue(elevation)) { + return { + shadowColor, + shadowOffset: { + width: new Animated.Value(0), + height: elevation.interpolate({ + inputRange: elevationInputRange, + outputRange: shadowLayers[0].height, + }), + }, + shadowOpacity: elevation.interpolate({ + inputRange: [0, 1], + outputRange: [0, 0.3], + extrapolate: 'clamp', + }), + shadowRadius: elevation.interpolate({ + inputRange: elevationInputRange, + outputRange: shadowLayers[0].shadowRadius, + }), + }; + } + + return { + shadowColor, + shadowOpacity: elevation ? 0.3 : 0, + shadowOffset: { + width: 0, + height: shadowLayers[0].height[elevation], + }, + shadowRadius: shadowLayers[0].shadowRadius[elevation], + }; +} diff --git a/src/theme/types/elevation.ts b/src/theme/types/elevation.ts index 839112f58c..8f531e0806 100644 --- a/src/theme/types/elevation.ts +++ b/src/theme/types/elevation.ts @@ -1,3 +1,7 @@ export type Elevation = 0 | 1 | 2 | 3 | 4 | 5; -export type ElevationColors = Record<`level${Elevation}`, string>; +export type ElevationLevel = `level${Elevation}`; + +export type ElevationColors = Record; + +export type ThemeElevation = Record; diff --git a/src/theme/types/theme.ts b/src/theme/types/theme.ts index a067fdec12..8e1e1cd770 100644 --- a/src/theme/types/theme.ts +++ b/src/theme/types/theme.ts @@ -1,6 +1,7 @@ import type { $DeepPartial } from '@callstack/react-theme-provider'; import type { ThemeColors } from './color'; +import type { ThemeElevation } from './elevation'; import type { MotionConfig } from './motion'; import type { ThemeShapes } from './shape'; import type { Typescale } from './typography'; @@ -20,6 +21,7 @@ export type Theme = ThemeBase & { fonts: Typescale; shapes: ThemeShapes; motion: MotionConfig; + elevation: ThemeElevation; }; export type InternalTheme = Theme; From 9ded0ac1490ce17c3cbc0ba151ab027012250357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lul?= <56554355+wonderlul@users.noreply.github.com> Date: Thu, 14 May 2026 12:22:35 +0200 Subject: [PATCH 15/94] feat: react-native-reanimated as peer dependency (#4912) * feat: reanimated as peer dependency --- .eslintignore | 2 +- babel.config.js | 1 + ...ting-started.md => 01-getting-started.mdx} | 48 ++++++-- docs/docs/guides/03-icons.mdx | 2 +- example/package.json | 4 +- jest/jestSetupAfterEnv.js | 3 + testSetup.js => jest/testSetup.js | 6 +- package.json | 9 +- yarn.lock | 106 ++++++++++-------- 9 files changed, 117 insertions(+), 64 deletions(-) rename docs/docs/guides/{01-getting-started.md => 01-getting-started.mdx} (73%) create mode 100644 jest/jestSetupAfterEnv.js rename testSetup.js => jest/testSetup.js (96%) diff --git a/.eslintignore b/.eslintignore index 18c50df521..5090b2f9a5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,7 +5,7 @@ docs/build/ docs/public/ docs/static/ web-build/ -testSetup.js +jest/testSetup.js __fixtures__/ # generated by bob diff --git a/babel.config.js b/babel.config.js index f7b3da3b33..8ba8eb658c 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,4 @@ module.exports = { presets: ['module:@react-native/babel-preset'], + plugins: ['react-native-worklets/plugin'], }; diff --git a/docs/docs/guides/01-getting-started.md b/docs/docs/guides/01-getting-started.mdx similarity index 73% rename from docs/docs/guides/01-getting-started.md rename to docs/docs/guides/01-getting-started.mdx index 19fc3c2dea..898bf8da18 100644 --- a/docs/docs/guides/01-getting-started.md +++ b/docs/docs/guides/01-getting-started.mdx @@ -2,29 +2,54 @@ title: Getting Started --- +import TabItem from '@theme/TabItem'; +import Tabs from '@theme/Tabs'; + # Getting Started ## Installation -- Open a Terminal in your project's folder and run: +Open a Terminal in your project's folder and install the library: ```bash npm2yarn npm install react-native-paper ``` -- From `v5` there is a need to install [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) for handling safe area. +React Native Paper uses [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) for handling safe area, [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/) and [react-native-worklets](https://docs.swmansion.com/react-native-worklets/) for animations. -```bash npm2yarn -npm install react-native-safe-area-context + + + +```bash +npx expo install react-native-safe-area-context react-native-reanimated react-native-worklets ``` -Additionaly for `iOS` platform there is a requirement to link the native parts of the library: +Minimum requirements: -```bash -npx pod-install +- `react-native-reanimated` **≥ 4.3.0** +- `react-native-worklets` **≥ 0.8.1** +- `react-native-safe-area-context` — version compatible with your React Native toolchain. + +Follow the [React Native Reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/). + +If you use Expo, you don't need to install vector icons - those are the part of the expo package. However, if you have a `babel.config.js` or `.babelrc` file, make sure that it includes `babel-preset-expo`. + + + + +```bash npm2yarn +npm install react-native-safe-area-context react-native-reanimated react-native-worklets ``` -- If you're on a vanilla React Native project, you also need to install and link [@react-native-vector-icons/material-design-icons](https://github.com/oblador/react-native-vector-icons). +Minimum requirements: + +- `react-native-reanimated` **≥ 4.3.0** +- `react-native-worklets` **≥ 0.8.1** +- `react-native-safe-area-context` — version compatible with your React Native toolchain. + +Follow the [React Native Reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/). + +If you're on a vanilla React Native project, you also need to install and link [@react-native-vector-icons/material-design-icons](https://github.com/oblador/react-native-vector-icons). Specifically `MaterialDesignIcons` icon pack needs to be included in the project, because some components use those internally (e.g. `AppBar.BackAction` on Android). @@ -36,11 +61,12 @@ npm install @react-native-vector-icons/material-design-icons The `react-native-vector-icons` library requires some additional setup steps for each platform. To ensure proper use of icon fonts, please follow their [installation guide](https://github.com/oblador/react-native-vector-icons?tab=readme-ov-file#setup). ::: -If you use Expo, you don't need to install vector icons - those are the part of the expo package. However, if you have a `babel.config.js` or `.babelrc` file, make sure that it includes `babel-preset-expo`. - :::info If you don't want to install vector icons, you can use [babel-plugin-optional-require](https://github.com/satya164/babel-plugin-optional-require) to opt-out. -:::note +::: + + + ### Bundle size optimization diff --git a/docs/docs/guides/03-icons.mdx b/docs/docs/guides/03-icons.mdx index 015a1bee1b..c773ce7a56 100644 --- a/docs/docs/guides/03-icons.mdx +++ b/docs/docs/guides/03-icons.mdx @@ -10,7 +10,7 @@ import IconsList from '@site/src/components/IconsList.tsx'; ## Configuring icons -Many of the components require the [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons) library to render correctly. If you're using Expo, you don't need to do anything extra, but if it's a vanilla React Native project, you need to link the library as described in the [getting started guide](./01-getting-started.md). +Many of the components require the [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons) library to render correctly. If you're using Expo, you don't need to do anything extra, but if it's a vanilla React Native project, you need to link the library as described in the [getting started guide](./01-getting-started.mdx). :::note If you opted out of vector icons support using [babel-plugin-optional-require](https://github.com/satya164/babel-plugin-optional-require), you won't be able to use icon names for the icon prop. Some components may not look correct without vector icons and might need extra configuration. diff --git a/example/package.json b/example/package.json index 3ae36fb1ec..6a412f008c 100644 --- a/example/package.json +++ b/example/package.json @@ -36,11 +36,11 @@ "react-native": "0.81.4", "react-native-gesture-handler": "~2.28.0", "react-native-monorepo-config": "^0.1.6", - "react-native-reanimated": "~4.1.1", + "react-native-reanimated": "^4.3.0", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", "react-native-web": "^0.21.0", - "react-native-worklets": "0.5.1", + "react-native-worklets": "^0.8.1", "typeface-roboto": "^1.1.13" }, "devDependencies": { diff --git a/jest/jestSetupAfterEnv.js b/jest/jestSetupAfterEnv.js new file mode 100644 index 0000000000..ee9e70fc5f --- /dev/null +++ b/jest/jestSetupAfterEnv.js @@ -0,0 +1,3 @@ +import { setUpTests } from 'react-native-reanimated'; + +setUpTests(); diff --git a/testSetup.js b/jest/testSetup.js similarity index 96% rename from testSetup.js rename to jest/testSetup.js index 43346ff860..5088ab5585 100644 --- a/testSetup.js +++ b/jest/testSetup.js @@ -4,6 +4,10 @@ jest.useFakeTimers(); jest.mock('react-native-safe-area-context', () => mockSafeAreaContext); +jest.mock('react-native-worklets', () => + require('react-native-worklets/lib/module/mock') +); + jest.mock('@react-native-vector-icons/material-design-icons', () => { const React = require('react'); const { Text } = require('react-native'); @@ -128,4 +132,4 @@ jest.mock('react-native/Libraries/Utilities/BackHandler', () => { }, }, }; -}); \ No newline at end of file +}); diff --git a/package.json b/package.json index fbf4482041..1f4c0f784c 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,9 @@ "react-dom": "18.3.1", "react-native": "0.82.1", "react-native-builder-bob": "^0.21.3", + "react-native-reanimated": "^4.3.0", "react-native-safe-area-context": "5.5.2", + "react-native-worklets": "^0.8.1", "react-test-renderer": "19.1.1", "release-it": "^13.4.0", "rimraf": "^3.0.2", @@ -105,7 +107,9 @@ "peerDependencies": { "react": "*", "react-native": "*", - "react-native-safe-area-context": "*" + "react-native-reanimated": ">=4.3.0", + "react-native-safe-area-context": "*", + "react-native-worklets": ">=0.8.1" }, "husky": { "hooks": { @@ -116,9 +120,10 @@ "jest": { "preset": "react-native", "setupFiles": [ - "/testSetup.js" + "/jest/testSetup.js" ], "setupFilesAfterEnv": [ + "/jest/jestSetupAfterEnv.js", "@testing-library/jest-native/extend-expect" ], "cacheDirectory": "./cache/jest", diff --git a/yarn.lock b/yarn.lock index 9696c756fb..5ae8c0517a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1124,7 +1124,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.0.0-0, @babel/plugin-transform-arrow-functions@npm:^7.24.7, @babel/plugin-transform-arrow-functions@npm:^7.27.1": +"@babel/plugin-transform-arrow-functions@npm:^7.24.7, @babel/plugin-transform-arrow-functions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-arrow-functions@npm:7.27.1" dependencies: @@ -1183,7 +1183,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-class-properties@npm:^7.0.0-0, @babel/plugin-transform-class-properties@npm:^7.25.4, @babel/plugin-transform-class-properties@npm:^7.27.1": +"@babel/plugin-transform-class-properties@npm:^7.25.4, @babel/plugin-transform-class-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-class-properties@npm:7.27.1" dependencies: @@ -1207,7 +1207,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.0.0-0, @babel/plugin-transform-classes@npm:^7.25.4, @babel/plugin-transform-classes@npm:^7.28.4": +"@babel/plugin-transform-classes@npm:^7.25.4, @babel/plugin-transform-classes@npm:^7.28.4": version: 7.28.4 resolution: "@babel/plugin-transform-classes@npm:7.28.4" dependencies: @@ -1481,7 +1481,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.0.0-0, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.27.1": +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.27.1" dependencies: @@ -1541,7 +1541,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.0.0-0, @babel/plugin-transform-optional-chaining@npm:^7.24.8, @babel/plugin-transform-optional-chaining@npm:^7.27.1, @babel/plugin-transform-optional-chaining@npm:^7.28.5": +"@babel/plugin-transform-optional-chaining@npm:^7.24.8, @babel/plugin-transform-optional-chaining@npm:^7.27.1, @babel/plugin-transform-optional-chaining@npm:^7.28.5": version: 7.28.5 resolution: "@babel/plugin-transform-optional-chaining@npm:7.28.5" dependencies: @@ -1732,7 +1732,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.0.0-0, @babel/plugin-transform-shorthand-properties@npm:^7.24.7, @babel/plugin-transform-shorthand-properties@npm:^7.27.1": +"@babel/plugin-transform-shorthand-properties@npm:^7.24.7, @babel/plugin-transform-shorthand-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.27.1" dependencies: @@ -1766,7 +1766,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:>=7, @babel/plugin-transform-template-literals@npm:^7.0.0-0, @babel/plugin-transform-template-literals@npm:^7.27.1": +"@babel/plugin-transform-template-literals@npm:>=7, @babel/plugin-transform-template-literals@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-template-literals@npm:7.27.1" dependencies: @@ -1826,7 +1826,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.0.0-0, @babel/plugin-transform-unicode-regex@npm:^7.24.7, @babel/plugin-transform-unicode-regex@npm:^7.27.1": +"@babel/plugin-transform-unicode-regex@npm:^7.24.7, @babel/plugin-transform-unicode-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-unicode-regex@npm:7.27.1" dependencies: @@ -1972,7 +1972,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.12.1, @babel/preset-typescript@npm:^7.16.7, @babel/preset-typescript@npm:^7.17.12, @babel/preset-typescript@npm:^7.18.6, @babel/preset-typescript@npm:^7.23.0": +"@babel/preset-typescript@npm:^7.12.1, @babel/preset-typescript@npm:^7.17.12, @babel/preset-typescript@npm:^7.18.6, @babel/preset-typescript@npm:^7.23.0, @babel/preset-typescript@npm:^7.27.1": version: 7.28.5 resolution: "@babel/preset-typescript@npm:7.28.5" dependencies: @@ -20025,6 +20025,16 @@ __metadata: languageName: node linkType: hard +"react-native-is-edge-to-edge@npm:^1.3.1": + version: 1.3.1 + resolution: "react-native-is-edge-to-edge@npm:1.3.1" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/28cebd5f1f3632864ff5e342278721d1e5e38627ae73859a8814012116ef15c629fee7137a6c9c97bb05d94bbe639b0b47e69b36fc2735ab53ed31570140663f + languageName: node + linkType: hard + "react-native-monorepo-config@npm:^0.1.6": version: 0.1.10 resolution: "react-native-monorepo-config@npm:0.1.10" @@ -20064,11 +20074,11 @@ __metadata: react-native: "npm:0.81.4" react-native-gesture-handler: "npm:~2.28.0" react-native-monorepo-config: "npm:^0.1.6" - react-native-reanimated: "npm:~4.1.1" + react-native-reanimated: "npm:^4.3.0" react-native-safe-area-context: "npm:~5.6.0" react-native-screens: "npm:~4.16.0" react-native-web: "npm:^0.21.0" - react-native-worklets: "npm:0.5.1" + react-native-worklets: "npm:^0.8.1" typeface-roboto: "npm:^1.1.13" url-loader: "npm:^4.1.1" languageName: unknown @@ -20120,7 +20130,9 @@ __metadata: react-dom: "npm:18.3.1" react-native: "npm:0.82.1" react-native-builder-bob: "npm:^0.21.3" + react-native-reanimated: "npm:^4.3.0" react-native-safe-area-context: "npm:5.5.2" + react-native-worklets: "npm:^0.8.1" react-test-renderer: "npm:19.1.1" release-it: "npm:^13.4.0" rimraf: "npm:^3.0.2" @@ -20129,22 +20141,23 @@ __metadata: peerDependencies: react: "*" react-native: "*" + react-native-reanimated: ">=4.3.0" react-native-safe-area-context: "*" + react-native-worklets: ">=0.8.1" languageName: unknown linkType: soft -"react-native-reanimated@npm:~4.1.1": - version: 4.1.6 - resolution: "react-native-reanimated@npm:4.1.6" +"react-native-reanimated@npm:^4.3.0": + version: 4.3.0 + resolution: "react-native-reanimated@npm:4.3.0" dependencies: - react-native-is-edge-to-edge: "npm:^1.2.1" - semver: "npm:7.7.2" + react-native-is-edge-to-edge: "npm:^1.3.1" + semver: "npm:^7.7.3" peerDependencies: - "@babel/core": ^7.0.0-0 react: "*" - react-native: "*" - react-native-worklets: ">=0.5.0" - checksum: 10c0/924b3a3fc0e6b47b97491122689bc00d59c5c2abf90ba05dd811f1c6d59efb8fb83135e4fa4463241ff937450025b7b335af54ab5f35c15197efaaef90235e91 + react-native: 0.81 - 0.85 + react-native-worklets: 0.8.x + checksum: 10c0/e882660f8876b5571b4cb6fe99cbf123f7329e1282376cd92fb4b45991a765aa364b295781acea2658bee1b0196ea122b624be63b3a906c06a72c2a67ab56486 languageName: node linkType: hard @@ -20229,26 +20242,27 @@ __metadata: languageName: node linkType: hard -"react-native-worklets@npm:0.5.1": - version: 0.5.1 - resolution: "react-native-worklets@npm:0.5.1" - dependencies: - "@babel/plugin-transform-arrow-functions": "npm:^7.0.0-0" - "@babel/plugin-transform-class-properties": "npm:^7.0.0-0" - "@babel/plugin-transform-classes": "npm:^7.0.0-0" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.0.0-0" - "@babel/plugin-transform-optional-chaining": "npm:^7.0.0-0" - "@babel/plugin-transform-shorthand-properties": "npm:^7.0.0-0" - "@babel/plugin-transform-template-literals": "npm:^7.0.0-0" - "@babel/plugin-transform-unicode-regex": "npm:^7.0.0-0" - "@babel/preset-typescript": "npm:^7.16.7" +"react-native-worklets@npm:^0.8.1": + version: 0.8.1 + resolution: "react-native-worklets@npm:0.8.1" + dependencies: + "@babel/plugin-transform-arrow-functions": "npm:^7.27.1" + "@babel/plugin-transform-class-properties": "npm:^7.27.1" + "@babel/plugin-transform-classes": "npm:^7.28.4" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.27.1" + "@babel/plugin-transform-optional-chaining": "npm:^7.27.1" + "@babel/plugin-transform-shorthand-properties": "npm:^7.27.1" + "@babel/plugin-transform-template-literals": "npm:^7.27.1" + "@babel/plugin-transform-unicode-regex": "npm:^7.27.1" + "@babel/preset-typescript": "npm:^7.27.1" convert-source-map: "npm:^2.0.0" - semver: "npm:7.7.2" + semver: "npm:^7.7.3" peerDependencies: - "@babel/core": ^7.0.0-0 + "@babel/core": "*" + "@react-native/metro-config": "*" react: "*" - react-native: "*" - checksum: 10c0/9eb9e6dea9abaf889400a6618355ef59af3075f5004a4bec9e4cba6dcfd13d8b63de0d4b29d75c00a3dcf5ad422e1bdb71636c75b1a2ad1c43d8b512f198bdab + react-native: 0.81 - 0.85 + checksum: 10c0/a82edbd65b09a31d973497dac899adcd618677b56f7e5c460fd9f3b1b4ef1547b7bfd2edaa644d1ebf866bf70658bdbd1314c9d0ab2856e98c6975d0e97fd449 languageName: node linkType: hard @@ -21674,15 +21688,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.7.2": - version: 7.7.2 - resolution: "semver@npm:7.7.2" - bin: - semver: bin/semver.js - checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea - languageName: node - linkType: hard - "semver@npm:^6.0.0, semver@npm:^6.2.0, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" @@ -21701,6 +21706,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.7.3": + version: 7.7.4 + resolution: "semver@npm:7.7.4" + bin: + semver: bin/semver.js + checksum: 10c0/5215ad0234e2845d4ea5bb9d836d42b03499546ddafb12075566899fc617f68794bb6f146076b6881d755de17d6c6cc73372555879ec7dce2c2feee947866ad2 + languageName: node + linkType: hard + "semver@npm:~7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" From e7dff9fae8c7141c1f9608e760a434aa088b34fd Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Thu, 14 May 2026 15:53:01 +0300 Subject: [PATCH 16/94] refactor: add reduceMotion prop and useReduceMotion hook (#4924) --- src/core/PaperProvider.tsx | 79 ++++------------ src/core/__tests__/PaperProvider.test.tsx | 93 +++++++++++++++---- src/core/useResolvedReduceMotion.ts | 45 +++++++++ src/core/useSystemColorScheme.ts | 28 ++++++ .../accessibility/ReduceMotionContext.tsx | 15 +++ src/theme/provider.tsx | 2 +- 6 files changed, 183 insertions(+), 79 deletions(-) create mode 100644 src/core/useResolvedReduceMotion.ts create mode 100644 src/core/useSystemColorScheme.ts create mode 100644 src/theme/accessibility/ReduceMotionContext.tsx diff --git a/src/core/PaperProvider.tsx b/src/core/PaperProvider.tsx index cdb2d5d8a9..56114744f2 100644 --- a/src/core/PaperProvider.tsx +++ b/src/core/PaperProvider.tsx @@ -1,92 +1,47 @@ import * as React from 'react'; -import { - AccessibilityInfo, - Appearance, - ColorSchemeName, - NativeEventSubscription, -} from 'react-native'; import { getDefaultDirection, LocaleProvider, type Direction } from './locale'; import SafeAreaProviderCompat from './SafeAreaProviderCompat'; import { Provider as SettingsProvider, Settings } from './settings'; import { defaultThemes, ThemeProvider } from './theming'; +import { + useResolvedReduceMotion, + type ReduceMotionPreference, +} from './useResolvedReduceMotion'; +import { useSystemColorScheme } from './useSystemColorScheme'; import MaterialCommunityIcon from '../components/MaterialCommunityIcon'; import PortalHost from '../components/Portal/PortalHost'; +import { ReduceMotionContext } from '../theme/accessibility/ReduceMotionContext'; import type { ThemeProp } from '../types'; -import { addEventListener } from '../utils/addEventListener'; export type Props = { children: React.ReactNode; theme?: ThemeProp; settings?: Settings; direction?: Direction; + reduceMotion?: ReduceMotionPreference; }; const PaperProvider = (props: Props) => { - const colorSchemeName = - (!props.theme && Appearance?.getColorScheme()) || 'light'; - - const [reduceMotionEnabled, setReduceMotionEnabled] = - React.useState(false); - const [colorScheme, setColorScheme] = - React.useState(colorSchemeName); - - const handleAppearanceChange = ( - preferences: Appearance.AppearancePreferences - ) => { - const { colorScheme } = preferences; - setColorScheme(colorScheme); - }; + const { reduceMotion = 'auto' } = props; - React.useEffect(() => { - let subscription: NativeEventSubscription | undefined; - - if (!props.theme) { - subscription = addEventListener( - AccessibilityInfo, - 'reduceMotionChanged', - setReduceMotionEnabled - ); - } - return () => { - if (!props.theme) { - subscription?.remove(); - } - }; - }, [props.theme]); - - React.useEffect(() => { - let appearanceSubscription: NativeEventSubscription | undefined; - if (!props.theme) { - appearanceSubscription = Appearance?.addChangeListener( - handleAppearanceChange - ) as NativeEventSubscription | undefined; - } - return () => { - if (!props.theme) { - if (appearanceSubscription) { - appearanceSubscription.remove(); - } else { - // @ts-expect-error: We keep deprecated listener remove method for backwards compat with old RN versions - Appearance?.removeChangeListener(handleAppearanceChange); - } - } - }; - }, [props.theme]); + const colorScheme = useSystemColorScheme(!props.theme); + const resolvedReduceMotion = useResolvedReduceMotion(reduceMotion); const theme = React.useMemo(() => { const scheme = colorScheme === 'dark' ? 'dark' : 'light'; const defaultThemeBase = defaultThemes[scheme]; + const userScale = props.theme?.animation?.scale ?? 1; return { ...defaultThemeBase, ...props.theme, animation: { ...props.theme?.animation, - scale: reduceMotionEnabled ? 0 : 1, + scale: resolvedReduceMotion ? 0 : userScale, }, }; - }, [colorScheme, props.theme, reduceMotionEnabled]); + }, [colorScheme, props.theme, resolvedReduceMotion]); const { children, settings } = props; @@ -105,9 +60,11 @@ const PaperProvider = (props: Props) => { - - {children} - + + + {children} + + diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 7b9b554ff7..d94d45b9f7 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -8,6 +8,7 @@ import { import { render, act } from '@testing-library/react-native'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { LightTheme, DarkTheme } from '../../theme/schemes'; import type { ThemeProp } from '../../types'; import PaperProvider from '../PaperProvider'; @@ -16,9 +17,7 @@ import { useTheme } from '../theming'; declare module 'react-native' { interface AccessibilityInfoStatic { removeEventListener(): void; - __internalListeners: Array< - (options: { reduceMotionEnabled: boolean }) => {} - >; + __internalListeners: Array<(enabled: boolean) => void>; } namespace Appearance { @@ -38,6 +37,7 @@ declare module 'react-native' { interface ViewProps { theme?: object; + reduceMotion?: boolean; } } @@ -82,6 +82,7 @@ const mockAccessibilityInfo = () => { removeEventListener: jest.fn((cb) => { listeners.push(cb); }), + isReduceMotionEnabled: jest.fn(() => Promise.resolve(false)), __internalListeners: listeners, }, }; @@ -122,36 +123,94 @@ describe('PaperProvider', () => { ); }); - it('should set AccessibilityInfo listeners, if there is no theme', async () => { + it('subscribes to AccessibilityInfo and adapts theme.animation.scale when OS reduce-motion is enabled (auto mode)', async () => { mockAppearance(); mockAccessibilityInfo(); - const { rerender, getByTestId } = render(createProvider()); + const { getByTestId } = render(createProvider()); expect(AccessibilityInfo.addEventListener).toHaveBeenCalled(); - act(() => - AccessibilityInfo.__internalListeners[0]({ - reduceMotionEnabled: true, - }) - ); + act(() => AccessibilityInfo.__internalListeners[0](true)); expect( getByTestId('provider-child-view').props.theme.animation.scale ).toStrictEqual(0); + }); + + it('exposes the resolved reduce-motion boolean via useReduceMotion to children', async () => { + mockAppearance(); + mockAccessibilityInfo(); + + const Probe = () => { + const reduceMotion = useReduceMotion(); + return ; + }; + + const { getByTestId, rerender } = render( + + + + ); + expect(getByTestId('reduce-motion-probe').props.reduceMotion).toBe(true); - rerender(createProvider(ExtendedLightTheme)); - expect(AccessibilityInfo.removeEventListener).toHaveBeenCalled(); + rerender( + + + + ); + expect(getByTestId('reduce-motion-probe').props.reduceMotion).toBe(false); }); - it('should not set AccessibilityInfo listeners, if there is a theme', async () => { + it('removes the AccessibilityInfo listener when reduceMotion switches from "auto" to "off"', async () => { mockAppearance(); - const { getByTestId } = render(createProvider(ExtendedDarkTheme)); + mockAccessibilityInfo(); - expect(AccessibilityInfo.addEventListener).not.toHaveBeenCalled(); + const { rerender } = render( + + + + ); + + expect(AccessibilityInfo.addEventListener).toHaveBeenCalledTimes(1); expect(AccessibilityInfo.removeEventListener).not.toHaveBeenCalled(); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( - ExtendedDarkTheme + + rerender( + + + ); + + expect(AccessibilityInfo.removeEventListener).toHaveBeenCalledTimes(1); + }); + + it('does not subscribe to AccessibilityInfo when reduceMotion is "off"', async () => { + mockAppearance(); + mockAccessibilityInfo(); + const { getByTestId } = render( + + + + ); + + expect(AccessibilityInfo.addEventListener).not.toHaveBeenCalled(); + expect( + getByTestId('provider-child-view').props.theme.animation.scale + ).toStrictEqual(1); + }); + + it('forces animation.scale to 0 when reduceMotion is "on" without subscribing', async () => { + mockAppearance(); + mockAccessibilityInfo(); + const { getByTestId } = render( + + + + ); + + expect(AccessibilityInfo.addEventListener).not.toHaveBeenCalled(); + expect( + getByTestId('provider-child-view').props.theme.animation.scale + ).toStrictEqual(0); }); it('should set Appearance listeners, if there is no theme', async () => { diff --git a/src/core/useResolvedReduceMotion.ts b/src/core/useResolvedReduceMotion.ts new file mode 100644 index 0000000000..fe264e54fd --- /dev/null +++ b/src/core/useResolvedReduceMotion.ts @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { AccessibilityInfo } from 'react-native'; + +import { addEventListener } from '../utils/addEventListener'; + +export type ReduceMotionPreference = 'auto' | 'on' | 'off'; + +/** + * Resolves a reduce-motion preference into a boolean. + * + * - `'on'` / `'off'` are explicit overrides. + * - `'auto'` subscribes to `AccessibilityInfo.reduceMotionChanged` and follows + * the OS-level setting. + * + * `AccessibilityInfo.isReduceMotionEnabled()` is async, so the first render + * returns `false` for one frame regardless of OS state. + */ +export function useResolvedReduceMotion( + preference: ReduceMotionPreference +): boolean { + const [osReduceMotion, setOsReduceMotion] = React.useState(false); + + React.useEffect(() => { + if (preference !== 'auto') return; + let cancelled = false; + + const init = async () => { + const v = await AccessibilityInfo.isReduceMotionEnabled?.(); + if (!cancelled && v != null) setOsReduceMotion(v); + }; + void init(); + + const sub = addEventListener( + AccessibilityInfo, + 'reduceMotionChanged', + setOsReduceMotion + ); + return () => { + cancelled = true; + sub.remove(); + }; + }, [preference]); + + return preference === 'auto' ? osReduceMotion : preference === 'on'; +} diff --git a/src/core/useSystemColorScheme.ts b/src/core/useSystemColorScheme.ts new file mode 100644 index 0000000000..11501db016 --- /dev/null +++ b/src/core/useSystemColorScheme.ts @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { Appearance, ColorSchemeName } from 'react-native'; + +/** + * Subscribes to the OS color-scheme setting via `Appearance.addChangeListener` + * and returns the current value. + * + * When `enabled` is false the hook does not subscribe and returns `'light'` — + * used by `PaperProvider` to skip system tracking when the user has supplied + * an explicit theme. + */ +export function useSystemColorScheme(enabled: boolean): ColorSchemeName { + const [colorScheme, setColorScheme] = React.useState(() => + enabled ? Appearance?.getColorScheme() ?? 'light' : 'light' + ); + + React.useEffect(() => { + if (!enabled) return; + const sub = Appearance?.addChangeListener((preferences) => { + setColorScheme(preferences.colorScheme); + }); + return () => { + sub?.remove(); + }; + }, [enabled]); + + return colorScheme; +} diff --git a/src/theme/accessibility/ReduceMotionContext.tsx b/src/theme/accessibility/ReduceMotionContext.tsx new file mode 100644 index 0000000000..34ad809060 --- /dev/null +++ b/src/theme/accessibility/ReduceMotionContext.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; + +export const ReduceMotionContext = React.createContext(false); + +/** + * Returns `true` when the user has requested reduced motion, either via the + * `reduceMotion` prop on `PaperProvider` (`"on"` | `"off"`) or, in `"auto"` + * mode (the default), via the OS-level setting reported by `AccessibilityInfo`. + * + * Use this in component code to gate motion-specific animations (translation, + * scale, transforms) while keeping non-motion animations (opacity, color) intact. + */ +export function useReduceMotion(): boolean { + return React.useContext(ReduceMotionContext); +} diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index 22056d9354..74f6cb5c8c 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -3,7 +3,7 @@ import type { ComponentType } from 'react'; import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; import { DarkTheme, LightTheme } from './schemes'; -import type { InternalTheme, Theme, NavigationTheme } from '../types'; +import type { InternalTheme, Theme, NavigationTheme } from './types'; export const DefaultTheme = LightTheme; From f5b5d755bcb72eff922ce90f539ed326822b2d26 Mon Sep 17 00:00:00 2001 From: Aziz Becha Date: Fri, 15 May 2026 06:04:48 +0100 Subject: [PATCH 17/94] chore: upgrade example app to Expo SDK 56 (#4944) Co-authored-by: Satyajit Sahoo --- example/.gitignore | 5 + example/android/.gitignore | 16 - example/android/app/build.gradle | 176 - example/android/app/debug.keystore | Bin 2257 -> 0 bytes example/android/app/proguard-rules.pro | 14 - .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 34 - .../reactnativepaperexample/MainActivity.kt | 65 - .../MainApplication.kt | 57 - .../res/drawable-hdpi/splashscreen_logo.png | Bin 5448 -> 0 bytes .../res/drawable-mdpi/splashscreen_logo.png | Bin 3114 -> 0 bytes .../res/drawable-xhdpi/splashscreen_logo.png | Bin 8154 -> 0 bytes .../res/drawable-xxhdpi/splashscreen_logo.png | Bin 13571 -> 0 bytes .../drawable-xxxhdpi/splashscreen_logo.png | Bin 20189 -> 0 bytes .../res/drawable/ic_launcher_background.xml | 6 - .../res/drawable/rn_edit_text_material.xml | 37 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1899 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.webp | Bin 3128 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 2620 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 1123 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.webp | Bin 1949 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1593 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 2727 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.webp | Bin 4316 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 3748 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 4391 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.webp | Bin 7102 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 5915 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 6003 -> 0 bytes .../ic_launcher_foreground.webp | Bin 9972 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 8147 -> 0 bytes .../app/src/main/res/values-night/colors.xml | 1 - .../app/src/main/res/values/colors.xml | 6 - .../app/src/main/res/values/strings.xml | 6 - .../app/src/main/res/values/styles.xml | 19 - example/android/build.gradle | 41 - example/android/gradle.properties | 56 - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 - example/android/gradlew | 252 -- example/android/gradlew.bat | 94 - example/android/settings.gradle | 38 - example/app.json | 38 +- example/assets/styles/fonts.css | 36 +- example/index.web.js | 6 +- example/metro.config.js | 2 +- example/package.json | 53 +- example/public/index.html | 16 +- example/src/DrawerItems.tsx | 2 +- .../AnimatedFABExample/AnimatedFABExample.tsx | 2 +- .../AnimatedFABExample/CustomFABControls.tsx | 1 - .../Examples/BottomNavigationBarExample.tsx | 2 +- example/src/Examples/TeamDetails.tsx | 2 +- example/src/Examples/TextInputExample.tsx | 2 +- .../Examples/ThemingWithReactNavigation.tsx | 2 +- example/webpack.config.js | 49 - package.json | 23 +- src/components/Modal.tsx | 2 +- .../TouchableRipple.native.tsx | 2 +- yarn.lock | 2950 +++++++---------- 62 files changed, 1216 insertions(+), 2921 deletions(-) delete mode 100644 example/android/.gitignore delete mode 100644 example/android/app/build.gradle delete mode 100644 example/android/app/debug.keystore delete mode 100644 example/android/app/proguard-rules.pro delete mode 100644 example/android/app/src/debug/AndroidManifest.xml delete mode 100644 example/android/app/src/main/AndroidManifest.xml delete mode 100644 example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainActivity.kt delete mode 100644 example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainApplication.kt delete mode 100644 example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png delete mode 100644 example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png delete mode 100644 example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png delete mode 100644 example/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png delete mode 100644 example/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png delete mode 100644 example/android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 example/android/app/src/main/res/drawable/rn_edit_text_material.xml delete mode 100644 example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp delete mode 100644 example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp delete mode 100644 example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp delete mode 100644 example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp delete mode 100644 example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp delete mode 100644 example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp delete mode 100644 example/android/app/src/main/res/values-night/colors.xml delete mode 100644 example/android/app/src/main/res/values/colors.xml delete mode 100644 example/android/app/src/main/res/values/strings.xml delete mode 100644 example/android/app/src/main/res/values/styles.xml delete mode 100644 example/android/build.gradle delete mode 100644 example/android/gradle.properties delete mode 100644 example/android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 example/android/gradle/wrapper/gradle-wrapper.properties delete mode 100755 example/android/gradlew delete mode 100644 example/android/gradlew.bat delete mode 100644 example/android/settings.gradle delete mode 100644 example/webpack.config.js diff --git a/example/.gitignore b/example/.gitignore index 42b97fb625..8f99825fde 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -3,6 +3,11 @@ .expo/ /coverage +# Expo CNG / prebuild output: native projects are regenerated by +# `expo prebuild` (and `expo run:*`) from app.json + config plugins. +/android +/ios + # Yarn .yarn/* !.yarn/patches diff --git a/example/android/.gitignore b/example/android/.gitignore deleted file mode 100644 index 8a6be07718..0000000000 --- a/example/android/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# OSX -# -.DS_Store - -# Android/IntelliJ -# -build/ -.idea -.gradle -local.properties -*.iml -*.hprof -.cxx/ - -# Bundle artifacts -*.jsbundle diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index b563ea2eaf..0000000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,176 +0,0 @@ -apply plugin: "com.android.application" -apply plugin: "org.jetbrains.kotlin.android" -apply plugin: "com.facebook.react" - -def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath() - -/** - * This is the configuration block to customize your React Native Android app. - * By default you don't need to apply any configuration, just uncomment the lines you need. - */ -react { - entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim()) - reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() - hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" - codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() - - // Use Expo CLI to bundle the app, this ensures the Metro config - // works correctly with Expo projects. - cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim()) - bundleCommand = "export:embed" - - /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '../..' - // root = file("../../") - // The folder where the react-native NPM package is. Default is ../../node_modules/react-native - // reactNativeDir = file("../../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen - // codegenDir = file("../../node_modules/@react-native/codegen") - - /* Variants */ - // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. By default is just 'debug'. - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] - - /* Bundling */ - // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] - - // - // The path to the CLI configuration file. Default is empty. - // bundleConfig = file(../rn-cli.config.js) - // - // The name of the generated asset file containing your JS bundle - // bundleAssetName = "MyApplication.android.bundle" - // - // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' - // entryFile = file("../js/MyApplication.android.js") - // - // A list of extra flags to pass to the 'bundle' commands. - // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle - // extraPackagerArgs = [] - - /* Hermes Commands */ - // The hermes compiler command to run. By default it is 'hermesc' - // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" - // - // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" - // hermesFlags = ["-O", "-output-source-map"] - - /* Autolinking */ - autolinkLibrariesWithApp() -} - -/** - * Set this to true to Run Proguard on Release builds to minify the Java bytecode. - */ -def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean() - -/** - * The preferred build flavor of JavaScriptCore (JSC) - * - * For example, to use the international variant, you can use: - * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - */ -def jscFlavor = 'org.webkit:android-jsc:+' - -android { - ndkVersion rootProject.ext.ndkVersion - - buildToolsVersion rootProject.ext.buildToolsVersion - compileSdk rootProject.ext.compileSdkVersion - - namespace 'com.callstack.reactnativepaperexample' - defaultConfig { - applicationId 'com.callstack.reactnativepaperexample' - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 30 - versionName "3.16.0" - } - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - buildTypes { - debug { - signingConfig signingConfigs.debug - } - release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig signingConfigs.debug - shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false) - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true) - } - } - packagingOptions { - jniLibs { - useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false) - } - } - androidResources { - ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~' - } -} - -// Apply static values from `gradle.properties` to the `android.packagingOptions` -// Accepts values in comma delimited lists, example: -// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini -["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop -> - // Split option: 'foo,bar' -> ['foo', 'bar'] - def options = (findProperty("android.packagingOptions.$prop") ?: "").split(","); - // Trim all elements in place. - for (i in 0.. 0) { - println "android.packagingOptions.$prop += $options ($options.length)" - // Ex: android.packagingOptions.pickFirsts += '**/SCCS/**' - options.each { - android.packagingOptions[prop] += it - } - } -} - -dependencies { - // The version of react-native is set by the React Native Gradle Plugin - implementation("com.facebook.react:react-android") - - def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true"; - def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true"; - def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true"; - - if (isGifEnabled) { - // For animated gif support - implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}") - } - - if (isWebpEnabled) { - // For webp support - implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}") - if (isWebpAnimatedEnabled) { - // Animated webp support - implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}") - } - } - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } -} diff --git a/example/android/app/debug.keystore b/example/android/app/debug.keystore deleted file mode 100644 index 364e105ed39fbfd62001429a68140672b06ec0de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2257 zcmchYXEfYt8;7T1^dLH$VOTZ%2NOdOH5j5LYLtZ0q7x-V8_6gU5)#7dkq{HTmsfNq zB3ZqcAxeY^G10@?efK?Q&)M(qInVv!xjx+IKEL}p*K@LYvIzo#AZG>st5|P)KF1_Z;y){W{<7K{nl!CPuE z_^(!C(Ol0n8 zK13*rzAtW>(wULKPRYLd7G18F8#1P`V*9`(Poj26eOXYyBVZPno~Cvvhx7vPjAuZo zF?VD!zB~QG(!zbw#qsxT8%BSpqMZ4f70ZPn-3y$L8{EVbbN9$H`B&Z1quk9tgp5FM zuxp3pJ0b8u|3+#5bkJ4SRnCF2l7#DyLYXYY8*?OuAwK4E6J{0N=O3QNVzQ$L#FKkR zi-c@&!nDvezOV$i$Lr}iF$XEcwnybQ6WZrMKuw8gCL^U#D;q3t&HpTbqyD%vG=TeDlzCT~MXUPC|Leb-Uk+ z=vnMd(|>ld?Fh>V8poP;q;;nc@en$|rnP0ytzD&fFkCeUE^kG9Kx4wUh!!rpjwKDP zyw_e|a^x_w3E zP}}@$g>*LLJ4i0`Gx)qltL}@;mDv}D*xR^oeWcWdPkW@Uu)B^X&4W1$p6}ze!zudJ zyiLg@uggoMIArBr*27EZV7djDg@W1MaL+rcZ-lrANJQ%%>u8)ZMWU@R2qtnmG(acP z0d_^!t>}5W zpT`*2NR+0+SpTHb+6Js4b;%LJB;B_-ChhnU5py}iJtku*hm5F0!iql8Hrpcy1aYbT z1*dKC5ua6pMX@@iONI?Hpr%h;&YaXp9n!ND7-=a%BD7v&g zOO41M6EbE24mJ#S$Ui0-brR5ML%@|ndz^)YLMMV1atna{Fw<;TF@>d&F|!Z>8eg>>hkFrV)W+uv=`^F9^e zzzM2*oOjT9%gLoub%(R57p-`TXFe#oh1_{&N-YN z<}artH|m=d8TQuKSWE)Z%puU|g|^^NFwC#N=@dPhasyYjoy(fdEVfKR@cXKHZV-`06HsP`|Ftx;8(YD$fFXumLWbGnu$GMqRncXYY9mwz9$ap zQtfZB^_BeNYITh^hA7+(XNFox5WMeG_LtJ%*Q}$8VKDI_p8^pqX)}NMb`0e|wgF7D zuQACY_Ua<1ri{;Jwt@_1sW9zzdgnyh_O#8y+C;LcZq6=4e^cs6KvmK@$vVpKFGbQ= z$)Eux5C|Fx;Gtmv9^#Y-g@7Rt7*eLp5n!gJmn7&B_L$G?NCN`AP>cXQEz}%F%K;vUs{+l4Q{}eWW;ATe2 zqvXzxoIDy(u;F2q1JH7Sf;{jy_j})F+cKlIOmNfjBGHoG^CN zM|Ho&&X|L-36f}Q-obEACz`sI%2f&k>z5c$2TyTSj~vmO)BW~+N^kt`Jt@R|s!){H ze1_eCrlNaPkJQhL$WG&iRvF*YG=gXd1IyYQ9ew|iYn7r~g!wOnw;@n42>enAxBv*A zEmV*N#sxdicyNM=A4|yaOC5MByts}s_Hpfj|y<6G=o=!3S@eIFKDdpR7|FY>L&Wat&oW&cm&X~ z5Bt>Fcq(fgnvlvLSYg&o6>&fY`ODg4`V^lWWD=%oJ#Kbad2u~! zLECFS*??>|vDsNR&pH=Ze0Eo`sC_G`OjoEKVHY|wmwlX&(XBE<@sx3Hd^gtd-fNwUHsylg06p`U2y_={u}Bc - - - - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 2e9f1dfc02..0000000000 --- a/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainActivity.kt b/example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainActivity.kt deleted file mode 100644 index 9c5d6f5f65..0000000000 --- a/example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainActivity.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.callstack.reactnativepaperexample -import expo.modules.splashscreen.SplashScreenManager - -import android.os.Build -import android.os.Bundle - -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate - -import expo.modules.ReactActivityDelegateWrapper - -class MainActivity : ReactActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - // Set the theme to AppTheme BEFORE onCreate to support - // coloring the background, status bar, and navigation bar. - // This is required for expo-splash-screen. - // setTheme(R.style.AppTheme); - // @generated begin expo-splashscreen - expo prebuild (DO NOT MODIFY) sync-f3ff59a738c56c9a6119210cb55f0b613eb8b6af - SplashScreenManager.registerOnActivity(this) - // @generated end expo-splashscreen - super.onCreate(null) - } - - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - override fun getMainComponentName(): String = "main" - - /** - * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] - * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] - */ - override fun createReactActivityDelegate(): ReactActivityDelegate { - return ReactActivityDelegateWrapper( - this, - BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, - object : DefaultReactActivityDelegate( - this, - mainComponentName, - fabricEnabled - ){}) - } - - /** - * Align the back button behavior with Android S - * where moving root activities to background instead of finishing activities. - * @see onBackPressed - */ - override fun invokeDefaultOnBackPressed() { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { - if (!moveTaskToBack(false)) { - // For non-root activities, use the default implementation to finish them. - super.invokeDefaultOnBackPressed() - } - return - } - - // Use the default back button implementation on Android S - // because it's doing more than [Activity.moveTaskToBack] in fact. - super.invokeDefaultOnBackPressed() - } -} diff --git a/example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainApplication.kt b/example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainApplication.kt deleted file mode 100644 index b6cd6d88a9..0000000000 --- a/example/android/app/src/main/java/com/callstack/reactnativepaperexample/MainApplication.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.callstack.reactnativepaperexample - -import android.app.Application -import android.content.res.Configuration - -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage -import com.facebook.react.ReactHost -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load -import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.react.soloader.OpenSourceMergedSoMapping -import com.facebook.soloader.SoLoader - -import expo.modules.ApplicationLifecycleDispatcher -import expo.modules.ReactNativeHostWrapper - -class MainApplication : Application(), ReactApplication { - - override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper( - this, - object : DefaultReactNativeHost(this) { - override fun getPackages(): List { - val packages = PackageList(this).packages - // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(new MyReactNativePackage()); - return packages - } - - override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } - ) - - override val reactHost: ReactHost - get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) - - override fun onCreate() { - super.onCreate() - SoLoader.init(this, OpenSourceMergedSoMapping) - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // If you opted-in for the New Architecture, we load the native entry point for this app. - load() - } - ApplicationLifecycleDispatcher.onApplicationCreate(this) - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) - } -} diff --git a/example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png b/example/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png deleted file mode 100644 index d2e4ebb52320cf6eb3b1e02abc709be1749fbc90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5448 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 diff --git a/example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png b/example/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png deleted file mode 100644 index d2905ac97eda042537e5a3eaacf9cd1affd7e33b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3114 zcmeHK`!^H(AE)oL?TP53xh76>X*7~cr1dD5+%LIqZV}URDWbWIY~T?S0cc!dZ=orJFFa8p_5+wICJ&@?Nez(Z<$I2se}6i~%GnJS#%S zWXVPGE8H*Idt26*W;Y#5D)kHb+I;KA#QsdW;F%=ci6a;>);RLx19aVtN~qnnNY6hp z0?~N;qn*K>=~M_Ed(_mtr-98|HR*@TNZR%J+!{u^A$VE+iY&ec>y-|8Pu^c z)=@s!Wu$T-wCWa^#WoQ+7UT541>d;I1PAhwBA&e3)m4Apfdq0|ej|2jY)S7s%<(#1E-2*LYyD z=ON`LPrB)YxOowFlZrSD(0w-bT(dVyQLi|VB5alK*DWa5LHA9{U|UoQ(A;YIu2pG{ z!^Y$xZVE)v&w~;ag?~KfZ?CB%tlR@JCdWTi^z$oUceRW}wtYhFa4RsKr#gBvjiadL zw^NueKVmmxXJkUqqIqBe{N6q7aUY>QzSTtbZlZLPS=b$4#x)A-`zLH=6K{bqU0A%E zjE{pU4Upb_{8N8}`fXgk5@IZT^SGt7EkqxLX~xU6LEde6;br;?6B6OL)bliwCt||vPqb~^;LCP(iu?#}fn8Dinuk#_?=3M#+ykM)>HZG0m6{Jb;S88f z!5vg%_o#Qye74#?!DN<(>mS2Yn%V8~`?NV@e`N&ns>3BUg_$b728JF#2A$+j)9FDh zq%3)*wIq>#qb(pnbdlip1;YE86@R#FOn07zhpsgpL0P^{fjOPFVC^az4OUtoor6Z8# z%MnW!M4axKBK;m^em(cAS`@_tLzz22topGVku({UT%2(FQN#Vd=*D*qtJ+69Zl+(< z{~qQMq6j_zOwFFMV8cu-0dOPXVI4o~^6NDp?;Dig{n|pxp>(EM16*9{sXa~LFpfWR zhv>;kuIQm{d-?|K6|#oF4~EF}d~G z%6GBgL{SB>OBybAMfLI^7QC2LUw%F7kV9l!YQaS)FL-VVJ3+cG=q=S6o8b%$mdZO-z0% zQr_Ir8w`ze(Z~`r9Nd(8~HrASptY=j0LkEsQ~&u7~I8cub=u_0d&A5 zPaEn!JoeC}CvQr76KSz@r5^uf0lakgazYZIBbC|mR(-!G8ld7*O7tl=xN z*kDR3G`gS9zd1(Rp6gIVdAx}EuTbKR%Rn*Jj(R}==#{no2tf6D7d@+)PWe}?Wi zCBk0fb4-1agt?co9pXr#Z;xHE=`!fDY=;MrEtXmQcz31FSjao58qw+vws(aHpaqa4 zaiONSclr-tqU9rAATl&kW(NHW_X)PBPBvYq>ffZ6nRc7<`SZvcnH3%;uFhXG-n;>4 zS93dNl-LzMF3zivA)L(77xC%dNE2Unx*ZsLmP`s=Yq%V|8~c3nYM-HLnel7thN+N3 zOe$xVW-9YiofXukX#*p9SaAp1zrRiqhnUTpz8)Rj93Z>r3BEdmxt))|KX`*ihezGWDaNr&E=trD|S?dttGPjdd zJF9wOsUABu#U?;3;*e$Td*jd0ahPHL)vZB|&J_;=Xg9LROKwr)*B_H@j|9;JE#wZ$ zcwsmy=FMjqkOTe2$Uw=c)s{|uISmX}WFQOp#tx2V*X|Ui&R%7SM_ln*3DBBJT6#_Z zlK0E+R2$jMO(URJMmy!l+`%jw+zBn5Iw-cxK+^L!?g*DU!w@)bH^+JvzM}~Sh=Wz` zns3^ax8!B#qd|h}C$mco@5X?Z6&bb}NPevbh6O-H17`kivh($ diff --git a/example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png b/example/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png deleted file mode 100644 index c2d00af7375449720db927e878fe615d4a512c5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8154 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 b59U9HxV|KP5nTXvwa<5`Z|lFg_~ZWpb8aBg diff --git a/example/android/app/src/main/res/drawable/ic_launcher_background.xml b/example/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 883b2a080f..0000000000 --- a/example/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml deleted file mode 100644 index 5c25e728ea..0000000000 --- a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - diff --git a/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 3941bea9b9..0000000000 --- a/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 3941bea9b9..0000000000 --- a/example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index 2fff66b74cf3078a4e16351e24591e9ba22af422..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1899 zcma)-dpHw{8^@_*X5pMtbIY~nQmELO+vs4}FzF!5T+dwQk{Fdte%Ua~Epwew$tX&0 zMTnTii0!ycLzYW!UEFdjbk^^Aet-U+=lt=$@AJHWect!^zVA21$>$=jbm8GTlADi?sa5b>M+e+CZX&-gu zdUm2H!~cyI8KHdt6?UEsjP|HaH7a>iz!LA5baC%MO!&B5DYTj(qGF6xlfcANnOaA* zkg;Q9_%AQ!&Z_Fj1N#5qgf-6)%=L2&lDb{B)o&fX^9Kc~YJEIY{Sfk?L@D`~KN0)> ze;fM4_VNqQGeI3{47zsJpnUe737}UD%P?Ro@ z;F5c0%q!C9CRfH#Om|Fs0fsS%u+Y2K@7Uc@gEBXrkpUO<$Xm%^4T-m$LzDXIaqrI8 zgg@!cJ!^6_&XAci({Lo9W#Y~F(8U#mK;ejShl;6so z9G9@lJayLKjY`1`d()#kUoe+{7X9#ull-Yyf3{-kJN&49%%)y)ibnSWGyS{_s%kE<)M zgV#yl9hFF}-4KJAm|!I_NbYuv`mwIP9JNqd=K5Wq`+k=ENZUq0j3J0vcJXjOlKVKP zI;7y_Q@B%a3e()p*~8x*&PZ?64Os2K{0&?E_Fx@L)WC{Cz+D@$1g|eLGr3iil8X|; zae^Jchk813`dNC<0wqT}UwNDxwincZTf^Vh4L}s3YQfiRQ#Z_@mGS4ha>)PHIYXl9 zNC<-TmpJ_9$Wwq|x~XWdb1vc6t2bPf*4O|v8IbL+F)h!p>-o?Ip)p@+!_2aaerFi!7nW76B^YGM;ZMAQyI)n0@>oPB+jGIF!1 zr1v-U`A29@^EBbcX=cyB;qSc0>(a8?QoEY&WrPQs!`^a}CP>K}X%xI*&{#`~BV7rh z+H@a6BZceGliPMr;1wMf=P~*Qt*EQ*jv4kO`zA1s8h<-$xOcJf!Ug4>_nI&k17`T# zZ3myy+hnYrRW8LZl|W@QPYo5X^V{^op1*vL96saSNV!D~TWtMmycIzE0clG+9hDT8 z$?qT>;0xc);R9kMX0jAdj@>9sygIRuUksO(mVOUEl${F#fpOmCyL!T zm2Gyu1d`1?oR1h)$1yHzl6eij$Lt{rnVemM?LVed%qlS(Rj9(7(^Iu^_lYYwC=D=F zK`iO@=@S$`?%K)}*k}>n!iXyMtgY&}`%Ip!QpHKBbJ-1<8JpNQ>|f*VAH*qe|C0h% zUyiX(!u3WjnDVA(l3QC~rq=w(pP)_8+)`nle?88>s0r0a?>~``Ra1vot7kAh;IjfM zG#Pe1VN!p;#--~xX6v&mQIbt5PW6KF%(&_-e0yy-I$Jl|_}msK#iDH`*SLjGrBQ8y zuoS=)uVZ{-cy$k!p?y12UjLmw=I6J`4a@oDH92rtOTj6c;XAQ>JlxEc(<%01^?e1L z8r1I(aHzi$EG5oshKf-D0s70c)9Y|Ub#|E5@A!!@i)Q6BdRz$y;XcKA0*M^`ZqDp0=0f@}hOH$k>R>d>_LaS0}#Nlhi_w3p^ZGFnHS@I8jY zGwzq6iC7<7GjlgAFP@91Lob8s@HO@tXiwD)*Ti diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp deleted file mode 100644 index 0537194be8c6254bdae7603bfa5b13cacc5d5cd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3128 zcmbVP`Bzid7DmKC%Xk49!e}TK0Vzp9B9kK5pn^d}!GxKRC)=nkXnDB1ig_@f7*PWo>zVubEyN;i_u;#d${N>o^ZT+k|iQ_N+ z+cmTu`v!`OPvqNSy6ppYY@G)a3S`}$O7WG{r(aeTY3woH{Ida|u}17_y&9@Nkq@mE z^$q`Sxn0YkEO~L`(Cg)egF7Bne}3KZXKnf3T^s8EwIH_cAy&aN_aBL^Zb(441CZvt z*Qg=DH?!Yn4&bhLjT0<0sn=)`ya?6?6)3T4JK!+Ak4};dEIUNg{&DVEntKK<7r7Ys zdHy1JE~lp3Y#N-(iOjp8IO+{;ml;$lTV$(`+$RYAs`7&aB%y6P92mRli}Ash{OtZK z?uA#_jxHt8_VWSs4h@^0`HH%CwtFX^-(&m+!L zPeKq%76@xii4uHREv!>jRNI2Q6UbxLTs0Bg^405^mzmcr=(zN3Xm!C1qJI@wD8ps2 z$YOp3_=PAUlI;Z_471E^y6B!hh4}f?Q0oZA0N~SA=7a%JwOE(wk?-F3{Uf;3qMzF> z?jYTaMX89AZHyQ7gb+sfC{u$`{?8eKQe^W{g*l=@m0>85_vC3Qao#GMb`hEEQ+4QY)y zSmCalW=^EHuf9X^U&fNUmh9sDAEcg;#z#^S%&!P0>xYk`YLYhA6pvAu*d3s6A_PtU z2w-1mjhHEM@769m_@3CE+NK$EUjWRXdNUW+9>h4~(r;FP!;36FinjD5+JDi-&%K7y ziN^#D3mPucHE8M%dK2FtYvqM$KHpVDSUVB`zQPGMuhLA4vIg|0)l0Fq_#c(s1XSg# z_i9fh;h6Z2H;c@mr{{!$Zp21dXQpt9pfNq2YDABZ2A$nJgN8EH!Y2(&L)POhEFs_a zH8t+gBbH-RF|@80q<5Ij_P4oW&P(ml$f=1tMbP_cXo^qJ#M>#35ij|R&r6-7>aZDE zmNC+)O;wzCnE1Sb!{?+{tv+JkI3{hR%z8gDlm*SLV9okp%Y$K{p)FtR?-y=`RT zP%}M38<%3}3sJ@OB);6a${M(P9Sus5L?cC>)SXv{d%7vd6qKl|19!@vwz*@N*HAPS zqo13YUPHRu6I5ji@FN~$6m77#-b?h2781hy9#cjt;sc-PEAG$Hrv!Ion%U`I87&-^ zJ?Z{wKrB1wu4&V2k&3<-8PzmUUAEP16ZB3<6x;&FOZagS)z-bqFa&)SodG2ZxT?To zGBpJpz5K|%Er?#$JwXW%kobfZz@zA1#b?=pOSf5`Pl_R~&|-u<=v=$Cc4ek)BVyDV z!Lx!fw)f(8>Q4RG73jdBv5y6$g5`#g-v_cA5`FSYgoSXVO+c(?G*^^r3QD$3kEd7X zm;rY^zISWeK__+kQO*)2c41rT%rfd6(7xl+3Tu7ziD2}Z3R;59@6tP-VdfSi8mMi6qu!Db6@58 z*`p(G2%}&IdhTUlRg_*_N<>wu>Sn8Ve8srDKf9H0QkrC z1kYlNrS1UxY?=f9MgEbvU*q`kNuc*(Cce+Dt^24fgrT>o4_;Cbl7&O+@I4;XI`-CU*YtOh z2^$~5!hY@&)lQhnW`U!z#!0E%#>s)d%#X!Ut8+^5bEVL7?b=fUi@03jEKyP`&4+c` z0h?aGL(M3x_E!Ty-@G1e7m4(yVv^<|`=)!1tioLoi$jMi@>1*+-%*38f3KjUd1sY-BaMMz@|}j? zvCi1uq;i3mLc=nDo^tXE+t7s%`BC>#3rnEhIllC&T}zHR;6dJwIP%zp#!MbUu{PT< z#{@{^F?ejjEu$MF=V)ucX=OMh7Lsx$-a>yNY7&74KDzco`Aqt6?ZKD%*`lmCH zfOoFJa%ie_%Z7z+sN%?G8a*~3OI?%N9)@You?B8B{@IAV@w(_C+L4D?evE#;&K=S&uTSEI2tJs~f~S9l z(qW|hZJftu8B|Wbzkv7vsoe)@DuU(Z1truPOupPBOLL9>PulsfKjK&4mTOoRE;MuI zj6P|1SO6tye_2%~J`Oq4h&^i6nC8*+_9{}SKeN=wdHoq^(}{AZb$oF5aEAJekTV;2 zeu7(Ta|*1xI|_eN9C|--+S7;k$2Zy-=C~B~FWN@Bw{0Z0gAq2*FVSId(KCRO6`3Lv{^W2 zG1Q!oOEGE>oqi6?m%nXCQ~zwzs(#XS$u7QQe~MNQ15rAVFMgRE*)ZgrUb^37|Flcw z73Z_AC>s5zFM2VZgl-dMi5Ud>IXU2C^1S-Z7VDwq@@tV9JU>88z;y6#Dx|~mn0Pjr1!Tr`#OeALIN?*S(Y(~- zWVWgePIciQe#^_tyS=iqa!{Ej`%4rqhrWFIQmKhTy?K8ScU?24w}%sD1zd7;stcGx zvPwR-tb@|6@jKB?X?6u?n)TCq^P_CgvhJ$LnkK*~^wUj!x%WADjKgEC*CPQCw(F0#?TC3C zWHr4)Oft!&`oE8l5OUcwot>Q+TG4{zqrP9l-JL@9lrz%YlG^6(h!A9YIXdUG&-5doE)`}XboH@LJMO(4l$9Q}M`9Q*jcmMNmI zJCF|a1$qN1KoZaeNCXt%0Cm7VU>C3**amC@Du8ODL=hb$fRR8Jkjn8XpcBvu=m`ww zNSlFGzzU$02vS5&FJLTi2ars7r2?tI1HevTF|YurBFq%wHxS4NvWbplU@9;bSOPo& zloKY3@aY8<0y)G^HjoXJ07XC*AyS0LG@ytRM-Gq!6aq5{fg&_%z-(YJC#E9cYG4vj zNjrGbm_j>^2UY;doY<0qdw?&2O4`VveP#mlIJwLNX3|CmZC4CT<>WLKD5ebz+GY_j zj+5IsU=gik(0au{4yT43pqN%LXq}nBI8H6&fSDYJ!SRg;rgCbU3XJC%42~@gn8&GY z9*{;0432F!r-#|Jz@TN*fWe#|1_RS*hN5}BfFe#X=Ui-XMV`eaSzdm0p@FiXX1CVc z-*aTci+(DW*gD`xXrChBC7_DKD4JKu2|^4=y;_kuIgUPAiciY|diB#jzWBfqy8+JZ zVaO#{Dstu|a7{s+%)PCauQt21Qz4McVPqWpILbhvj2N>zz@WPuGRpVH^_{CUW6m zrPn=Y%e*mllr8l0-7ZZ_8f)O6ulvcIY)RJ?kA8odUud5Wz%mX}9H|$OO$=N#M3I$m zaoP1=zStDth5H+*SYqS&Xtn)cYx&@HKWU>(q5ZOf=|B}t6h|6M6sb7$fYGL8PPRBV z)kEd0ejXWK%SZ1z93-?+zy^<(P>lr&XregM9YjIrBw^^i7DMi_@OWWOv90IaSSNe8 zyM#gv=#%B;qjw!bbO%sC6U8ATfMg;lrN2jK=3Df?)s$~Ob9r@oBV`MH_-YZtpw|#Z z*JOiNrZo_vWMBkPsyJjM5nw3jcaxW_{5XBVK#y!$=`ed#t=_WI5$z&`(ZF02U!7*- zD>h*;5-3$1l0_KBcNT`;Ycc#ji-a?Ub?Ku z;MkgPK6Tkz>epLWIpR9}pu<_BFS*JqtDp3V>kv+T6+yiTql{dWy*ph#de@;nQhw@T z#AJ)~+fCW`h0B(e4j;Yi#~UZ~=h5vE%FJikl01IBF|*_#5g*171k}Z<+%xU z{Od!PmelRl6yH^k*Aji_+yqe;^73yRiGUQT#}^_5q4BM%t)`nb!f9apSv9J@fwG{J6s|lQ4ypFgMqoG4meuYzhHB?xHDkT zT^1uIS#&+$V8c?I1$p({U7X0e=WJYub~(SFhXd9wu!#_*2yy^L2*d*eMw_zrEra7n;pq66r+pYqA-r+m6*w-zFA zmm+98Fqjbh{_+}X_PE$}2*(iD5iT3$mGz5l{0+3|yvsaf-ET2qw8`#IU0GJppvxB8 z;&0#pAv(4zg0>N&y*pf5wYhjxjTO^Toj zqM+;f2EB$TR{Wt6?Ly}yVfZgCJ>(abXcyj@>*MXGeC)5*6m97!pREt*-JiHZ>{KX% zs)5ZwDiJg=$3z5nZ4HpOFoFKJne6_=m1PADlr6Hw-@p%&(>x^g@lZ0UUWlE|K(*qK zRX{2clrhFcM3_*L$cLp4vu~)?o8NE*%@GFXm^gmTh8JxO2(ht>CW=E=01pr$W+KFQ z65T}R-(64H^EOR{qlgF6vrYZsTaMK1)% znwnGPSPRUc83xBTiPOU*T3~Q&l|UY+wmhJc78o2~F))i$(=4EvV=!o)0$@I; zmia&d$6?TVE%iJcYnmpqh44gvSivC7=+31k7Ogl;pi3RnS@5JFp>eSzLU3XlYJ0TKZPI6xh+57-542etv5 efC`|RDET)T#$pA2$dOh600009KiAK?>WyocQ4nRie)NjM!w|mt(@Dm&^ccyB@{Hx8Kwk?9*}dKyoX90jCH!MZhTnP7!d5fKvpVBH%RtvtjubMJq!L%C(4j80{eEfAFc?+)c~bIEm8y z4=fw=-~wBUR%Mv>3voG8=JIn@3=D!m?ksKAd={3S%RBj{Dn=r#@vPrSnuPm{nXxWI zO3Jgu?Szk??~6E zqLV~vl5~HuHupWGW#hPZ;xTT^#D%u0I~FpbI6@( zligQLXPc3;N264|@6pw2$S(zTVx-rdVhTze*>I{CAFk_SzzD_Z%BTT^1Zk2mVV*-nb&U24hJO*rouy52X@=OD z$kBIP_LX*Fe6G22(a0BDT-q+gS@lUa$9H(*dn84Zg!~eR>fIjKbF!iE$&i`8IwawK z!q(Xl8f$%ea{BWamKLh_xFk!HpqzoiusnkDf4ByD+gZeGVvJl>aTZDKEHz= z35=U-v+&JKwlC>07=$Eg5>!y)&}XnRcBXK6Yq!*&@P&WKh^ZPk0*&AK+>Fk^cUIk^rETGmjf?!(OBz)z=GE^ z*}1HPbkL-LWn=Cw)Y4ELqik-3)@H+1_e`{mrICgjpL9^9fFDIRPi`k?pfGt^2vJ-! z=?O<__IY4P3q^`F*TvaXc!j%$E7n$q3HKMHG~_+*n3}zwAx*6DtlvnA_)(}m7{#(- zMMbt`!&YgFRPwz;H}CQX{o#Mv;qope(KoFd>90jCH! pMZhTnP7!d5fKvpVBH$DO_ZRw4d^r+Th1~!E002ovPDHLkV1iro9AW?f diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp deleted file mode 100644 index 2e3c35551d2126b5e044241d7c1731dd23f75a7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1949 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 E-*it!jsO4v diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 5a10eeb1fcbcc03f4d06612e7ca9c727a35298be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4316 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 diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 47ca7d7f471028b8f4467c91be6ca5d7fe595fe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7102 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 diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 1c38eb6136a4b0ab31406b2e96c045538a3fb38f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5915 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 diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index fba93f074f8b1b9d777b6d5bd2a56a27f3241eb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6003 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 p8e#s@e~Rn>@3!~zB diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp deleted file mode 100644 index 1899f38acf95c6bf680b56f218dc5acb75ff582a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9972 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 \ No newline at end of file diff --git a/example/android/app/src/main/res/values/colors.xml b/example/android/app/src/main/res/values/colors.xml deleted file mode 100644 index 5ad704b32e..0000000000 --- a/example/android/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,6 +0,0 @@ - - #6200EE - #6200EE - #6200EE - #6200EE - \ No newline at end of file diff --git a/example/android/app/src/main/res/values/strings.xml b/example/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 076a2376b0..0000000000 --- a/example/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - React Native Paper Example - cover - false - exposdk:52.0.0 - \ No newline at end of file diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 7f305ebf13..0000000000 --- a/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - \ No newline at end of file diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index e119b72df9..0000000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,41 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - ext { - buildToolsVersion = findProperty('android.buildToolsVersion') ?: '35.0.0' - minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '24') - compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '35') - targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34') - kotlinVersion = findProperty('android.kotlinVersion') ?: '2.0.21' - - ndkVersion = "27.1.12297006" - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath('com.android.tools.build:gradle') - classpath('com.facebook.react:react-native-gradle-plugin') - classpath('org.jetbrains.kotlin:kotlin-gradle-plugin') - } -} - -apply plugin: "com.facebook.react.rootproject" - -allprojects { - repositories { - maven { - // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android')) - } - maven { - // Android JSC is installed from npm - url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist')) - } - - google() - mavenCentral() - maven { url 'https://www.jitpack.io' } - } -} diff --git a/example/android/gradle.properties b/example/android/gradle.properties deleted file mode 100644 index 7531e9eb23..0000000000 --- a/example/android/gradle.properties +++ /dev/null @@ -1,56 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true - -# Enable AAPT2 PNG crunching -android.enablePngCrunchInReleaseBuilds=true - -# Use this property to specify which architecture you want to build. -# You can also override it from the CLI using -# ./gradlew -PreactNativeArchitectures=x86_64 -reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 - -# Use this property to enable support to the new architecture. -# This will allow you to use TurboModules and the Fabric render in -# your application. You should enable this flag either if you want -# to write custom TurboModules/Fabric components OR use libraries that -# are providing them. -newArchEnabled=true - -# Use this property to enable or disable the Hermes JS engine. -# If set to false, you will be using JSC instead. -hermesEnabled=true - -# Enable GIF support in React Native images (~200 B increase) -expo.gif.enabled=true -# Enable webp support in React Native images (~85 KB increase) -expo.webp.enabled=true -# Enable animated webp support (~3.4 MB increase) -# Disabled by default because iOS doesn't support animated webp -expo.webp.animated=false - -# Enable network inspector -EX_DEV_CLIENT_NETWORK_INSPECTOR=true - -# Use legacy packaging to compress native libraries in the resulting APK. -expo.useLegacyPackaging=false diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index a4b76b9530d66f5e68d973ea569d8e19de379189..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 79eb9d003f..0000000000 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/example/android/gradlew b/example/android/gradlew deleted file mode 100755 index f5feea6d6b..0000000000 --- a/example/android/gradlew +++ /dev/null @@ -1,252 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat deleted file mode 100644 index 9b42019c79..0000000000 --- a/example/android/gradlew.bat +++ /dev/null @@ -1,94 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index d0cca28108..0000000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,38 +0,0 @@ -pluginManagement { - includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile().toString()) -} -plugins { id("com.facebook.react.settings") } - -extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> - if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') { - ex.autolinkLibrariesFromCommand() - } else { - def command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'android' - ].toList() - ex.autolinkLibrariesFromCommand(command) - } -} - -rootProject.name = 'React Native Paper Example' - -dependencyResolutionManagement { - versionCatalogs { - reactAndroidLibs { - from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml"))) - } - } -} - -apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle"); -useExpoModules() - -include ':app' -includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile()) diff --git a/example/app.json b/example/app.json index 57c5d74b41..d8b2b6debb 100644 --- a/example/app.json +++ b/example/app.json @@ -3,18 +3,16 @@ "name": "React Native Paper Example", "description": "Example app for React Native Paper: https://callstack.github.io/react-native-paper/", "slug": "react-native-paper-example", - "privacy": "public", "version": "3.16.0", "orientation": "default", "primaryColor": "#6200EE", "userInterfaceStyle": "automatic", "icon": "./assets/images/paper-icon.png", - "newArchEnabled": true, - "packagerOpts": { - "config": "./metro.config.js", - "projectRoots": "" - }, - "platforms": ["android", "ios", "web"], + "platforms": [ + "android", + "ios", + "web" + ], "owner": "react-native-paper", "ios": { "bundleIdentifier": "com.callstack.reactnativepaperexample", @@ -23,27 +21,37 @@ "android": { "package": "com.callstack.reactnativepaperexample", "versionCode": 38, - "permissions": ["READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE"], + "permissions": [ + "READ_EXTERNAL_STORAGE", + "WRITE_EXTERNAL_STORAGE" + ], "icon": "./assets/images/paper-icon.png", "adaptiveIcon": { "foregroundImage": "./assets/images/android-icon.png", "backgroundColor": "#6200EE" } }, - "splash": { - "image": "./assets/images/splash.png", - "backgroundColor": "#6200EE", - "resizeMode": "cover" - }, "extra": { "eas": { "projectId": "ba8f5139-58fe-48f0-b2f2-2d675b6eb2d4" } }, - "runtimeVersion": "exposdk:52.0.0", + "runtimeVersion": { + "policy": "sdkVersion" + }, "updates": { "url": "https://u.expo.dev/ba8f5139-58fe-48f0-b2f2-2d675b6eb2d4" }, - "plugins": ["expo-font"] + "plugins": [ + "expo-font", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash.png", + "backgroundColor": "#6200EE", + "resizeMode": "cover" + } + ] + ] } } diff --git a/example/assets/styles/fonts.css b/example/assets/styles/fonts.css index 1b282d638f..6c6a8a2866 100644 --- a/example/assets/styles/fonts.css +++ b/example/assets/styles/fonts.css @@ -1,47 +1,37 @@ +/* + * Roboto is loaded via Google Fonts in public/index.html. + * These @font-face aliases map the legacy "Roboto-" family names + * used in the app code to the right weights of the loaded Roboto font. + */ + @font-face { font-family: 'Roboto-Thin'; font-style: normal; font-display: swap; font-weight: 100; - src: - local('Roboto Thin '), - local('Roboto-Thin'), - url('~typeface-roboto/files/roboto-latin-100.woff2') format('woff2'), /* Super Modern Browsers */ - url('~typeface-roboto/files/roboto-latin-100.woff') format('woff'); /* Modern Browsers */ + src: local('Roboto Thin'), local('Roboto-Thin'), local('Roboto'); } - + @font-face { font-family: 'Roboto-Light'; font-style: normal; font-display: swap; font-weight: 300; - src: - local('Roboto Light '), - local('Roboto-Light'), - url('~typeface-roboto/files/roboto-latin-300.woff2') format('woff2'), /* Super Modern Browsers */ - url('~typeface-roboto/files/roboto-latin-300.woff') format('woff'); /* Modern Browsers */ + src: local('Roboto Light'), local('Roboto-Light'), local('Roboto'); } - + @font-face { font-family: 'Roboto'; font-style: normal; font-display: swap; font-weight: 400; - src: - local('Roboto Regular '), - local('Roboto-Regular'), - url('~typeface-roboto/files/roboto-latin-400.woff2') format('woff2'), /* Super Modern Browsers */ - url('~typeface-roboto/files/roboto-latin-400.woff') format('woff'); /* Modern Browsers */ + src: local('Roboto Regular'), local('Roboto-Regular'), local('Roboto'); } - + @font-face { font-family: 'Roboto-Medium'; font-style: normal; font-display: swap; font-weight: 500; - src: - local('Roboto Medium '), - local('Roboto-Medium'), - url('~typeface-roboto/files/roboto-latin-500.woff2') format('woff2'), /* Super Modern Browsers */ - url('~typeface-roboto/files/roboto-latin-500.woff') format('woff'); /* Modern Browsers */ + src: local('Roboto Medium'), local('Roboto-Medium'), local('Roboto'); } \ No newline at end of file diff --git a/example/index.web.js b/example/index.web.js index 88adf8f637..e69c84e6dd 100644 --- a/example/index.web.js +++ b/example/index.web.js @@ -1,3 +1,7 @@ import './assets/styles/fonts.css'; -export { default } from './src/index'; +import { registerRootComponent } from 'expo'; + +import PaperExample from './src/index'; + +registerRootComponent(PaperExample); diff --git a/example/metro.config.js b/example/metro.config.js index 687d72ec22..418cdb3d81 100644 --- a/example/metro.config.js +++ b/example/metro.config.js @@ -1,5 +1,5 @@ const { getDefaultConfig } = require('@expo/metro-config'); -const path = require('path'); +const path = require('node:path'); const { withMetroConfig } = require('react-native-monorepo-config'); /** @type {import('metro-config').MetroConfig} */ diff --git a/example/package.json b/example/package.json index 6a412f008c..324ad3d8bf 100644 --- a/example/package.json +++ b/example/package.json @@ -6,48 +6,39 @@ "private": true, "scripts": { "eas-build-pre-install": "cd .. && yarn", - "start": "EXPO_NO_TYPESCRIPT_SETUP=1 expo start --go", - "android": "EXPO_NO_TYPESCRIPT_SETUP=1 expo start --android", - "android:native": "EXPO_NO_TYPESCRIPT_SETUP=1 expo run:android", - "ios:native": "EXPO_NO_TYPESCRIPT_SETUP=1 expo run:ios", - "ios": "EXPO_NO_TYPESCRIPT_SETUP=1 expo start --ios", - "web": "EXPO_NO_TYPESCRIPT_SETUP=1 expo start --web" + "start": "EXPO_NO_TYPESCRIPT_SETUP=1 expo start --dev-client", + "android": "EXPO_NO_TYPESCRIPT_SETUP=1 expo run:android", + "ios": "EXPO_NO_TYPESCRIPT_SETUP=1 expo run:ios" }, "dependencies": { - "@expo/vector-icons": "^15.0.2", - "@expo/webpack-config": "~19.0.1", "@react-native-async-storage/async-storage": "2.2.0", "@react-native-masked-view/masked-view": "0.3.2", "@react-navigation/bottom-tabs": "^7.3.10", "@react-navigation/drawer": "^7.3.9", "@react-navigation/native": "^7.1.6", "@react-navigation/stack": "^7.2.10", - "expo": "^54.0.0", - "expo-crypto": "~15.0.7", - "expo-dev-client": "~6.0.14", - "expo-font": "~14.0.9", - "expo-keep-awake": "~15.0.7", - "expo-splash-screen": "~31.0.10", - "expo-status-bar": "~3.0.8", - "expo-updates": "~29.0.12", - "file-loader": "^6.2.0", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-native": "0.81.4", - "react-native-gesture-handler": "~2.28.0", - "react-native-monorepo-config": "^0.1.6", - "react-native-reanimated": "^4.3.0", - "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0", + "expo": "~56.0.0-preview.11", + "expo-crypto": "~56.0.3", + "expo-dev-client": "~56.0.9", + "expo-font": "~56.0.3", + "expo-keep-awake": "~56.0.3", + "expo-splash-screen": "~56.0.5", + "expo-status-bar": "~56.0.4", + "expo-updates": "~56.0.10", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-native": "0.85.3", + "react-native-gesture-handler": "~2.31.1", + "react-native-reanimated": "4.3.1", + "react-native-safe-area-context": "~5.7.0", + "react-native-screens": "4.25.0", "react-native-web": "^0.21.0", - "react-native-worklets": "^0.8.1", - "typeface-roboto": "^1.1.13" + "react-native-worklets": "0.8.3" }, "devDependencies": { - "@babel/core": "^7.25.2", - "babel-plugin-module-resolver": "^5.0.0", - "babel-preset-expo": "~54.0.0", - "url-loader": "^4.1.1" + "@babel/core": "^7.29.0", + "babel-preset-expo": "~56.0.0", + "react-native-monorepo-config": "^0.3.4" }, "engines": { "node": ">=20" diff --git a/example/public/index.html b/example/public/index.html index 62cc885b40..f1ef43f74d 100644 --- a/example/public/index.html +++ b/example/public/index.html @@ -1,11 +1,19 @@ + - - - + + App + + + + + - - -

- - -``` - -Now we're ready to start configuring the project. - -## Configure webpack - -### 1. Alias `react-native` to `react-native-web` - -First, we have to tell webpack to use `react-native-web` instead of `react-native`. Add the following alias in your webpack config under `resolve`: - -```js -alias: { - 'react-native$': require.resolve('react-native-web'), -} -``` - -### 2. Configure `babel-loader` - -Next, we want to tell `babel-loader` to compile `react-native-paper` and `react-native-vector-icons`. We would also want to disable reading the babel configuration files to prevent any conflicts. - -First install the required dependencies: - -```bash npm2yarn -npm install --save-dev babel-loader @babel/preset-env @babel/preset-react @babel/preset-flow @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread -``` - -Now, add the following in the `module.rules` array in your webpack config: - -```js -{ - test: /\.js$/, - exclude: /node_modules[/\\](?!react-native-vector-icons)/, - use: { - loader: 'babel-loader', - options: { - // Disable reading babel configuration - babelrc: false, - configFile: false, - - // The configuration for compilation - presets: [ - ['@babel/preset-env', { useBuiltIns: 'usage' }], - '@babel/preset-react', - '@babel/preset-flow', - "@babel/preset-typescript" - ], - plugins: [ - '@babel/plugin-proposal-class-properties', - '@babel/plugin-proposal-object-rest-spread' - ], - }, - }, -}, +```bash +npx expo start --web ``` -### 3. Configure `file-loader` +No additional bundler configuration is required. See the [Expo Web docs](https://docs.expo.dev/workflow/web/) for details on how Expo configures React Native for Web under the hood. -#### webpack < 5.0 +## Without Expo -To be able to import images and other assets using `require`, we need to configure `file-loader`. Let's install it: - -```bash npm2yarn -npm install --save-dev file-loader -``` +If you're not using Expo, follow the [React Native for Web setup guide](https://necolas.github.io/react-native-web/docs/setup/) to configure your bundler. The setup covers aliasing `react-native` to `react-native-web` in webpack, Babel, and Jest. -To configure it, add the following in the `module.rules` array in your webpack config: +You will also need to manually load the Material Design icon font used by Paper. Add the following to your HTML shell or inject it at the root of your app: -```js -{ - test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/, - loader: 'file-loader', +```css +@font-face { + font-family: 'MaterialDesignIcons'; + src: url('~@react-native-vector-icons/material-design-icons/fonts/MaterialDesignIcons.ttf') format('truetype'); } ``` -##### webpack >= 5.0 - -Use `asset/resource`, since `file-loader` was deprecated in webpack v5. - -```js -{ - test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/, - type: 'asset/resource' -} -``` - -## Load the Material Design Icons - -If you followed the getting started guide, you should have the following code in your root component: - -```js - - - -``` - -Now we need tweak this section to load the Material Design Icons from the [`react-native-vector-icons`](https://github.com/oblador/react-native-vector-icons) library: - -```js - - - {Platform.OS === 'web' ? ( - - ) : null} - - - -``` - -Remember to import `Platform` from `react-native` at the top: - -```js -import { Platform } from 'react-native'; -``` - -You can also load these fonts using [`css-loader`](https://github.com/webpack-contrib/css-loader) if you prefer. - -## Load the Roboto fonts (optional) - -The default theme in React Native Paper uses the Roboto font. You can add them to your project following [the instructions on its Google Fonts page](https://fonts.google.com/specimen/Roboto?selection.family=Roboto:100,300,400,500). - -## We're done! - -You can run `webpack-dev-server` to run the webpack server and open your project in the browser. You can add the following script in your `package.json` under the `"scripts"` section to make it easier: - -```json -"web": "webpack-dev-server --open" -``` +## Load the Roboto font (optional) -Now you can run `yarn web` to run the project on web. +The default Paper theme uses the Roboto typeface. With Expo, use the [`@expo-google-fonts/roboto`](https://github.com/expo/google-fonts/tree/master/font-packages/roboto) package. For other setups, follow the instructions on the [Roboto specimen page](https://fonts.google.com/specimen/Roboto). From e8606051954a109be7ba30fb693fa3e2e550fc11 Mon Sep 17 00:00:00 2001 From: Fabrizio Cucci Date: Tue, 19 May 2026 16:09:14 +0100 Subject: [PATCH 24/94] feat(checkbox): add error state via MD3 error tokens Adds an `error?: boolean` prop to Checkbox, CheckboxAndroid and CheckboxIOS. When true, the outline (unchecked) and container (checked / indeterminate) use `theme.colors.error`. The `disabled` state and explicit `color` / `uncheckedColor` overrides take precedence. Addresses one bullet from #4937 / #4949 (Checkbox section, "Error state not implemented"). Verified visually on iOS Simulator and Android Emulator across light and dark themes. --- src/components/Checkbox/Checkbox.tsx | 7 + src/components/Checkbox/CheckboxAndroid.tsx | 9 ++ src/components/Checkbox/CheckboxIOS.tsx | 8 ++ src/components/Checkbox/utils.ts | 32 ++++- .../__tests__/Checkbox/utils.test.tsx | 123 ++++++++++++++++++ 5 files changed, 177 insertions(+), 2 deletions(-) diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index b6cfd9a64b..a035e951fa 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -27,6 +27,13 @@ export type Props = { * Custom color for checkbox. */ color?: string; + /** + * Whether the checkbox is in an error state. When true, the outline + * (unchecked) and container (checked / indeterminate) use + * `theme.colors.error`. `disabled` and explicit `color`/`uncheckedColor` + * overrides take precedence. + */ + error?: boolean; /** * @optional */ diff --git a/src/components/Checkbox/CheckboxAndroid.tsx b/src/components/Checkbox/CheckboxAndroid.tsx index 8a93cc6a70..d73bdf57c5 100644 --- a/src/components/Checkbox/CheckboxAndroid.tsx +++ b/src/components/Checkbox/CheckboxAndroid.tsx @@ -34,6 +34,13 @@ export type Props = $RemoveChildren & { * Custom color for checkbox. */ color?: ColorValue; + /** + * Whether the checkbox is in an error state. When true, the outline + * (unchecked) and container (checked / indeterminate) use + * `theme.colors.error`. `disabled` and explicit `color`/`uncheckedColor` + * overrides take precedence. + */ + error?: boolean; /** * @optional */ @@ -60,6 +67,7 @@ const CheckboxAndroid = ({ disabled, onPress, testID, + error, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); @@ -107,6 +115,7 @@ const CheckboxAndroid = ({ checked, customColor: rest.color, customUncheckedColor: rest.uncheckedColor, + error, }); const borderWidth = scaleAnim.interpolate({ diff --git a/src/components/Checkbox/CheckboxIOS.tsx b/src/components/Checkbox/CheckboxIOS.tsx index d4a8127180..5830359372 100644 --- a/src/components/Checkbox/CheckboxIOS.tsx +++ b/src/components/Checkbox/CheckboxIOS.tsx @@ -29,6 +29,12 @@ export type Props = $RemoveChildren & { * Custom color for checkbox. */ color?: ColorValue; + /** + * Whether the checkbox is in an error state. When true, the checked / + * indeterminate icon uses `theme.colors.error`. `disabled` and explicit + * `color` overrides take precedence. + */ + error?: boolean; /** * @optional */ @@ -52,6 +58,7 @@ const CheckboxIOS = ({ onPress, theme: themeOverrides, testID, + error, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); @@ -62,6 +69,7 @@ const CheckboxIOS = ({ theme, disabled, customColor: rest.color, + error, }); const icon = indeterminate ? 'minus' : 'check'; diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index f47457151b..87362df00c 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -8,28 +8,40 @@ const { stateOpacity } = tokens.md.ref; const getAndroidCheckedColor = ({ theme, customColor, + error, }: { theme: InternalTheme; customColor?: ColorValue; + error?: boolean; }) => { if (customColor) { return customColor; } + if (error) { + return theme.colors.error; + } + return theme.colors.primary; }; const getAndroidUncheckedColor = ({ theme, customUncheckedColor, + error, }: { theme: InternalTheme; customUncheckedColor?: ColorValue; + error?: boolean; }) => { if (customUncheckedColor) { return customUncheckedColor; } + if (error) { + return theme.colors.error; + } + return theme.colors.onSurfaceVariant; }; @@ -62,17 +74,20 @@ export const getAndroidSelectionControlColor = ({ checked, customColor, customUncheckedColor, + error, }: { theme: InternalTheme; checked: boolean; disabled?: boolean; customColor?: ColorValue; customUncheckedColor?: ColorValue; + error?: boolean; }) => { - const checkedColor = getAndroidCheckedColor({ theme, customColor }); + const checkedColor = getAndroidCheckedColor({ theme, customColor, error }); const uncheckedColor = getAndroidUncheckedColor({ theme, customUncheckedColor, + error, }); const selectionControlOpacity = disabled ? stateOpacity.disabled @@ -94,10 +109,12 @@ const getIOSCheckedColor = ({ theme, disabled, customColor, + error, }: { theme: InternalTheme; customColor?: ColorValue; disabled?: boolean; + error?: boolean; }) => { if (disabled) { return theme.colors.primary; @@ -107,6 +124,10 @@ const getIOSCheckedColor = ({ return customColor; } + if (error) { + return theme.colors.error; + } + return theme.colors.primary; }; @@ -114,12 +135,19 @@ export const getSelectionControlIOSColor = ({ theme, disabled, customColor, + error, }: { theme: InternalTheme; disabled?: boolean; customColor?: ColorValue; + error?: boolean; }) => { - const checkedColor = getIOSCheckedColor({ theme, disabled, customColor }); + const checkedColor = getIOSCheckedColor({ + theme, + disabled, + customColor, + error, + }); const checkedColorOpacity = disabled ? stateOpacity.disabled : stateOpacity.enabled; diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index a18dd2f78e..2aa20fd688 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -87,6 +87,82 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { selectionControlColor: getTheme(false).colors.onSurfaceVariant, }); }); + + it('should return error color, checked, when error is true', () => { + expect( + getAndroidSelectionControlColor({ + theme: getTheme(), + checked: true, + error: true, + }) + ).toMatchObject({ + selectionControlColor: getTheme().colors.error, + }); + }); + + it('should return error color, unchecked, when error is true', () => { + expect( + getAndroidSelectionControlColor({ + theme: getTheme(), + checked: false, + error: true, + }) + ).toMatchObject({ + selectionControlColor: getTheme().colors.error, + }); + }); + + it('should return error color, checked, dark mode, when error is true', () => { + expect( + getAndroidSelectionControlColor({ + theme: getTheme(true), + checked: true, + error: true, + }) + ).toMatchObject({ + selectionControlColor: getTheme(true).colors.error, + }); + }); + + it('should return disabled color when both disabled and error are true (disabled wins)', () => { + expect( + getAndroidSelectionControlColor({ + theme: getTheme(), + disabled: true, + checked: true, + error: true, + }) + ).toMatchObject({ + selectionControlColor: getTheme().colors.onSurface, + selectionControlOpacity: stateOpacity.disabled, + }); + }); + + it('should return custom color when both customColor and error are true, checked (customColor wins)', () => { + expect( + getAndroidSelectionControlColor({ + theme: getTheme(), + checked: true, + customColor: 'purple', + error: true, + }) + ).toMatchObject({ + selectionControlColor: 'purple', + }); + }); + + it('should return custom unchecked color when both customUncheckedColor and error are true, unchecked (customUncheckedColor wins)', () => { + expect( + getAndroidSelectionControlColor({ + theme: getTheme(), + checked: false, + customUncheckedColor: 'purple', + error: true, + }) + ).toMatchObject({ + selectionControlColor: 'purple', + }); + }); }); describe('getSelectionControlIOSColor - checked color', () => { @@ -122,4 +198,51 @@ describe('getSelectionControlIOSColor - checked color', () => { checkedColor: getTheme().colors.primary, }); }); + + it('should return error color when error is true', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + error: true, + }) + ).toMatchObject({ + checkedColor: getTheme().colors.error, + }); + }); + + it('should return error color, dark mode, when error is true', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(true), + error: true, + }) + ).toMatchObject({ + checkedColor: getTheme(true).colors.error, + }); + }); + + it('should return disabled color when both disabled and error are true (disabled wins)', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + disabled: true, + error: true, + }) + ).toMatchObject({ + checkedColor: getTheme().colors.primary, + checkedColorOpacity: stateOpacity.disabled, + }); + }); + + it('should return custom color when both customColor and error are true (customColor wins)', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + customColor: 'purple', + error: true, + }) + ).toMatchObject({ + checkedColor: 'purple', + }); + }); }); From 6d92d77598b61e9d257952c0d3caa24513afebc8 Mon Sep 17 00:00:00 2001 From: Fabrizio Cucci Date: Thu, 21 May 2026 09:51:51 +0100 Subject: [PATCH 25/94] refactor(checkbox): unify platform-specific files (use Android impl) Per maintainer feedback on #4955, drop the iOS-specific Checkbox renderer and inline the unified MD3 implementation directly into Checkbox.tsx. The same MaterialCommunityIcon-based control now renders on both platforms. Changes: - Checkbox.tsx: inlines what was the Android implementation; renders the same control on both platforms. Drops the Platform.OS dispatching. - CheckboxAndroid.tsx, CheckboxIOS.tsx: deleted. - Checkbox/index.ts: `Checkbox.Android` and `Checkbox.IOS` are kept as back-compat aliases of `Checkbox` itself, so existing imports keep working with no breaking change. - CheckboxItem.tsx: drops the `mode`-based branching; always renders ``. The `mode` prop is kept and marked deprecated. - src/index.tsx: `CheckboxAndroidProps` and `CheckboxIOSProps` now alias the unified `Props` from Checkbox.tsx (no breaking change). - docs/docusaurus.config.js: removed entries for the now-deleted CheckboxAndroid and CheckboxIOS files. - Snapshot tests updated: the renderer now uses the unified container (36x36 ripple) and MaterialCommunityIcon glyphs on both platforms. `getSelectionControlIOSColor` in utils.ts is intentionally left in place because RadioButtonIOS still depends on it. A similar unification for RadioButton can follow in a separate PR. As a side benefit, callers using `` will now see the prop applied (the old iOS variant silently dropped it), addressing one of the API-parity gaps noted in #4949. --- docs/docusaurus.config.js | 2 - example/src/Examples/CheckboxItemExample.tsx | 17 +- src/components/Checkbox/Checkbox.tsx | 149 ++++++- src/components/Checkbox/CheckboxAndroid.tsx | 194 --------- src/components/Checkbox/CheckboxIOS.tsx | 116 ----- src/components/Checkbox/CheckboxItem.tsx | 18 +- src/components/Checkbox/index.ts | 6 - src/components/Checkbox/utils.ts | 67 +-- .../RadioButton/RadioButtonAndroid.tsx | 4 +- src/components/RadioButton/RadioButtonIOS.tsx | 2 +- src/components/RadioButton/utils.ts | 60 ++- .../__tests__/Checkbox/CheckboxItem.test.tsx | 21 - .../__snapshots__/Checkbox.test.tsx.snap | 281 +++++++++++- .../__snapshots__/CheckboxItem.test.tsx.snap | 409 ++---------------- .../__tests__/Checkbox/utils.test.tsx | 115 +---- .../__tests__/RadioButton/utils.test.tsx | 87 ++++ src/index.tsx | 2 - 17 files changed, 622 insertions(+), 928 deletions(-) delete mode 100644 src/components/Checkbox/CheckboxAndroid.tsx delete mode 100644 src/components/Checkbox/CheckboxIOS.tsx create mode 100644 src/components/__tests__/RadioButton/utils.test.tsx diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 1dec1bb6ec..bc75f57c94 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -92,8 +92,6 @@ const config = { }, Checkbox: { Checkbox: 'Checkbox/Checkbox', - CheckboxAndroid: 'Checkbox/CheckboxAndroid', - CheckboxIOS: 'Checkbox/CheckboxIOS', CheckboxItem: 'Checkbox/CheckboxItem', }, Chip: { diff --git a/example/src/Examples/CheckboxItemExample.tsx b/example/src/Examples/CheckboxItemExample.tsx index acafd709b7..7e7c0751b8 100644 --- a/example/src/Examples/CheckboxItemExample.tsx +++ b/example/src/Examples/CheckboxItemExample.tsx @@ -7,8 +7,6 @@ import ScreenWrapper from '../ScreenWrapper'; const CheckboxExample = () => { const [checkedDefault, setCheckedDefault] = React.useState(true); - const [checkedAndroid, setCheckedAndroid] = React.useState(true); - const [checkedIOS, setCheckedIOS] = React.useState(true); const [checkedLeadingControl, setCheckedLeadingControl] = React.useState(true); const [checkedDisabled, setCheckedDisabled] = React.useState(true); @@ -17,27 +15,14 @@ const CheckboxExample = () => { return ( setCheckedDefault(!checkedDefault)} /> - setCheckedAndroid(!checkedAndroid)} - /> - setCheckedIOS(!checkedIOS)} - /> setCheckedLeadingControl(!checkedLeadingControl)} - mode="ios" position="leading" /> & { /** * Status of checkbox. */ @@ -22,11 +29,11 @@ export type Props = { /** * Custom color for unchecked checkbox. */ - uncheckedColor?: string; + uncheckedColor?: ColorValue; /** * Custom color for checkbox. */ - color?: string; + color?: ColorValue; /** * Whether the checkbox is in an error state. When true, the outline * (unchecked) and container (checked / indeterminate) use @@ -44,6 +51,8 @@ export type Props = { testID?: string; }; +const ANIMATION_DURATION = 100; + /** * Checkboxes allow the selection of multiple options from a set. * @@ -68,15 +77,131 @@ export type Props = { * export default MyComponent; * ``` */ -const Checkbox = ({ theme: themeOverrides, ...props }: Props) => { +const Checkbox = ({ + status, + theme: themeOverrides, + disabled, + onPress, + testID, + error, + ...rest +}: Props) => { const theme = useInternalTheme(themeOverrides); - return Platform.OS === 'ios' ? ( - - ) : ( - + const { current: scaleAnim } = React.useRef( + new Animated.Value(1) + ); + const isFirstRendering = React.useRef(true); + + const { + animation: { scale }, + } = theme; + + React.useEffect(() => { + // Do not run animation on very first rendering + if (isFirstRendering.current) { + isFirstRendering.current = false; + return; + } + + const checked = status === 'checked'; + + Animated.sequence([ + Animated.timing(scaleAnim, { + toValue: 0.85, + duration: checked ? ANIMATION_DURATION * scale : 0, + useNativeDriver: false, + }), + Animated.timing(scaleAnim, { + toValue: 1, + duration: checked + ? ANIMATION_DURATION * scale + : ANIMATION_DURATION * scale * 1.75, + useNativeDriver: false, + }), + ]).start(); + }, [status, scaleAnim, scale]); + + const checked = status === 'checked'; + const indeterminate = status === 'indeterminate'; + + const { selectionControlColor, selectionControlOpacity } = + getSelectionControlColor({ + theme, + disabled, + checked, + customColor: rest.color, + customUncheckedColor: rest.uncheckedColor, + error, + }); + + const borderWidth = scaleAnim.interpolate({ + inputRange: [0.8, 1], + outputRange: [7, 0], + }); + + const icon = indeterminate + ? 'minus-box' + : checked + ? 'checkbox-marked' + : 'checkbox-blank-outline'; + + return ( + + + + + + + + ); }; +const styles = StyleSheet.create({ + container: { + borderRadius: 18, + width: 36, + height: 36, + padding: 6, + }, + fillContainer: { + alignItems: 'center', + justifyContent: 'center', + }, + fill: { + height: 14, + width: 14, + }, +}); + export default Checkbox; // @component-docs ignore-next-line diff --git a/src/components/Checkbox/CheckboxAndroid.tsx b/src/components/Checkbox/CheckboxAndroid.tsx deleted file mode 100644 index d73bdf57c5..0000000000 --- a/src/components/Checkbox/CheckboxAndroid.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import * as React from 'react'; -import { - Animated, - ColorValue, - GestureResponderEvent, - StyleSheet, - View, -} from 'react-native'; - -import { getAndroidSelectionControlColor } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../../types'; -import MaterialCommunityIcon from '../MaterialCommunityIcon'; -import TouchableRipple from '../TouchableRipple/TouchableRipple'; - -export type Props = $RemoveChildren & { - /** - * Status of checkbox. - */ - status: 'checked' | 'unchecked' | 'indeterminate'; - /** - * Whether checkbox is disabled. - */ - disabled?: boolean; - /** - * Function to execute on press. - */ - onPress?: (e: GestureResponderEvent) => void; - /** - * Custom color for unchecked checkbox. - */ - uncheckedColor?: ColorValue; - /** - * Custom color for checkbox. - */ - color?: ColorValue; - /** - * Whether the checkbox is in an error state. When true, the outline - * (unchecked) and container (checked / indeterminate) use - * `theme.colors.error`. `disabled` and explicit `color`/`uncheckedColor` - * overrides take precedence. - */ - error?: boolean; - /** - * @optional - */ - theme?: ThemeProp; - /** - * testID to be used on tests. - */ - testID?: string; -}; - -// From https://material.io/design/motion/speed.html#duration -const ANIMATION_DURATION = 100; - -/** - * Checkboxes allow the selection of multiple options from a set. - * This component follows platform guidelines for Android, but can be used - * on any platform. - * - * @extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple - */ -const CheckboxAndroid = ({ - status, - theme: themeOverrides, - disabled, - onPress, - testID, - error, - ...rest -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const { current: scaleAnim } = React.useRef( - new Animated.Value(1) - ); - const isFirstRendering = React.useRef(true); - - const { - animation: { scale }, - } = theme; - - React.useEffect(() => { - // Do not run animation on very first rendering - if (isFirstRendering.current) { - isFirstRendering.current = false; - return; - } - - const checked = status === 'checked'; - - Animated.sequence([ - Animated.timing(scaleAnim, { - toValue: 0.85, - duration: checked ? ANIMATION_DURATION * scale : 0, - useNativeDriver: false, - }), - Animated.timing(scaleAnim, { - toValue: 1, - duration: checked - ? ANIMATION_DURATION * scale - : ANIMATION_DURATION * scale * 1.75, - useNativeDriver: false, - }), - ]).start(); - }, [status, scaleAnim, scale]); - - const checked = status === 'checked'; - const indeterminate = status === 'indeterminate'; - - const { selectionControlColor, selectionControlOpacity } = - getAndroidSelectionControlColor({ - theme, - disabled, - checked, - customColor: rest.color, - customUncheckedColor: rest.uncheckedColor, - error, - }); - - const borderWidth = scaleAnim.interpolate({ - inputRange: [0.8, 1], - outputRange: [7, 0], - }); - - const icon = indeterminate - ? 'minus-box' - : checked - ? 'checkbox-marked' - : 'checkbox-blank-outline'; - - return ( - - - - - - - - - ); -}; - -CheckboxAndroid.displayName = 'Checkbox.Android'; - -const styles = StyleSheet.create({ - container: { - borderRadius: 18, - width: 36, - height: 36, - padding: 6, - }, - fillContainer: { - alignItems: 'center', - justifyContent: 'center', - }, - fill: { - height: 14, - width: 14, - }, -}); - -export default CheckboxAndroid; - -// @component-docs ignore-next-line -export { CheckboxAndroid }; diff --git a/src/components/Checkbox/CheckboxIOS.tsx b/src/components/Checkbox/CheckboxIOS.tsx deleted file mode 100644 index 5830359372..0000000000 --- a/src/components/Checkbox/CheckboxIOS.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import * as React from 'react'; -import { - ColorValue, - GestureResponderEvent, - StyleSheet, - View, -} from 'react-native'; - -import { getSelectionControlIOSColor } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../../types'; -import MaterialCommunityIcon from '../MaterialCommunityIcon'; -import TouchableRipple from '../TouchableRipple/TouchableRipple'; - -export type Props = $RemoveChildren & { - /** - * Status of checkbox. - */ - status: 'checked' | 'unchecked' | 'indeterminate'; - /** - * Whether checkbox is disabled. - */ - disabled?: boolean; - /** - * Function to execute on press. - */ - onPress?: (e: GestureResponderEvent) => void; - /** - * Custom color for checkbox. - */ - color?: ColorValue; - /** - * Whether the checkbox is in an error state. When true, the checked / - * indeterminate icon uses `theme.colors.error`. `disabled` and explicit - * `color` overrides take precedence. - */ - error?: boolean; - /** - * @optional - */ - theme?: ThemeProp; - /** - * testID to be used on tests. - */ - testID?: string; -}; - -/** - * Checkboxes allow the selection of multiple options from a set. - * This component follows platform guidelines for iOS, but can be used - * on any platform. - * - * @extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple - */ -const CheckboxIOS = ({ - status, - disabled, - onPress, - theme: themeOverrides, - testID, - error, - ...rest -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const checked = status === 'checked'; - const indeterminate = status === 'indeterminate'; - - const { checkedColor } = getSelectionControlIOSColor({ - theme, - disabled, - customColor: rest.color, - error, - }); - - const icon = indeterminate ? 'minus' : 'check'; - const opacity = indeterminate || checked ? 1 : 0; - - return ( - - - - - - ); -}; - -CheckboxIOS.displayName = 'Checkbox.IOS'; - -const styles = StyleSheet.create({ - container: { - borderRadius: 18, - padding: 6, - }, -}); - -export default CheckboxIOS; - -// @component-docs ignore-next-line -export { CheckboxIOS }; diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 9037137e7a..c0935ffb7a 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -10,8 +10,6 @@ import { } from 'react-native'; import Checkbox from './Checkbox'; -import CheckboxAndroid from './CheckboxAndroid'; -import CheckboxIOS from './CheckboxIOS'; import { useInternalTheme } from '../../core/theming'; import { tokens } from '../../theme/tokens'; import type { ThemeProp, TypescaleKey } from '../../types'; @@ -99,11 +97,6 @@ export type Props = { * Checkbox control position. */ position?: 'leading' | 'trailing'; - /** - * Whether `` or `` should be used. - * Left undefined `` will be used. - */ - mode?: 'android' | 'ios'; /** * Sets additional distance outside of element in which a press can be detected. */ @@ -138,7 +131,6 @@ const CheckboxItem = ({ labelStyle, theme: themeOverrides, testID, - mode, position = 'trailing', accessibilityLabel = label, disabled, @@ -151,15 +143,7 @@ const CheckboxItem = ({ const theme = useInternalTheme(themeOverrides); const checkboxProps = { ...props, status, theme, disabled }; const isLeading = position === 'leading'; - let checkbox; - - if (mode === 'android') { - checkbox = ; - } else if (mode === 'ios') { - checkbox = ; - } else { - checkbox = ; - } + const checkbox = ; const textColor = theme.colors.onSurface; const textAlign = isLeading ? 'right' : 'left'; diff --git a/src/components/Checkbox/index.ts b/src/components/Checkbox/index.ts index 54a65538c8..32274f97d9 100644 --- a/src/components/Checkbox/index.ts +++ b/src/components/Checkbox/index.ts @@ -1,6 +1,4 @@ import CheckboxComponent from './Checkbox'; -import CheckboxAndroid from './CheckboxAndroid'; -import CheckboxIOS from './CheckboxIOS'; import CheckboxItem from './CheckboxItem'; const Checkbox = Object.assign( @@ -9,10 +7,6 @@ const Checkbox = Object.assign( { // @component ./CheckboxItem.tsx Item: CheckboxItem, - // @component ./CheckboxAndroid.tsx - Android: CheckboxAndroid, - // @component ./CheckboxIOS.tsx - IOS: CheckboxIOS, } ); diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index 87362df00c..1314193174 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -5,7 +5,7 @@ import type { InternalTheme } from '../../types'; const { stateOpacity } = tokens.md.ref; -const getAndroidCheckedColor = ({ +const getCheckedColor = ({ theme, customColor, error, @@ -25,7 +25,7 @@ const getAndroidCheckedColor = ({ return theme.colors.primary; }; -const getAndroidUncheckedColor = ({ +const getUncheckedColor = ({ theme, customUncheckedColor, error, @@ -45,7 +45,7 @@ const getAndroidUncheckedColor = ({ return theme.colors.onSurfaceVariant; }; -const getAndroidControlColor = ({ +const getControlColor = ({ theme, checked, disabled, @@ -68,7 +68,7 @@ const getAndroidControlColor = ({ return uncheckedColor; }; -export const getAndroidSelectionControlColor = ({ +export const getSelectionControlColor = ({ theme, disabled, checked, @@ -83,8 +83,8 @@ export const getAndroidSelectionControlColor = ({ customUncheckedColor?: ColorValue; error?: boolean; }) => { - const checkedColor = getAndroidCheckedColor({ theme, customColor, error }); - const uncheckedColor = getAndroidUncheckedColor({ + const checkedColor = getCheckedColor({ theme, customColor, error }); + const uncheckedColor = getUncheckedColor({ theme, customUncheckedColor, error, @@ -94,7 +94,7 @@ export const getAndroidSelectionControlColor = ({ : stateOpacity.enabled; return { - selectionControlColor: getAndroidControlColor({ + selectionControlColor: getControlColor({ theme, disabled, checked, @@ -104,56 +104,3 @@ export const getAndroidSelectionControlColor = ({ selectionControlOpacity, }; }; - -const getIOSCheckedColor = ({ - theme, - disabled, - customColor, - error, -}: { - theme: InternalTheme; - customColor?: ColorValue; - disabled?: boolean; - error?: boolean; -}) => { - if (disabled) { - return theme.colors.primary; - } - - if (customColor) { - return customColor; - } - - if (error) { - return theme.colors.error; - } - - return theme.colors.primary; -}; - -export const getSelectionControlIOSColor = ({ - theme, - disabled, - customColor, - error, -}: { - theme: InternalTheme; - disabled?: boolean; - customColor?: ColorValue; - error?: boolean; -}) => { - const checkedColor = getIOSCheckedColor({ - theme, - disabled, - customColor, - error, - }); - const checkedColorOpacity = disabled - ? stateOpacity.disabled - : stateOpacity.enabled; - - return { - checkedColor, - checkedColorOpacity, - }; -}; diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index 4b68455101..cb1757726b 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -5,7 +5,7 @@ import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; import { handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, ThemeProp } from '../../types'; -import { getAndroidSelectionControlColor } from '../Checkbox/utils'; +import { getSelectionControlColor } from '../Checkbox/utils'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; export type Props = $RemoveChildren & { @@ -110,7 +110,7 @@ const RadioButtonAndroid = ({ value, }) === 'checked'; - const { selectionControlColor } = getAndroidSelectionControlColor({ + const { selectionControlColor } = getSelectionControlColor({ theme, disabled, checked, diff --git a/src/components/RadioButton/RadioButtonIOS.tsx b/src/components/RadioButton/RadioButtonIOS.tsx index defd7cb932..d5d8df7090 100644 --- a/src/components/RadioButton/RadioButtonIOS.tsx +++ b/src/components/RadioButton/RadioButtonIOS.tsx @@ -3,9 +3,9 @@ import { GestureResponderEvent, StyleSheet, View } from 'react-native'; import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; import { handlePress, isChecked } from './utils'; +import { getSelectionControlIOSColor } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, ThemeProp } from '../../types'; -import { getSelectionControlIOSColor } from '../Checkbox/utils'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/RadioButton/utils.ts b/src/components/RadioButton/utils.ts index 18eca2a0d7..9cd776ad8d 100644 --- a/src/components/RadioButton/utils.ts +++ b/src/components/RadioButton/utils.ts @@ -1,4 +1,9 @@ -import type { GestureResponderEvent } from 'react-native'; +import type { ColorValue, GestureResponderEvent } from 'react-native'; + +import { tokens } from '../../theme/tokens'; +import type { InternalTheme } from '../../types'; + +const { stateOpacity } = tokens.md.ref; export const handlePress = ({ onPress, @@ -35,3 +40,56 @@ export const isChecked = ({ return status; } }; + +const getIOSCheckedColor = ({ + theme, + disabled, + customColor, + error, +}: { + theme: InternalTheme; + customColor?: ColorValue; + disabled?: boolean; + error?: boolean; +}) => { + if (disabled) { + return theme.colors.primary; + } + + if (customColor) { + return customColor; + } + + if (error) { + return theme.colors.error; + } + + return theme.colors.primary; +}; + +export const getSelectionControlIOSColor = ({ + theme, + disabled, + customColor, + error, +}: { + theme: InternalTheme; + disabled?: boolean; + customColor?: ColorValue; + error?: boolean; +}) => { + const checkedColor = getIOSCheckedColor({ + theme, + disabled, + customColor, + error, + }); + const checkedColorOpacity = disabled + ? stateOpacity.disabled + : stateOpacity.enabled; + + return { + checkedColor, + checkedColorOpacity, + }; +}; diff --git a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx index 28546d16e5..7018d01c38 100644 --- a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx +++ b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { Platform } from 'react-native'; import { act, fireEvent } from '@testing-library/react-native'; @@ -14,31 +13,11 @@ it('renders unchecked', () => { expect(tree).toMatchSnapshot(); }); -it('can render the iOS checkbox on different platforms', () => { - Platform.OS = 'android'; - const tree = render( - - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('can render the Android checkbox on different platforms', () => { - Platform.OS = 'ios'; - const tree = render( - - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - it('can render leading checkbox control', () => { - Platform.OS = 'ios'; const tree = render( ).toJSON(); diff --git a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap index 9e6deed50f..f1a7834ac6 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap @@ -40,16 +40,24 @@ exports[`renders Checkbox with custom testID 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } testID="custom:testID" > @@ -81,8 +89,37 @@ exports[`renders Checkbox with custom testID 1`] = ` ] } > - check + checkbox-marked
+ + + `; @@ -128,15 +165,23 @@ exports[`renders checked Checkbox with color 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } > @@ -168,8 +213,37 @@ exports[`renders checked Checkbox with color 1`] = ` ] } > - check + checkbox-marked
+ + + `; @@ -214,15 +288,23 @@ exports[`renders checked Checkbox with onPress 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } > @@ -254,8 +336,37 @@ exports[`renders checked Checkbox with onPress 1`] = ` ] } > - check + checkbox-marked
+ + + `; @@ -300,15 +411,23 @@ exports[`renders indeterminate Checkbox 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } > @@ -321,7 +440,7 @@ exports[`renders indeterminate Checkbox 1`] = ` style={ [ { - "color": "rgba(103, 80, 164, 1)", + "color": "rgba(73, 69, 79, 1)", "fontSize": 24, }, [ @@ -340,8 +459,37 @@ exports[`renders indeterminate Checkbox 1`] = ` ] } > - minus + minus-box + + + `; @@ -387,15 +535,23 @@ exports[`renders indeterminate Checkbox with color 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } > @@ -408,7 +564,7 @@ exports[`renders indeterminate Checkbox with color 1`] = ` style={ [ { - "color": "red", + "color": "rgba(73, 69, 79, 1)", "fontSize": 24, }, [ @@ -427,8 +583,37 @@ exports[`renders indeterminate Checkbox with color 1`] = ` ] } > - minus + minus-box + + + `; @@ -474,15 +659,23 @@ exports[`renders unchecked Checkbox with color 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } > @@ -514,8 +707,37 @@ exports[`renders unchecked Checkbox with color 1`] = ` ] } > - check + checkbox-marked + + + `; @@ -560,15 +782,23 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } > @@ -581,7 +811,7 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` style={ [ { - "color": "rgba(103, 80, 164, 1)", + "color": "rgba(73, 69, 79, 1)", "fontSize": 24, }, [ @@ -600,8 +830,37 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` ] } > - check + checkbox-blank-outline + + + `; diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index 2615640570..8f8faef29b 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -56,225 +56,6 @@ exports[`can render leading checkbox control 1`] = ` ] } > - - - - check - - - - - Default with leading control - - - -`; - -exports[`can render the Android checkbox on different platforms 1`] = ` - - - - iOS Checkbox - - - -`; - -exports[`can render the iOS checkbox on different platforms 1`] = ` - - - iOS Checkbox + Default with leading control - - - - check - - - `; @@ -713,15 +351,23 @@ exports[`renders unchecked 1`] = ` }, { "borderRadius": 18, + "height": 36, "padding": 6, + "width": 36, }, ] } > @@ -734,7 +380,7 @@ exports[`renders unchecked 1`] = ` style={ [ { - "color": "rgba(103, 80, 164, 1)", + "color": "rgba(73, 69, 79, 1)", "fontSize": 24, }, [ @@ -753,8 +399,37 @@ exports[`renders unchecked 1`] = ` ] } > - check + checkbox-blank-outline + + + diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index 2aa20fd688..118fe1690f 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,15 +1,12 @@ import { getTheme } from '../../../core/theming'; import { tokens } from '../../../theme/tokens'; -import { - getAndroidSelectionControlColor, - getSelectionControlIOSColor, -} from '../../Checkbox/utils'; +import { getSelectionControlColor } from '../../Checkbox/utils'; const { stateOpacity } = tokens.md.ref; -describe('getAndroidSelectionControlColor - checkbox color', () => { +describe('getSelectionControlColor - checkbox color', () => { it('should return correct disabled color, for theme version 3', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), disabled: true, checked: false, @@ -22,7 +19,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return custom color, checked', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: true, customColor: 'purple', @@ -34,7 +31,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return theme color, for theme version 3, checked', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: true, }) @@ -45,7 +42,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return custom color, unchecked', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: false, customUncheckedColor: 'purple', @@ -57,7 +54,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return theme color, for theme version 3, unchecked', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: false, }) @@ -68,7 +65,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return theme color, unchecked, dark mode', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(true), checked: false, }) @@ -79,7 +76,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return theme color, unchecked, light mode', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(false), checked: false, }) @@ -90,7 +87,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return error color, checked, when error is true', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: true, error: true, @@ -102,7 +99,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return error color, unchecked, when error is true', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: false, error: true, @@ -114,7 +111,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return error color, checked, dark mode, when error is true', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(true), checked: true, error: true, @@ -126,7 +123,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return disabled color when both disabled and error are true (disabled wins)', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), disabled: true, checked: true, @@ -140,7 +137,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return custom color when both customColor and error are true, checked (customColor wins)', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: true, customColor: 'purple', @@ -153,7 +150,7 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { it('should return custom unchecked color when both customUncheckedColor and error are true, unchecked (customUncheckedColor wins)', () => { expect( - getAndroidSelectionControlColor({ + getSelectionControlColor({ theme: getTheme(), checked: false, customUncheckedColor: 'purple', @@ -164,85 +161,3 @@ describe('getAndroidSelectionControlColor - checkbox color', () => { }); }); }); - -describe('getSelectionControlIOSColor - checked color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(), - disabled: true, - }) - ).toMatchObject({ - checkedColor: getTheme().colors.primary, - checkedColorOpacity: stateOpacity.disabled, - }); - }); - - it('should return custom color, checked', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(), - customColor: 'purple', - }) - ).toMatchObject({ - checkedColor: 'purple', - }); - }); - - it('should return theme color, for theme version 3, checked', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(), - }) - ).toMatchObject({ - checkedColor: getTheme().colors.primary, - }); - }); - - it('should return error color when error is true', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(), - error: true, - }) - ).toMatchObject({ - checkedColor: getTheme().colors.error, - }); - }); - - it('should return error color, dark mode, when error is true', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(true), - error: true, - }) - ).toMatchObject({ - checkedColor: getTheme(true).colors.error, - }); - }); - - it('should return disabled color when both disabled and error are true (disabled wins)', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(), - disabled: true, - error: true, - }) - ).toMatchObject({ - checkedColor: getTheme().colors.primary, - checkedColorOpacity: stateOpacity.disabled, - }); - }); - - it('should return custom color when both customColor and error are true (customColor wins)', () => { - expect( - getSelectionControlIOSColor({ - theme: getTheme(), - customColor: 'purple', - error: true, - }) - ).toMatchObject({ - checkedColor: 'purple', - }); - }); -}); diff --git a/src/components/__tests__/RadioButton/utils.test.tsx b/src/components/__tests__/RadioButton/utils.test.tsx new file mode 100644 index 0000000000..929d0f7a2c --- /dev/null +++ b/src/components/__tests__/RadioButton/utils.test.tsx @@ -0,0 +1,87 @@ +import { getTheme } from '../../../core/theming'; +import { tokens } from '../../../theme/tokens'; +import { getSelectionControlIOSColor } from '../../RadioButton/utils'; + +const { stateOpacity } = tokens.md.ref; + +describe('getSelectionControlIOSColor - checked color', () => { + it('should return correct disabled color, for theme version 3', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + disabled: true, + }) + ).toMatchObject({ + checkedColor: getTheme().colors.primary, + checkedColorOpacity: stateOpacity.disabled, + }); + }); + + it('should return custom color, checked', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + customColor: 'purple', + }) + ).toMatchObject({ + checkedColor: 'purple', + }); + }); + + it('should return theme color, for theme version 3, checked', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + }) + ).toMatchObject({ + checkedColor: getTheme().colors.primary, + }); + }); + + it('should return error color when error is true', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + error: true, + }) + ).toMatchObject({ + checkedColor: getTheme().colors.error, + }); + }); + + it('should return error color, dark mode, when error is true', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(true), + error: true, + }) + ).toMatchObject({ + checkedColor: getTheme(true).colors.error, + }); + }); + + it('should return disabled color when both disabled and error are true (disabled wins)', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + disabled: true, + error: true, + }) + ).toMatchObject({ + checkedColor: getTheme().colors.primary, + checkedColorOpacity: stateOpacity.disabled, + }); + }); + + it('should return custom color when both customColor and error are true (customColor wins)', () => { + expect( + getSelectionControlIOSColor({ + theme: getTheme(), + customColor: 'purple', + error: true, + }) + ).toMatchObject({ + checkedColor: 'purple', + }); + }); +}); diff --git a/src/index.tsx b/src/index.tsx index 1430e797c4..b5ecadea41 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -81,8 +81,6 @@ export type { Props as CardContentProps } from './components/Card/CardContent'; export type { Props as CardCoverProps } from './components/Card/CardCover'; export type { Props as CardTitleProps } from './components/Card/CardTitle'; export type { Props as CheckboxProps } from './components/Checkbox/Checkbox'; -export type { Props as CheckboxAndroidProps } from './components/Checkbox/CheckboxAndroid'; -export type { Props as CheckboxIOSProps } from './components/Checkbox/CheckboxIOS'; export type { Props as CheckboxItemProps } from './components/Checkbox/CheckboxItem'; export type { Props as ChipProps } from './components/Chip/Chip'; export type { Props as DataTableProps } from './components/DataTable/DataTable'; From 1026d26d5d3f4b5d95db1b7f2e861e7b92b4f718 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Sun, 24 May 2026 13:56:20 +0200 Subject: [PATCH 26/94] chore: configure release-it for prerelease --- .release-it.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.release-it.json b/.release-it.json index 869746da21..141b5d1193 100644 --- a/.release-it.json +++ b/.release-it.json @@ -4,10 +4,12 @@ "tagName": "v${version}" }, "npm": { - "publish": true + "publish": true, + "tag": "alpha" }, "github": { - "release": true + "release": true, + "preRelease": true }, "plugins": { "@release-it/conventional-changelog": { From 33815155239b8fce9ad7faeab35a8407955f1ce3 Mon Sep 17 00:00:00 2001 From: Aziz Becha Date: Tue, 26 May 2026 08:58:52 +0100 Subject: [PATCH 27/94] fix: resolve RN 0.85 web warnings and upgrade Expo SDK 56 (#4958) --- example/package.json | 12 +- example/src/Examples/BannerExample.tsx | 2 +- .../src/Examples/BottomNavigationExample.tsx | 2 +- src/components/Appbar/AppbarBackIcon.tsx | 9 +- src/components/Icon.tsx | 2 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 16 +- src/theme/tokens/sys/elevation.ts | 36 +- yarn.lock | 639 +++++++++++------- 8 files changed, 447 insertions(+), 271 deletions(-) diff --git a/example/package.json b/example/package.json index 8edec0e291..36603ea4aa 100644 --- a/example/package.json +++ b/example/package.json @@ -17,21 +17,21 @@ "@react-navigation/drawer": "^8.0.0-alpha.31", "@react-navigation/native": "^8.0.0-alpha.25", "@react-navigation/native-stack": "^8.0.0-alpha.31", - "expo": "~56.0.0-preview.11", + "expo": "~56.0.0", "expo-crypto": "~56.0.3", - "expo-dev-client": "~56.0.9", - "expo-font": "~56.0.3", + "expo-dev-client": "~56.0.13", + "expo-font": "~56.0.5", "expo-keep-awake": "~56.0.3", - "expo-splash-screen": "~56.0.5", + "expo-splash-screen": "~56.0.9", "expo-status-bar": "~56.0.4", - "expo-updates": "~56.0.10", + "expo-updates": "~56.0.14", "react": "19.2.3", "react-dom": "19.2.3", "react-native": "0.85.3", "react-native-gesture-handler": "~2.31.1", "react-native-reanimated": "4.3.1", "react-native-safe-area-context": "~5.7.0", - "react-native-screens": "4.25.0", + "react-native-screens": "4.25.1", "react-native-web": "^0.21.0", "react-native-worklets": "0.8.3" }, diff --git a/example/src/Examples/BannerExample.tsx b/example/src/Examples/BannerExample.tsx index b9bd08315b..85e7ec986d 100644 --- a/example/src/Examples/BannerExample.tsx +++ b/example/src/Examples/BannerExample.tsx @@ -49,6 +49,7 @@ const BannerExample = () => { @@ -131,7 +132,6 @@ const styles = StyleSheet.create({ }, photo: { flex: 1, - resizeMode: 'cover', }, fab: { alignSelf: 'center', diff --git a/example/src/Examples/BottomNavigationExample.tsx b/example/src/Examples/BottomNavigationExample.tsx index 7a0e20c51d..e12bb58ad1 100644 --- a/example/src/Examples/BottomNavigationExample.tsx +++ b/example/src/Examples/BottomNavigationExample.tsx @@ -38,6 +38,7 @@ const PhotoGallery = ({ route }: Route) => { @@ -185,7 +186,6 @@ const styles = StyleSheet.create({ }), photo: { flex: 1, - resizeMode: 'cover', }, screen: { flex: 1, diff --git a/src/components/Appbar/AppbarBackIcon.tsx b/src/components/Appbar/AppbarBackIcon.tsx index 763e859412..8981fc979f 100644 --- a/src/components/Appbar/AppbarBackIcon.tsx +++ b/src/components/Appbar/AppbarBackIcon.tsx @@ -28,10 +28,8 @@ const AppbarBackIcon = ({ > @@ -50,9 +48,6 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, - icon: { - resizeMode: 'contain', - }, }); export default AppbarBackIcon; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 8790b454ac..d24a6caeec 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -130,6 +130,7 @@ const Icon = ({ {...rest} testID={testID} source={s} + resizeMode="contain" style={[ { transform: [{ scaleX: direction === 'rtl' ? -1 : 1 }], @@ -138,7 +139,6 @@ const Icon = ({ width: size, height: size, tintColor: color, - resizeMode: `contain`, }, ]} {...accessibilityProps} diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 9880395ec2..7dbcf9f0a5 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -625,22 +625,18 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A > diff --git a/src/theme/tokens/sys/elevation.ts b/src/theme/tokens/sys/elevation.ts index f02be56f29..f6793771f3 100644 --- a/src/theme/tokens/sys/elevation.ts +++ b/src/theme/tokens/sys/elevation.ts @@ -1,7 +1,9 @@ // M3 elevation tokens and shadow builder per spec: // https://m3.material.io/styles/elevation/tokens -import { Animated, type ColorValue } from 'react-native'; +import { Animated, Platform, type ColorValue } from 'react-native'; + +import color from 'color'; import { isAnimatedValue } from '../../../utils/animations'; import type { Elevation, ThemeElevation } from '../../types'; @@ -32,10 +34,42 @@ export const shadowLayers = [ }, ]; +const getShadowColor = (shadowColor: ColorValue, shadowOpacity: number) => { + if (typeof shadowColor !== 'string') { + throw new Error( + `Expected a string shadow color on Web, but received a ${typeof shadowColor}.` + ); + } + + return color(shadowColor).alpha(shadowOpacity).rgb().string(); +}; + +const getBoxShadowValue = (elevation: number, shadowColor: string) => + `0px ${shadowLayers[0].height[elevation]}px ${shadowLayers[0].shadowRadius[elevation]}px ${shadowColor}`; + export function shadow( elevation: number | Animated.Value = 0, shadowColor: ColorValue ) { + if (Platform.OS === 'web') { + const webShadowColor = getShadowColor(shadowColor, 0.3); + + if (isAnimatedValue(elevation)) { + return { + boxShadow: elevation.interpolate({ + inputRange: elevationInputRange, + outputRange: elevationInputRange.map((value) => + getBoxShadowValue(value, webShadowColor) + ), + }), + }; + } + + return { + boxShadow: getBoxShadowValue(elevation, webShadowColor), + }; + } + if (isAnimatedValue(elevation)) { return { shadowColor, diff --git a/yarn.lock b/yarn.lock index 4f0e4a39ce..bc18b34207 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3671,36 +3671,35 @@ __metadata: languageName: node linkType: hard -"@expo/cli@npm:^56.1.4": - version: 56.1.4 - resolution: "@expo/cli@npm:56.1.4" +"@expo/cli@npm:^56.1.7": + version: 56.1.7 + resolution: "@expo/cli@npm:56.1.7" dependencies: "@expo/code-signing-certificates": "npm:^0.0.6" - "@expo/config": "npm:~56.0.5" - "@expo/config-plugins": "npm:~56.0.5" + "@expo/config": "npm:~56.0.8" + "@expo/config-plugins": "npm:~56.0.7" "@expo/devcert": "npm:^1.2.1" - "@expo/env": "npm:~2.2.0" - "@expo/image-utils": "npm:^0.9.2" - "@expo/inline-modules": "npm:^0.0.7" - "@expo/json-file": "npm:^10.1.0" - "@expo/log-box": "npm:^56.0.9" + "@expo/env": "npm:~2.3.0" + "@expo/image-utils": "npm:^0.10.0" + "@expo/inline-modules": "npm:^0.0.9" + "@expo/json-file": "npm:^10.2.0" + "@expo/log-box": "npm:^56.0.12" "@expo/metro": "npm:~56.0.0" - "@expo/metro-config": "npm:~56.0.8" - "@expo/metro-file-map": "npm:^56.0.2" - "@expo/osascript": "npm:^2.5.0" - "@expo/package-manager": "npm:^1.11.0" - "@expo/plist": "npm:^0.6.0" - "@expo/prebuild-config": "npm:^56.0.7" - "@expo/require-utils": "npm:^56.1.0" - "@expo/router-server": "npm:^56.0.8" + "@expo/metro-config": "npm:~56.0.11" + "@expo/metro-file-map": "npm:^56.0.3" + "@expo/osascript": "npm:^2.6.0" + "@expo/package-manager": "npm:^1.12.0" + "@expo/plist": "npm:^0.7.0" + "@expo/prebuild-config": "npm:^56.0.10" + "@expo/require-utils": "npm:^56.1.2" + "@expo/router-server": "npm:^56.0.10" "@expo/schema-utils": "npm:^56.0.0" - "@expo/spawn-async": "npm:^1.7.2" + "@expo/spawn-async": "npm:^1.8.0" "@expo/ws-tunnel": "npm:^1.0.1" "@expo/xcpretty": "npm:^4.4.4" "@react-native/dev-middleware": "npm:0.85.3" accepts: "npm:^1.3.8" arg: "npm:^5.0.2" - better-opn: "npm:~3.0.2" bplist-creator: "npm:0.1.0" bplist-parser: "npm:^0.3.1" chalk: "npm:^4.0.0" @@ -3709,7 +3708,7 @@ __metadata: connect: "npm:^3.7.0" debug: "npm:^4.3.4" dnssd-advertise: "npm:^1.1.4" - expo-server: "npm:^56.0.2" + expo-server: "npm:^56.0.4" fetch-nodeshim: "npm:^0.4.10" getenv: "npm:^2.0.0" glob: "npm:^13.0.0" @@ -3744,7 +3743,7 @@ __metadata: optional: true bin: expo-internal: main.js - checksum: 10c0/34f0f8f5230a0264df3265ca63ed4c78cbae49bd1e05ebbf6c23e4baef4813489c43d1cd069aa7fc98c2f1a6329e7b4c7120ca0952f4cb32b3e26172b85baaeb + checksum: 10c0/d85fd954379263537d514d1649c54d12af0b3a0157f5a19e34545268e93edc32ca38410324955046bc83b4f6bc6cd576b367f955585350c2fb5784413951379d languageName: node linkType: hard @@ -3757,49 +3756,49 @@ __metadata: languageName: node linkType: hard -"@expo/config-plugins@npm:~56.0.5": - version: 56.0.5 - resolution: "@expo/config-plugins@npm:56.0.5" +"@expo/config-plugins@npm:~56.0.7": + version: 56.0.7 + resolution: "@expo/config-plugins@npm:56.0.7" dependencies: - "@expo/config-types": "npm:^56.0.4" - "@expo/json-file": "npm:~10.1.0" - "@expo/plist": "npm:^0.6.0" + "@expo/config-types": "npm:^56.0.5" + "@expo/json-file": "npm:~10.2.0" + "@expo/plist": "npm:^0.7.0" + "@expo/require-utils": "npm:^56.1.2" "@expo/sdk-runtime-versions": "npm:^1.0.0" chalk: "npm:^4.1.2" debug: "npm:^4.3.5" getenv: "npm:^2.0.0" glob: "npm:^13.0.0" - resolve-from: "npm:^5.0.0" semver: "npm:^7.5.4" slugify: "npm:^1.6.6" xcode: "npm:^3.0.1" xml2js: "npm:0.6.0" - checksum: 10c0/cd5b6e7e5fffaa93f56523980d79a933ab2be1db1e1125e0270ad85baa84ea6bfa2d7d658cbe8e18b586b71a036c1d40f55a5869f91015cc50d34e4424f4c6d1 + checksum: 10c0/072883de80ddba355c159685e5c5cd92036e8a5069349bc2c93ccfcfa7458c36d3268d316ed86ae553a508d838e1fe4ba40058a00580f4f38191c328a1acf30c languageName: node linkType: hard -"@expo/config-types@npm:^56.0.4": - version: 56.0.4 - resolution: "@expo/config-types@npm:56.0.4" - checksum: 10c0/125026c105411bc58c1f49a3eaf45f2261d0d85b71bd8a5197309f201d91f4c7d92cd92260994623b4275bd4baac9bf0536c5991e0c9ca4598e87d158012d102 +"@expo/config-types@npm:^56.0.5": + version: 56.0.5 + resolution: "@expo/config-types@npm:56.0.5" + checksum: 10c0/b6aa98e3cb4b66954e307640550a168290d1e843c9fdedce64077e286d77cfd7e33c40333672d6434c32ad5468fa723e251bc49897fd606f7c1b985dc2170a1d languageName: node linkType: hard -"@expo/config@npm:~56.0.5": - version: 56.0.5 - resolution: "@expo/config@npm:56.0.5" +"@expo/config@npm:~56.0.8": + version: 56.0.8 + resolution: "@expo/config@npm:56.0.8" dependencies: - "@expo/config-plugins": "npm:~56.0.5" - "@expo/config-types": "npm:^56.0.4" - "@expo/json-file": "npm:^10.1.0" - "@expo/require-utils": "npm:^56.1.0" + "@expo/config-plugins": "npm:~56.0.7" + "@expo/config-types": "npm:^56.0.5" + "@expo/json-file": "npm:^10.2.0" + "@expo/require-utils": "npm:^56.1.2" deepmerge: "npm:^4.3.1" getenv: "npm:^2.0.0" glob: "npm:^13.0.0" resolve-workspace-root: "npm:^2.0.0" semver: "npm:^7.6.0" slugify: "npm:^1.3.4" - checksum: 10c0/6770cd142fdfa070837791b446805ebdc09dc359cb5b232b14989b8e81e8b3ad7151a93f707d910054ea8131bf8609a747fbcc8967e30ed9681b154b28bc39bb + checksum: 10c0/faee70cfc2a04f90203914d35058094d00eb1cbfa94d88efaec38fa7bdd934dc23cbdc8eedde9945d8b77ed758566403a1f5da6a599408d6c77df7f4b30cd2e9 languageName: node linkType: hard @@ -3841,30 +3840,30 @@ __metadata: languageName: node linkType: hard -"@expo/env@npm:^2.2.0, @expo/env@npm:~2.2.0": - version: 2.2.1 - resolution: "@expo/env@npm:2.2.1" +"@expo/env@npm:^2.3.0, @expo/env@npm:~2.3.0": + version: 2.3.0 + resolution: "@expo/env@npm:2.3.0" dependencies: chalk: "npm:^4.0.0" debug: "npm:^4.3.4" getenv: "npm:^2.0.0" - checksum: 10c0/5ae0be6ffe419a9fb42174aabca18a5a8fc8b12ea874b258d097785324e9236ed40d517aef756e7f7eceb30af1090a291e4bc5283cd5d96a79332eaaec38fd02 + checksum: 10c0/2a930a86a12daa63503ca250dfe8a45fe060f1f9b2e272a0aef83924cb103f7dc0ebbe6ebe6b067ecb369fafdcb7e017bd88bb161b5d54465b8da014fda89f84 languageName: node linkType: hard -"@expo/expo-modules-macros-plugin@npm:~0.0.8": +"@expo/expo-modules-macros-plugin@npm:~0.0.9": version: 0.0.9 resolution: "@expo/expo-modules-macros-plugin@npm:0.0.9" checksum: 10c0/ed48100a888052338437d3165111b72099a9a415975e507a43cdc89f4dd26defca3a15621223b62b816ba4c12ecf95697c70a001698b69139897288c801aebad languageName: node linkType: hard -"@expo/fingerprint@npm:^0.18.1": - version: 0.18.1 - resolution: "@expo/fingerprint@npm:0.18.1" +"@expo/fingerprint@npm:^0.19.0": + version: 0.19.0 + resolution: "@expo/fingerprint@npm:0.19.0" dependencies: - "@expo/env": "npm:^2.2.0" - "@expo/spawn-async": "npm:^1.7.2" + "@expo/env": "npm:^2.3.0" + "@expo/spawn-async": "npm:^1.8.0" arg: "npm:^5.0.2" chalk: "npm:^4.1.2" debug: "npm:^4.3.4" @@ -3876,57 +3875,57 @@ __metadata: semver: "npm:^7.6.0" bin: fingerprint: bin/cli.js - checksum: 10c0/737488c8511f27d438b5c8a61d3f09e621efc9e16cd83699de199d78afe117d129b8cfd133a9e6233a00c286630e25d4abfe0b739353e50836269c55b86930b2 + checksum: 10c0/197acde4a297b75177859397303a4fba465d2dc0fc1e48c063cde92e57292ee9b0cf83cebb77af19aa7f1aebca357ae69b4070b7955240290c4712eedf348bac languageName: node linkType: hard -"@expo/image-utils@npm:^0.9.2": - version: 0.9.2 - resolution: "@expo/image-utils@npm:0.9.2" +"@expo/image-utils@npm:^0.10.0": + version: 0.10.0 + resolution: "@expo/image-utils@npm:0.10.0" dependencies: - "@expo/require-utils": "npm:^56.1.0" - "@expo/spawn-async": "npm:^1.7.2" + "@expo/require-utils": "npm:^56.1.2" + "@expo/spawn-async": "npm:^1.8.0" chalk: "npm:^4.0.0" getenv: "npm:^2.0.0" jimp-compact: "npm:0.16.1" parse-png: "npm:^2.1.0" semver: "npm:^7.6.0" - checksum: 10c0/3d91da454a09de07b42aed3d1f6ce7f010fba9f794e648f2d6ef62ab34c3939582b2ea43228029c18a5459d0073ab2ec25965a2b66d6ffde9f314aff010689a0 + checksum: 10c0/62ea44fb7c598c8dc88cad16294018a3e87a86bc9c723ef15c6ce43e44176b0251ec2255e99048d4cde5fd770f3bc45ffc23dd631884e7cd02a10b7f6a34af3d languageName: node linkType: hard -"@expo/inline-modules@npm:^0.0.7": - version: 0.0.7 - resolution: "@expo/inline-modules@npm:0.0.7" +"@expo/inline-modules@npm:^0.0.9": + version: 0.0.9 + resolution: "@expo/inline-modules@npm:0.0.9" dependencies: - "@expo/config-plugins": "npm:~56.0.5" - checksum: 10c0/8b03ebf1e49769d5f4cc7610e300abb8630eabccd3919f90e8979304cca63af846e6ea0ec38c494154079e130b5a52ed7685aa374ce2cc7a2a7f0d463cc8e4cd + "@expo/config-plugins": "npm:~56.0.7" + checksum: 10c0/7129b0c05cd444b2a0d1db365c37a9523bfc65730ca1c1720a9fd002dad0c6fa98d8560a082821276684751ff9dbf3ff7112e9dded707693f47d5b98dbd41e5a languageName: node linkType: hard -"@expo/json-file@npm:^10.1.0, @expo/json-file@npm:~10.1.0": - version: 10.1.1 - resolution: "@expo/json-file@npm:10.1.1" +"@expo/json-file@npm:^10.2.0, @expo/json-file@npm:~10.2.0": + version: 10.2.0 + resolution: "@expo/json-file@npm:10.2.0" dependencies: "@babel/code-frame": "npm:^7.20.0" json5: "npm:^2.2.3" - checksum: 10c0/7cc6f0b115e166b2cc7fa1676e254a71479a503b6a40b95e1948bf0d85743769b3a8e169d5fe34ce3bbd3f0d7690142895bf4e7fe0e82d5bf95db8a57a5f1b77 + checksum: 10c0/198058e18dea2f31083c2ae8a6831dddfc8fc01c4cb30020728da04f155a6b600b4219830b6df48195548fa29a450b5b775007ed8430fb8098fd9a1656188ea0 languageName: node linkType: hard -"@expo/local-build-cache-provider@npm:^56.0.5": - version: 56.0.5 - resolution: "@expo/local-build-cache-provider@npm:56.0.5" +"@expo/local-build-cache-provider@npm:^56.0.7": + version: 56.0.7 + resolution: "@expo/local-build-cache-provider@npm:56.0.7" dependencies: - "@expo/config": "npm:~56.0.5" + "@expo/config": "npm:~56.0.8" chalk: "npm:^4.1.2" - checksum: 10c0/23b4e2e6a8f66bc952a23766e52f074ca68a87749761a4a55d54ebeb1f483f3c12c64ca5d8986da21e0d3f811b905a0851e9fa5caba5ae36e94133a97befcdde + checksum: 10c0/284eb6f662850550cb8018117e0751f6a0e63acf3b468f3b5e3af2ede6dfc0f866e7dd0f752d8db861130cb168b32cd90a3748c2f627a6a6cb0c950e4d2259de languageName: node linkType: hard -"@expo/log-box@npm:^56.0.9": - version: 56.0.9 - resolution: "@expo/log-box@npm:56.0.9" +"@expo/log-box@npm:^56.0.12": + version: 56.0.12 + resolution: "@expo/log-box@npm:56.0.12" dependencies: "@expo/dom-webview": "npm:^56.0.5" anser: "npm:^1.4.9" @@ -3936,22 +3935,22 @@ __metadata: expo: "*" react: "*" react-native: "*" - checksum: 10c0/096cf49183bb05ad7b4623e47bc5c8fcb2583889ecd4e23f488c7f003ea4bb7f078ea86af937beda563bb271b9cb14c3cb47c9c22a5fc5890ccb83955e65ef5a + checksum: 10c0/454d9f16df1921cad6afe8bf008693971d50c1f3ef7271a9ced86b69a4f5ec570d4781141731ce9306c7a222dd7ddd22712de85a208479bf4df028f81113a33e languageName: node linkType: hard -"@expo/metro-config@npm:~56.0.8": - version: 56.0.8 - resolution: "@expo/metro-config@npm:56.0.8" +"@expo/metro-config@npm:~56.0.11": + version: 56.0.11 + resolution: "@expo/metro-config@npm:56.0.11" dependencies: "@babel/code-frame": "npm:^7.20.0" "@babel/core": "npm:^7.20.0" "@babel/generator": "npm:^7.20.5" - "@expo/config": "npm:~56.0.5" - "@expo/env": "npm:~2.2.0" - "@expo/json-file": "npm:~10.1.0" + "@expo/config": "npm:~56.0.8" + "@expo/env": "npm:~2.3.0" + "@expo/json-file": "npm:~10.2.0" "@expo/metro": "npm:~56.0.0" - "@expo/spawn-async": "npm:^1.7.2" + "@expo/spawn-async": "npm:^1.8.0" "@jridgewell/gen-mapping": "npm:^0.3.13" "@jridgewell/remapping": "npm:^2.3.5" "@jridgewell/sourcemap-codec": "npm:^1.5.5" @@ -3963,6 +3962,7 @@ __metadata: hermes-parser: "npm:^0.33.3" jsc-safe-url: "npm:^0.2.4" lightningcss: "npm:^1.30.1" + msgpackr: "npm:^2.0.1" picomatch: "npm:^4.0.4" postcss: "npm:^8.5.14" resolve-from: "npm:^5.0.0" @@ -3971,13 +3971,13 @@ __metadata: peerDependenciesMeta: expo: optional: true - checksum: 10c0/7b0e4f33f1e6d0fa571255f70985d5e0b8e5d383a6172bec794e11d0fa7109585a9507c10fee7cc401f073cc59e9f0746f9f021a2b3bce66db8ae8b5098ee3f1 + checksum: 10c0/2031f0040c216b850c89801769fa6a31aef5c8f6329d66bdafd7cd7032f4372bb992d59e91667aed57a559075b58ab63c48100a41efcdf6d80950d11df4cdfc8 languageName: node linkType: hard -"@expo/metro-file-map@npm:^56.0.2": - version: 56.0.2 - resolution: "@expo/metro-file-map@npm:56.0.2" +"@expo/metro-file-map@npm:^56.0.3": + version: 56.0.3 + resolution: "@expo/metro-file-map@npm:56.0.3" dependencies: debug: "npm:^4.3.4" fb-watchman: "npm:^2.0.2" @@ -3985,7 +3985,7 @@ __metadata: jest-worker: "npm:^29.7.0" micromatch: "npm:^4.0.4" walker: "npm:^1.0.8" - checksum: 10c0/d0a064565b9aceb4a5016b417d25922fee19eb4a3e9db58d216a3afe8f8a5cebfa3e9a569eb162a7a400f0a1c1495022b45ae55d3b151fac4a4167babaac6998 + checksum: 10c0/457e751c7a2824788494dc456579b6badde852c1ebc22a37231f5f6caf543e51155c276b6283ffd751e430e7300847cd8c85c7cc5e630469b09513ead4d97e9a languageName: node linkType: hard @@ -4011,61 +4011,61 @@ __metadata: languageName: node linkType: hard -"@expo/osascript@npm:^2.5.0": - version: 2.5.1 - resolution: "@expo/osascript@npm:2.5.1" +"@expo/osascript@npm:^2.6.0": + version: 2.6.0 + resolution: "@expo/osascript@npm:2.6.0" dependencies: - "@expo/spawn-async": "npm:^1.7.2" - checksum: 10c0/01d9f058d76470b5a859f0bcf1adc29838731a4af4bdd878b5dd997044d8d3b7b2d264f080427e58891977b615b713aa05ea40240e239e2802cfbff85ad57ba2 + "@expo/spawn-async": "npm:^1.8.0" + checksum: 10c0/1d5f6440ea97e0ece1e46c8a54c35ad793ef942c53268f9535df7c7caac330e01114426c6780dacf5b716d5ee96bfdeaa7a924b7522eb18edcf9cf3b309d2421 languageName: node linkType: hard -"@expo/package-manager@npm:^1.11.0": - version: 1.11.1 - resolution: "@expo/package-manager@npm:1.11.1" +"@expo/package-manager@npm:^1.12.0": + version: 1.12.0 + resolution: "@expo/package-manager@npm:1.12.0" dependencies: - "@expo/json-file": "npm:^10.1.0" - "@expo/spawn-async": "npm:^1.7.2" + "@expo/json-file": "npm:^10.2.0" + "@expo/spawn-async": "npm:^1.8.0" chalk: "npm:^4.0.0" npm-package-arg: "npm:^11.0.0" ora: "npm:^3.4.0" resolve-workspace-root: "npm:^2.0.0" - checksum: 10c0/a67e42fa0ac4c760a370e27ea8607d840161b878910b07b3636e2a3d27991a300110f0e4407b81b4fde35115c3c874b907821d4819b3d8db6c4ee63aea7ab604 + checksum: 10c0/e6060b67ae4557594a31b5dd259c0a322945a07df72e59d78a96059806756adccb67db32a210941cc49de47d0ac07f68dd4adc5d308d97f8d23f8daadc3db4b2 languageName: node linkType: hard -"@expo/plist@npm:^0.6.0": - version: 0.6.1 - resolution: "@expo/plist@npm:0.6.1" +"@expo/plist@npm:^0.7.0": + version: 0.7.0 + resolution: "@expo/plist@npm:0.7.0" dependencies: "@xmldom/xmldom": "npm:^0.8.8" base64-js: "npm:^1.5.1" xmlbuilder: "npm:^15.1.1" - checksum: 10c0/d7b29b954db57d849f2c8ad26c150b0d25807d48e9a86e19310c9d94e9cb173bad9783836b47ff4cd5369b90c51b7dc3393027b74148e7f6cee22652f3880ee5 + checksum: 10c0/b0d3df057f9a388a761a9261381d55e86ad0935ca15d32bd78da536c803bfc9334c2381fdced99f086e76f5d675535101bfb652d9a79a39c10b0c764c140dcc9 languageName: node linkType: hard -"@expo/prebuild-config@npm:^56.0.7": - version: 56.0.7 - resolution: "@expo/prebuild-config@npm:56.0.7" +"@expo/prebuild-config@npm:^56.0.10": + version: 56.0.10 + resolution: "@expo/prebuild-config@npm:56.0.10" dependencies: - "@expo/config": "npm:~56.0.5" - "@expo/config-plugins": "npm:~56.0.5" - "@expo/config-types": "npm:^56.0.4" - "@expo/image-utils": "npm:^0.9.2" - "@expo/json-file": "npm:^10.1.0" + "@expo/config": "npm:~56.0.8" + "@expo/config-plugins": "npm:~56.0.7" + "@expo/config-types": "npm:^56.0.5" + "@expo/image-utils": "npm:^0.10.0" + "@expo/json-file": "npm:^10.2.0" "@react-native/normalize-colors": "npm:0.85.3" debug: "npm:^4.3.1" - expo-modules-autolinking: "npm:~56.0.6" + expo-modules-autolinking: "npm:~56.0.9" resolve-from: "npm:^5.0.0" semver: "npm:^7.6.0" - checksum: 10c0/f8df4c9ef8def4dec6e287a45fd6598e3ad012328a9e1dea37319d4f5d09450a202a69b120be5789623b1ee4f7ef78ea570cb32361f8b9bfd5117ed32ebe480d + checksum: 10c0/04c40196d6d0b5744dc6d3a09c4cdfa46b800f4288173b64cebbabff8bad32cd86a37d0a616161afc054c4bc9decef3c8b19d3e831f4e358dbc2890f4efaa090 languageName: node linkType: hard -"@expo/require-utils@npm:^56.1.0": - version: 56.1.0 - resolution: "@expo/require-utils@npm:56.1.0" +"@expo/require-utils@npm:^56.1.2": + version: 56.1.2 + resolution: "@expo/require-utils@npm:56.1.2" dependencies: "@babel/code-frame": "npm:^7.20.0" "@babel/core": "npm:^7.25.2" @@ -4075,22 +4075,22 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/13bfefa0498ed80e290174c094a96b81859bad1351522abf9ba95ffb6c142b28f6501477915c4c6610fad2adac6eb95b3e2686cd32bdaba1fe1e852d9a3341a2 + checksum: 10c0/c0396d9129831430959b83ed5e376e7d86a76678ab66f53998109fe73eedf6366b3147f64d5229a3c9773d506f0ebd24a79a24fc73dfada7d169d02d0ef5967f languageName: node linkType: hard -"@expo/router-server@npm:^56.0.8": - version: 56.0.8 - resolution: "@expo/router-server@npm:56.0.8" +"@expo/router-server@npm:^56.0.10": + version: 56.0.10 + resolution: "@expo/router-server@npm:56.0.10" dependencies: debug: "npm:^4.3.4" peerDependencies: - "@expo/metro-runtime": ^56.0.8 + "@expo/metro-runtime": ^56.0.10 expo: "*" - expo-constants: ^56.0.10 - expo-font: ^56.0.3 + expo-constants: ^56.0.13 + expo-font: ^56.0.5 expo-router: "*" - expo-server: ^56.0.2 + expo-server: ^56.0.4 react: "*" react-dom: "*" react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 @@ -4103,7 +4103,7 @@ __metadata: optional: true react-server-dom-webpack: optional: true - checksum: 10c0/cc24ec95a652b303865e7e57809eb6f154c3702cdeb082dfd6eb0fd5b23306227b349ff6456174e34c2a53d6d9d8a675647a6760e954b267260a48fc0be0fccb + checksum: 10c0/e50a3f33f1f002b30e8ee2ac6b742cc53b38e80ec0e0a6c81e938e11d12e7c900db7f879159dc1f36127e12085e0219f8c52e52df971ccff78063ed15fdf4334 languageName: node linkType: hard @@ -4121,12 +4121,12 @@ __metadata: languageName: node linkType: hard -"@expo/spawn-async@npm:^1.7.2": - version: 1.7.2 - resolution: "@expo/spawn-async@npm:1.7.2" +"@expo/spawn-async@npm:^1.8.0": + version: 1.8.0 + resolution: "@expo/spawn-async@npm:1.8.0" dependencies: - cross-spawn: "npm:^7.0.3" - checksum: 10c0/0548c4e95ee39393c2f3919bc605f21eba4f0a8ba66fa82fbbc4b1b624e0054526918489227b924f03af5bc156a011f39a2472c223c0d2237fb7afd8dedd5357 + cross-spawn: "npm:^7.0.6" + checksum: 10c0/08d3c63f9cc097ce9c8cf6850ca482fd7999a6fddc4cb38a3a9915a1662cb674fe7353de2eb3c693728542bf57db732ae433e82b2d698be141d07cea3092ebf3 languageName: node linkType: hard @@ -4674,6 +4674,48 @@ __metadata: languageName: node linkType: hard +"@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.3" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.3" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.3" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.3": + version: 3.0.3 + resolution: "@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@napi-rs/wasm-runtime@npm:^0.2.3": version: 0.2.12 resolution: "@napi-rs/wasm-runtime@npm:0.2.12" @@ -7818,7 +7860,7 @@ __metadata: languageName: node linkType: hard -"babel-preset-expo@npm:~56.0.0, babel-preset-expo@npm:~56.0.8": +"babel-preset-expo@npm:~56.0.0": version: 56.0.8 resolution: "babel-preset-expo@npm:56.0.8" dependencies: @@ -7880,6 +7922,68 @@ __metadata: languageName: node linkType: hard +"babel-preset-expo@npm:~56.0.10": + version: 56.0.10 + resolution: "babel-preset-expo@npm:56.0.10" + dependencies: + "@babel/generator": "npm:^7.20.5" + "@babel/helper-module-imports": "npm:^7.25.9" + "@babel/plugin-proposal-decorators": "npm:^7.12.9" + "@babel/plugin-proposal-export-default-from": "npm:^7.24.7" + "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + "@babel/plugin-syntax-export-default-from": "npm:^7.24.7" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/plugin-transform-async-generator-functions": "npm:^7.25.4" + "@babel/plugin-transform-async-to-generator": "npm:^7.24.7" + "@babel/plugin-transform-block-scoping": "npm:^7.25.0" + "@babel/plugin-transform-class-properties": "npm:^7.25.4" + "@babel/plugin-transform-class-static-block": "npm:^7.27.1" + "@babel/plugin-transform-classes": "npm:^7.25.4" + "@babel/plugin-transform-destructuring": "npm:^7.24.8" + "@babel/plugin-transform-export-namespace-from": "npm:^7.25.9" + "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2" + "@babel/plugin-transform-for-of": "npm:^7.24.7" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7" + "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7" + "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" + "@babel/plugin-transform-parameters": "npm:^7.24.7" + "@babel/plugin-transform-private-methods": "npm:^7.24.7" + "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7" + "@babel/plugin-transform-react-display-name": "npm:^7.24.7" + "@babel/plugin-transform-react-jsx": "npm:^7.28.6" + "@babel/plugin-transform-react-jsx-development": "npm:^7.27.1" + "@babel/plugin-transform-react-pure-annotations": "npm:^7.27.1" + "@babel/plugin-transform-runtime": "npm:^7.24.7" + "@babel/plugin-transform-typescript": "npm:^7.25.2" + "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" + "@babel/preset-typescript": "npm:^7.23.0" + "@react-native/babel-plugin-codegen": "npm:0.85.3" + babel-plugin-react-compiler: "npm:^1.0.0" + babel-plugin-react-native-web: "npm:~0.21.0" + babel-plugin-syntax-hermes-parser: "npm:^0.33.3" + babel-plugin-transform-flow-enums: "npm:^0.0.2" + debug: "npm:^4.3.4" + peerDependencies: + "@babel/runtime": ^7.20.0 + expo: "*" + expo-widgets: ^56.0.11 + react-refresh: ">=0.14.0 <1.0.0" + peerDependenciesMeta: + "@babel/runtime": + optional: true + expo: + optional: true + expo-widgets: + optional: true + checksum: 10c0/2db26378df4138ad48a92fb62c25a2a8d0ec7cbf4b49024e662121bead9ed9dd7a39cb9ce8c758554c22f148d52876f925486e336d3570c9f48d4cefce513282 + languageName: node + linkType: hard + "babel-preset-jest@npm:^29.6.3": version: 29.6.3 resolution: "babel-preset-jest@npm:29.6.3" @@ -8062,15 +8166,6 @@ __metadata: languageName: node linkType: hard -"better-opn@npm:~3.0.2": - version: 3.0.2 - resolution: "better-opn@npm:3.0.2" - dependencies: - open: "npm:^8.0.4" - checksum: 10c0/911ef25d44da75aabfd2444ce7a4294a8000ebcac73068c04a60298b0f7c7506b60421aa4cd02ac82502fb42baaff7e4892234b51e6923eded44c5a11185f2f5 - languageName: node - linkType: hard - "big-integer@npm:1.6.x": version: 1.6.52 resolution: "big-integer@npm:1.6.52" @@ -10601,7 +10696,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.3": +"detect-libc@npm:^2.0.1, detect-libc@npm:^2.0.3": version: 2.1.2 resolution: "detect-libc@npm:2.1.2" checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 @@ -11860,29 +11955,29 @@ __metadata: languageName: node linkType: hard -"expo-asset@npm:~56.0.9": - version: 56.0.9 - resolution: "expo-asset@npm:56.0.9" +"expo-asset@npm:~56.0.12": + version: 56.0.12 + resolution: "expo-asset@npm:56.0.12" dependencies: - "@expo/image-utils": "npm:^0.9.2" - expo-constants: "npm:~56.0.10" + "@expo/image-utils": "npm:^0.10.0" + expo-constants: "npm:~56.0.13" peerDependencies: expo: "*" react: "*" react-native: "*" - checksum: 10c0/166523acf70ae6d814b31e364e4b462534ecf76b637e66ab2e32f57f3fa125594d4b13da7be21cbce0080f81d49eedc518231b51d68a6e941e628c5ddbf76dad + checksum: 10c0/be5cdea96f3152aa9d8b48f83e1d2b80895da2c7a66063feff5194fe808237d63cc20293f21a9b170e144bba50dc9ff5bf335e7b0a66e56e45ccf8c3a3692679 languageName: node linkType: hard -"expo-constants@npm:~56.0.10": - version: 56.0.10 - resolution: "expo-constants@npm:56.0.10" +"expo-constants@npm:~56.0.13": + version: 56.0.13 + resolution: "expo-constants@npm:56.0.13" dependencies: - "@expo/env": "npm:~2.2.0" + "@expo/env": "npm:~2.3.0" peerDependencies: expo: "*" react-native: "*" - checksum: 10c0/409be4d3e002f7f2b085452104cc96b3d8c6497ab89690a2d132bcd7ad103dda68bd0c511914cae63fa6019556bd82c1930c39164836d8e7a2244bbf080307dc + checksum: 10c0/bb6648ab0cf146f95c20a2bb52d25cc38259a201ef6e85e04a8568318ee732f7c42b97e6fb7e4797c147c012f6839e1836629a03819aef44013ee73144d91bd0 languageName: node linkType: hard @@ -11895,32 +11990,32 @@ __metadata: languageName: node linkType: hard -"expo-dev-client@npm:~56.0.9": - version: 56.0.10 - resolution: "expo-dev-client@npm:56.0.10" +"expo-dev-client@npm:~56.0.13": + version: 56.0.13 + resolution: "expo-dev-client@npm:56.0.13" dependencies: - expo-dev-launcher: "npm:~56.0.10" - expo-dev-menu: "npm:~56.0.9" + expo-dev-launcher: "npm:~56.0.13" + expo-dev-menu: "npm:~56.0.12" expo-dev-menu-interface: "npm:~56.0.0" - expo-manifests: "npm:~56.0.3" + expo-manifests: "npm:~56.0.4" expo-updates-interface: "npm:~56.0.1" peerDependencies: expo: "*" - checksum: 10c0/3c9c96e3a35670bc418c845c38ace7c609856f9cef37423d10f6800bf6638ad51b44237a7de1276f721a065f041f1de64e0efe90993b599d3e764b5d85759fcb + checksum: 10c0/937138497f5d789d83a67969e04ce8dea034f460f1d19a1425e2aa6c39fa81eb5d5c873f7fd9c8b1639728f7be572c528c0201a3ca81837a090aa9fbf966e9a5 languageName: node linkType: hard -"expo-dev-launcher@npm:~56.0.10": - version: 56.0.10 - resolution: "expo-dev-launcher@npm:56.0.10" +"expo-dev-launcher@npm:~56.0.13": + version: 56.0.13 + resolution: "expo-dev-launcher@npm:56.0.13" dependencies: "@expo/schema-utils": "npm:^56.0.0" - expo-dev-menu: "npm:~56.0.9" - expo-manifests: "npm:~56.0.3" + expo-dev-menu: "npm:~56.0.12" + expo-manifests: "npm:~56.0.4" peerDependencies: expo: "*" react-native: "*" - checksum: 10c0/5774a1e526518a7d466128b87525371c0ffb1272640764695d05c021e786376d729e8be9496d5a87387c5b2279d59d5b79ac4898656f0765e1956189b377b028 + checksum: 10c0/daf92e91cd727e68c951574f1572ee7fbf0394ebaf7d3cda09fa9ba5b0b4aba217bc76104a1abb1d96bd3cf200c851455ca3805b0757478b3797f09e5f62a171 languageName: node linkType: hard @@ -11933,15 +12028,15 @@ __metadata: languageName: node linkType: hard -"expo-dev-menu@npm:~56.0.9": - version: 56.0.9 - resolution: "expo-dev-menu@npm:56.0.9" +"expo-dev-menu@npm:~56.0.12": + version: 56.0.12 + resolution: "expo-dev-menu@npm:56.0.12" dependencies: expo-dev-menu-interface: "npm:~56.0.0" peerDependencies: expo: "*" react-native: "*" - checksum: 10c0/c3a52d3a43e9c09430bf009f6fab2c9786853772d0926b2b38a0ff4552cd0a81f87be5584f529ca0714c2021209cb2473bb0c665b664c8c660510420a6260b56 + checksum: 10c0/9d7a16f6763b7791534a6997fd29a6aae6241e78f3648541397a1b8746907c28eca11f5610a2af0429a727adaefb543268dcd69cdbb7c9697029c88803dee665 languageName: node linkType: hard @@ -11952,26 +12047,26 @@ __metadata: languageName: node linkType: hard -"expo-file-system@npm:~56.0.4": - version: 56.0.4 - resolution: "expo-file-system@npm:56.0.4" +"expo-file-system@npm:~56.0.7": + version: 56.0.7 + resolution: "expo-file-system@npm:56.0.7" peerDependencies: expo: "*" react-native: "*" - checksum: 10c0/ae29098da6f590dea54118e0a303fa3c77778fe38aa141a4b0746f2dd9e3ed1ad41a1cc8d88aa5b68d539d8752b17b6a6fc0caa1df9e47dc0f93d35813599d58 + checksum: 10c0/a21211a2fded2926897071af7a7b3e13d422a7218d4de4928c5241849b1b0d38354e42a6dd7ef0a58dadc322815c71f1fc0d09e857f79319d4ccd85e13e12417 languageName: node linkType: hard -"expo-font@npm:~56.0.3": - version: 56.0.3 - resolution: "expo-font@npm:56.0.3" +"expo-font@npm:~56.0.5": + version: 56.0.5 + resolution: "expo-font@npm:56.0.5" dependencies: fontfaceobserver: "npm:^2.1.0" peerDependencies: expo: "*" react: "*" react-native: "*" - checksum: 10c0/250969951c2db7cbfd58d89be44f35753b81c225eb6e5747d05da5de7c778a31ec43ecf41b0e8339bfaa35aea9d9db8f72834cf10f669b68e86ac798c96fdb67 + checksum: 10c0/d4d821e6f176596f09ec0e1ee68d5af64d68fa139c8c5da3549d014d6a297bea4d0ccca6503ad753f1c0096e097364754e65ee176cafc74902a72e47ac810655 languageName: node linkType: hard @@ -11992,37 +12087,37 @@ __metadata: languageName: node linkType: hard -"expo-manifests@npm:~56.0.3": - version: 56.0.3 - resolution: "expo-manifests@npm:56.0.3" +"expo-manifests@npm:~56.0.4": + version: 56.0.4 + resolution: "expo-manifests@npm:56.0.4" dependencies: expo-json-utils: "npm:~56.0.0" peerDependencies: expo: "*" - checksum: 10c0/3eb466e472203a8c867c77e786264e3cffe10590b333ea26b34f75ce92b696a332dd7e7d879024742ea1f124c822618db07b4fcfd7a57fb1d993117c2270b5bd + checksum: 10c0/5ba98148a759d98059bd30f4551eba8ed97a74e8461e7d47ea6f1d9d614666c1fd531a71a521c7dc413c5da2162530c0a93afc2d31900b7011ff3cf2e2c39c32 languageName: node linkType: hard -"expo-modules-autolinking@npm:~56.0.6": - version: 56.0.6 - resolution: "expo-modules-autolinking@npm:56.0.6" +"expo-modules-autolinking@npm:~56.0.9": + version: 56.0.9 + resolution: "expo-modules-autolinking@npm:56.0.9" dependencies: - "@expo/require-utils": "npm:^56.1.0" - "@expo/spawn-async": "npm:^1.7.2" + "@expo/require-utils": "npm:^56.1.2" + "@expo/spawn-async": "npm:^1.8.0" chalk: "npm:^4.1.0" commander: "npm:^7.2.0" bin: expo-modules-autolinking: bin/expo-modules-autolinking.js - checksum: 10c0/96e364f4f8173ba3ebfb492bab279c4bbbf0fbb0fba122786f5fe24582fd1e2cb7295b423c640a7ae5c375664f8b0bedd84f119a872f9f44f1a2d46a548c7112 + checksum: 10c0/1265e8577c4781adce060ff3c024c7b730132c0189d90c8fdf16aed3a401ed4f0df9358fdeafb4d3ea7c50b01d3cd3568e889fa0a60a29ee1da90f3c8357d25f languageName: node linkType: hard -"expo-modules-core@npm:~56.0.8": - version: 56.0.8 - resolution: "expo-modules-core@npm:56.0.8" +"expo-modules-core@npm:~56.0.11": + version: 56.0.11 + resolution: "expo-modules-core@npm:56.0.11" dependencies: - "@expo/expo-modules-macros-plugin": "npm:~0.0.8" - expo-modules-jsi: "npm:~56.0.4" + "@expo/expo-modules-macros-plugin": "npm:~0.0.9" + expo-modules-jsi: "npm:~56.0.7" invariant: "npm:^2.2.4" peerDependencies: react: "*" @@ -12031,36 +12126,36 @@ __metadata: peerDependenciesMeta: react-native-worklets: optional: true - checksum: 10c0/3efdc8c064850b4b7d42d05c0b5560cc4b5c3ac3458c627b4377615a8dfdf5c5d89cb00a7e0e717e9c3ce1bdf07941ade595f26df365fdc59a16c77c83a0080f + checksum: 10c0/47005b45d5c5a60c3ea06ccc5d6e554d1f48ed41176322cc37e8f51e5f0c93785de08384cb93595de0af7f03482e66288c935149c7047af7e6d02427bdb53d23 languageName: node linkType: hard -"expo-modules-jsi@npm:~56.0.4": - version: 56.0.4 - resolution: "expo-modules-jsi@npm:56.0.4" +"expo-modules-jsi@npm:~56.0.7": + version: 56.0.7 + resolution: "expo-modules-jsi@npm:56.0.7" peerDependencies: react-native: "*" - checksum: 10c0/f7c8c93428ca43921e95a436daaa7d9fa7d0c99379c006b6e13de57c9f7a8362e7cfb589e6a43676bd85055952cdca4dbab829e2a8dae331916e2b1c24af1eed + checksum: 10c0/4d5c19333102401bbefa5fc6ad38cf522a93c18f23f8f11a0b5c91a0781da7c1f7f670b668446125178a0bf2d9d4a13adbded3bf5733b8a04c2c1e8a73035137 languageName: node linkType: hard -"expo-server@npm:^56.0.2": - version: 56.0.2 - resolution: "expo-server@npm:56.0.2" - checksum: 10c0/ce694d64103b71f5059c9d8800b73282d07eda890e4d7336ea15a16c73af2362dbfb7f0eb1479b463eba74586e642e38ccb778bd3d9248082f26523ab50dfbe9 +"expo-server@npm:^56.0.4": + version: 56.0.4 + resolution: "expo-server@npm:56.0.4" + checksum: 10c0/80b9217b742b4c88e540d00fd60b9b02537ffb7ecc03d205abc7936d655ba02d6c5fa17be0e9b0a1a28550a2cbd958b32b22e5d1ef116341c506467a3a8822a1 languageName: node linkType: hard -"expo-splash-screen@npm:~56.0.5": - version: 56.0.6 - resolution: "expo-splash-screen@npm:56.0.6" +"expo-splash-screen@npm:~56.0.9": + version: 56.0.9 + resolution: "expo-splash-screen@npm:56.0.9" dependencies: - "@expo/config-plugins": "npm:~56.0.5" - "@expo/image-utils": "npm:^0.9.2" + "@expo/config-plugins": "npm:~56.0.7" + "@expo/image-utils": "npm:^0.10.0" xml2js: "npm:0.6.0" peerDependencies: expo: "*" - checksum: 10c0/4743836bb403010c88ceba293d058995dbec1a257e119ab3958524c5a91d01b761c6171d4acf1b672846f61bfd4f41c9d318183881082ed89fe9072db40bd117 + checksum: 10c0/90f207be9c1a8def374852370723f872cc6722a6ab994f8998c01bdafeb38c9b206d9bdff26c21609a0fbd60a8d4c515b04333ea30cf1839adc4b88cca481d69 languageName: node linkType: hard @@ -12091,18 +12186,18 @@ __metadata: languageName: node linkType: hard -"expo-updates@npm:~56.0.10": - version: 56.0.11 - resolution: "expo-updates@npm:56.0.11" +"expo-updates@npm:~56.0.14": + version: 56.0.14 + resolution: "expo-updates@npm:56.0.14" dependencies: "@expo/code-signing-certificates": "npm:^0.0.6" - "@expo/plist": "npm:^0.6.0" - "@expo/spawn-async": "npm:^1.7.2" + "@expo/plist": "npm:^0.7.0" + "@expo/spawn-async": "npm:^1.8.0" arg: "npm:^4.1.0" chalk: "npm:^4.1.2" debug: "npm:^4.3.4" expo-eas-client: "npm:~56.0.0" - expo-manifests: "npm:~56.0.3" + expo-manifests: "npm:~56.0.4" expo-structured-headers: "npm:~56.0.0" expo-updates-interface: "npm:~56.0.1" getenv: "npm:^2.0.0" @@ -12120,34 +12215,34 @@ __metadata: optional: true bin: expo-updates: bin/cli.js - checksum: 10c0/3090fb76d118ddf87856dde0e7512894c4b311da52dd778a4dce9fc5e7e3458706956c5fd2b969bee1b9bcb54a3f7b5e927961ffd9fea2e7c1ca84c008807dee + checksum: 10c0/b19786df2458ba33a4b9f12d9a543780826a0337216cbe184d105d092e6d6b12f8716ee018465a3a3597f01a8ad52c4894bcfb9af2e05cf7defd673743928664 languageName: node linkType: hard -"expo@npm:~56.0.0-preview.11": - version: 56.0.0-preview.11 - resolution: "expo@npm:56.0.0-preview.11" +"expo@npm:~56.0.0": + version: 56.0.0 + resolution: "expo@npm:56.0.0" dependencies: "@babel/runtime": "npm:^7.20.0" - "@expo/cli": "npm:^56.1.4" - "@expo/config": "npm:~56.0.5" - "@expo/config-plugins": "npm:~56.0.5" + "@expo/cli": "npm:^56.1.7" + "@expo/config": "npm:~56.0.8" + "@expo/config-plugins": "npm:~56.0.7" "@expo/devtools": "npm:~56.0.2" "@expo/dom-webview": "npm:~56.0.5" - "@expo/fingerprint": "npm:^0.18.1" - "@expo/local-build-cache-provider": "npm:^56.0.5" - "@expo/log-box": "npm:^56.0.9" + "@expo/fingerprint": "npm:^0.19.0" + "@expo/local-build-cache-provider": "npm:^56.0.7" + "@expo/log-box": "npm:^56.0.12" "@expo/metro": "npm:~56.0.0" - "@expo/metro-config": "npm:~56.0.8" + "@expo/metro-config": "npm:~56.0.11" "@ungap/structured-clone": "npm:^1.3.0" - babel-preset-expo: "npm:~56.0.8" - expo-asset: "npm:~56.0.9" - expo-constants: "npm:~56.0.10" - expo-file-system: "npm:~56.0.4" - expo-font: "npm:~56.0.3" + babel-preset-expo: "npm:~56.0.10" + expo-asset: "npm:~56.0.12" + expo-constants: "npm:~56.0.13" + expo-file-system: "npm:~56.0.7" + expo-font: "npm:~56.0.5" expo-keep-awake: "npm:~56.0.3" - expo-modules-autolinking: "npm:~56.0.6" - expo-modules-core: "npm:~56.0.8" + expo-modules-autolinking: "npm:~56.0.9" + expo-modules-core: "npm:~56.0.11" pretty-format: "npm:^29.7.0" react-refresh: "npm:^0.14.2" whatwg-url-minimum: "npm:^0.1.2" @@ -12174,7 +12269,7 @@ __metadata: expo: bin/cli expo-modules-autolinking: bin/autolinking fingerprint: bin/fingerprint - checksum: 10c0/9990fabf519d4af13fb36ce96c558954d011878c9ed9e965aa2008c184c3b7ebc6cac28bb6078a8a76d8034d4a4e8463a9c7d07966e25dcb6e352bee06f6725e + checksum: 10c0/9493bd284ea7f8ba813827a789f7a23b43715a9fb195f54612e1375fd74b975dee41302dfd118ff16a25d4d2086194dc4f34f7326a57693b018717244d91219e languageName: node linkType: hard @@ -17811,6 +17906,49 @@ __metadata: languageName: node linkType: hard +"msgpackr-extract@npm:^3.0.2": + version: 3.0.3 + resolution: "msgpackr-extract@npm:3.0.3" + dependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "npm:3.0.3" + "@msgpackr-extract/msgpackr-extract-darwin-x64": "npm:3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm": "npm:3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm64": "npm:3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-x64": "npm:3.0.3" + "@msgpackr-extract/msgpackr-extract-win32-x64": "npm:3.0.3" + node-gyp: "npm:latest" + node-gyp-build-optional-packages: "npm:5.2.2" + dependenciesMeta: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-darwin-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-win32-x64": + optional: true + bin: + download-msgpackr-prebuilds: bin/download-prebuilds.js + checksum: 10c0/e504fd8bf86a29d7527c83776530ee6dc92dcb0273bb3679fd4a85173efead7f0ee32fb82c8410a13c33ef32828c45f81118ffc0fbed5d6842e72299894623b4 + languageName: node + linkType: hard + +"msgpackr@npm:^2.0.1": + version: 2.0.1 + resolution: "msgpackr@npm:2.0.1" + dependencies: + msgpackr-extract: "npm:^3.0.2" + dependenciesMeta: + msgpackr-extract: + optional: true + checksum: 10c0/683eaaecf4835acb759e393cf5e76dd097354e1f3b35ed260539b4fd4e0da5523b93bbaf8fc1a956a06d9a60aa2571fe210159c6f2a29fd37a9b0fa54863b6a2 + languageName: node + linkType: hard + "multicast-dns@npm:^7.2.5": version: 7.2.5 resolution: "multicast-dns@npm:7.2.5" @@ -17990,6 +18128,19 @@ __metadata: languageName: node linkType: hard +"node-gyp-build-optional-packages@npm:5.2.2": + version: 5.2.2 + resolution: "node-gyp-build-optional-packages@npm:5.2.2" + dependencies: + detect-libc: "npm:^2.0.1" + bin: + node-gyp-build-optional-packages: bin.js + node-gyp-build-optional-packages-optional: optional.js + node-gyp-build-optional-packages-test: build-test.js + checksum: 10c0/c81128c6f91873381be178c5eddcbdf66a148a6a89a427ce2bcd457593ce69baf2a8662b6d22cac092d24aa9c43c230dec4e69b3a0da604503f4777cd77e282b + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 12.1.0 resolution: "node-gyp@npm:12.1.0" @@ -18374,7 +18525,7 @@ __metadata: languageName: node linkType: hard -"open@npm:^8.0.4, open@npm:^8.0.9, open@npm:^8.4.0": +"open@npm:^8.0.9, open@npm:^8.4.0": version: 8.4.2 resolution: "open@npm:8.4.2" dependencies: @@ -20411,14 +20562,14 @@ __metadata: "@react-navigation/native": "npm:^8.0.0-alpha.25" "@react-navigation/native-stack": "npm:^8.0.0-alpha.31" babel-preset-expo: "npm:~56.0.0" - expo: "npm:~56.0.0-preview.11" + expo: "npm:~56.0.0" expo-crypto: "npm:~56.0.3" - expo-dev-client: "npm:~56.0.9" - expo-font: "npm:~56.0.3" + expo-dev-client: "npm:~56.0.13" + expo-font: "npm:~56.0.5" expo-keep-awake: "npm:~56.0.3" - expo-splash-screen: "npm:~56.0.5" + expo-splash-screen: "npm:~56.0.9" expo-status-bar: "npm:~56.0.4" - expo-updates: "npm:~56.0.10" + expo-updates: "npm:~56.0.14" react: "npm:19.2.3" react-dom: "npm:19.2.3" react-native: "npm:0.85.3" @@ -20427,7 +20578,7 @@ __metadata: react-native-monorepo-config: "npm:^0.3.4" react-native-reanimated: "npm:4.3.1" react-native-safe-area-context: "npm:~5.7.0" - react-native-screens: "npm:4.25.0" + react-native-screens: "npm:4.25.1" react-native-web: "npm:^0.21.0" react-native-worklets: "npm:0.8.3" languageName: unknown @@ -20531,16 +20682,16 @@ __metadata: languageName: node linkType: hard -"react-native-screens@npm:4.25.0": - version: 4.25.0 - resolution: "react-native-screens@npm:4.25.0" +"react-native-screens@npm:4.25.1": + version: 4.25.1 + resolution: "react-native-screens@npm:4.25.1" dependencies: react-freeze: "npm:^1.0.0" warn-once: "npm:^0.1.0" peerDependencies: react: "*" react-native: ">=0.82.0" - checksum: 10c0/ca6fe80ebd4c4ac578394686fc31a62fed22261c4478ef50245d135378e947aa3cf9edb110ac880f292d96a0ea02855d467fb5a0781a05dfc1a19519dd394033 + checksum: 10c0/9657e2bb35f73b779acad5d9d2f280791842b3a830496368806170fe9b97d2c09ec792f4566ac30237334f16aa646638f5062c770828feebb4135ea62c32c61e languageName: node linkType: hard From 08ecaeb8a72ac7ff5d8d7ee8e6ff5c6a55dc2c18 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 26 May 2026 15:14:34 +0300 Subject: [PATCH 28/94] refactor: improve structure of reference and system tokens (#4952) * add focusIndicator tokens * add shadow overrides * include mass in toRawSpring * add contentColorFor helper --- src/components/Button/utils.tsx | 2 +- src/components/Checkbox/CheckboxItem.tsx | 8 +- src/components/Checkbox/utils.ts | 2 +- src/components/Chip/helpers.tsx | 2 +- src/components/HelperText/utils.ts | 23 +- src/components/IconButton/utils.ts | 2 +- src/components/Menu/utils.ts | 2 +- src/components/Modal.tsx | 2 +- .../RadioButton/RadioButtonItem.tsx | 8 +- src/components/RadioButton/utils.ts | 2 +- src/components/SegmentedButtons/utils.ts | 2 +- src/components/TextInput/Adornment/utils.ts | 12 +- src/components/TextInput/helpers.tsx | 2 +- src/components/__tests__/Button.test.tsx | 2 +- .../__tests__/Checkbox/utils.test.tsx | 2 +- src/components/__tests__/Chip.test.tsx | 2 +- src/components/__tests__/IconButton.test.tsx | 2 +- src/components/__tests__/MenuItem.test.tsx | 2 +- src/components/__tests__/Modal.test.tsx | 2 +- .../__tests__/RadioButton/utils.test.tsx | 2 +- .../__tests__/SegmentedButton.test.tsx | 2 +- src/components/__tests__/TextInput.test.tsx | 2 +- src/theme/schemes/DarkTheme.tsx | 4 +- src/theme/schemes/LightTheme.tsx | 4 +- src/theme/tokens/index.ts | 362 +----------------- src/theme/tokens/ref/palette.ts | 97 +++++ src/theme/tokens/ref/typeface.ts | 22 ++ .../tokens/sys/{color/roles.ts => color.ts} | 11 +- src/theme/tokens/sys/elevation.ts | 39 +- src/theme/tokens/sys/motion.ts | 18 +- src/theme/tokens/sys/state.ts | 18 + src/theme/tokens/sys/typography.ts | 216 ++++++++++- src/theme/types/color.ts | 4 + src/theme/types/index.ts | 2 +- src/theme/types/motion.ts | 6 + src/theme/types/state.ts | 10 + src/theme/types/utils.ts | 9 - src/theme/utils/color.ts | 66 ++++ src/theme/utils/state.ts | 23 ++ src/types.tsx | 10 + 40 files changed, 583 insertions(+), 425 deletions(-) create mode 100644 src/theme/tokens/ref/palette.ts create mode 100644 src/theme/tokens/ref/typeface.ts rename src/theme/tokens/sys/{color/roles.ts => color.ts} (95%) create mode 100644 src/theme/tokens/sys/state.ts create mode 100644 src/theme/types/state.ts delete mode 100644 src/theme/types/utils.ts create mode 100644 src/theme/utils/color.ts create mode 100644 src/theme/utils/state.ts diff --git a/src/components/Button/utils.tsx b/src/components/Button/utils.tsx index 03ce9513ea..a2ff870c46 100644 --- a/src/components/Button/utils.tsx +++ b/src/components/Button/utils.tsx @@ -5,7 +5,7 @@ import { tokens } from '../../theme/tokens'; import type { InternalTheme, Theme } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; export type ButtonMode = | 'text' diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index c0935ffb7a..f74d8dc9d5 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -11,7 +11,7 @@ import { import Checkbox from './Checkbox'; import { useInternalTheme } from '../../core/theming'; -import { tokens } from '../../theme/tokens'; +import { getStateLayer } from '../../theme/utils/state'; import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple, { Props as TouchableRippleProps, @@ -145,14 +145,10 @@ const CheckboxItem = ({ const isLeading = position === 'leading'; const checkbox = ; - const textColor = theme.colors.onSurface; const textAlign = isLeading ? 'right' : 'left'; const computedStyle = { - color: textColor, - opacity: disabled - ? tokens.md.ref.stateOpacity.disabled - : tokens.md.ref.stateOpacity.enabled, + ...getStateLayer(theme, 'onSurface', disabled ? 'disabled' : 'enabled'), textAlign, } as TextStyle; diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index 1314193174..10f6d4eb1e 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -3,7 +3,7 @@ import type { ColorValue } from 'react-native'; import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; const getCheckedColor = ({ theme, diff --git a/src/components/Chip/helpers.tsx b/src/components/Chip/helpers.tsx index 6090ed45bb..4b0fdf9e06 100644 --- a/src/components/Chip/helpers.tsx +++ b/src/components/Chip/helpers.tsx @@ -7,7 +7,7 @@ import type { InternalTheme, Theme } from '../../types'; const md3 = (theme: InternalTheme) => theme as Theme; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; export type ChipAvatarProps = { style?: StyleProp; diff --git a/src/components/HelperText/utils.ts b/src/components/HelperText/utils.ts index 6c7fe16a58..86c39c2e45 100644 --- a/src/components/HelperText/utils.ts +++ b/src/components/HelperText/utils.ts @@ -1,8 +1,6 @@ -import { tokens } from '../../theme/tokens'; +import { getStateLayer } from '../../theme/utils/state'; import type { InternalTheme } from '../../types'; -const { stateOpacity } = tokens.md.ref; - type BaseProps = { theme: InternalTheme; disabled?: boolean; @@ -11,18 +9,11 @@ type BaseProps = { export function getTextColor({ theme, disabled, type }: BaseProps) { if (type === 'error') { - return { color: theme.colors.error, opacity: stateOpacity.enabled }; - } - - if (disabled) { - return { - color: theme.colors.onSurfaceVariant, - opacity: stateOpacity.disabled, - }; + return getStateLayer(theme, 'error', 'enabled'); } - - return { - color: theme.colors.onSurfaceVariant, - opacity: stateOpacity.enabled, - }; + return getStateLayer( + theme, + 'onSurfaceVariant', + disabled ? 'disabled' : 'enabled' + ); } diff --git a/src/components/IconButton/utils.ts b/src/components/IconButton/utils.ts index 284e9efb2d..cbcf5f1051 100644 --- a/src/components/IconButton/utils.ts +++ b/src/components/IconButton/utils.ts @@ -3,7 +3,7 @@ import type { ColorValue } from 'react-native'; import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; type IconButtonMode = 'outlined' | 'contained' | 'contained-tonal'; diff --git a/src/components/Menu/utils.ts b/src/components/Menu/utils.ts index 9d17a863e6..2aae40d2e4 100644 --- a/src/components/Menu/utils.ts +++ b/src/components/Menu/utils.ts @@ -2,7 +2,7 @@ import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; import type { IconSource } from '../Icon'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; export const MIN_WIDTH = 112; export const MAX_WIDTH = 280; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 7cce27eb3c..d6590881a3 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -20,7 +20,7 @@ import { addEventListener } from '../utils/addEventListener'; import { BackHandler } from '../utils/BackHandler/BackHandler'; import useAnimatedValue from '../utils/useAnimatedValue'; -const { scrimAlpha } = tokens.md.ref; +const scrimAlpha = tokens.md.sys.scrim.alpha; export type Props = { /** diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index 936a2cdf3d..4c6bcb824d 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -15,7 +15,7 @@ import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; import RadioButtonIOS from './RadioButtonIOS'; import { handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; -import { tokens } from '../../theme/tokens'; +import { getStateLayer } from '../../theme/utils/state'; import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple, { Props as TouchableRippleProps, @@ -179,14 +179,10 @@ const RadioButtonItem = ({ radioButton = ; } - const textColor = theme.colors.onSurface; const textAlign = isLeading ? 'right' : 'left'; const computedStyle = { - color: textColor, - opacity: disabled - ? tokens.md.ref.stateOpacity.disabled - : tokens.md.ref.stateOpacity.enabled, + ...getStateLayer(theme, 'onSurface', disabled ? 'disabled' : 'enabled'), textAlign, } as TextStyle; diff --git a/src/components/RadioButton/utils.ts b/src/components/RadioButton/utils.ts index 9cd776ad8d..96adafaf47 100644 --- a/src/components/RadioButton/utils.ts +++ b/src/components/RadioButton/utils.ts @@ -3,7 +3,7 @@ import type { ColorValue, GestureResponderEvent } from 'react-native'; import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; export const handlePress = ({ onPress, diff --git a/src/components/SegmentedButtons/utils.ts b/src/components/SegmentedButtons/utils.ts index af12651971..bf56e25139 100644 --- a/src/components/SegmentedButtons/utils.ts +++ b/src/components/SegmentedButtons/utils.ts @@ -3,7 +3,7 @@ import { ViewStyle } from 'react-native'; import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; type BaseProps = { theme: InternalTheme; diff --git a/src/components/TextInput/Adornment/utils.ts b/src/components/TextInput/Adornment/utils.ts index 64e5462b74..3a6e819963 100644 --- a/src/components/TextInput/Adornment/utils.ts +++ b/src/components/TextInput/Adornment/utils.ts @@ -1,9 +1,10 @@ import type { ColorValue } from 'react-native'; import { tokens } from '../../../theme/tokens'; +import { getStateLayer } from '../../../theme/utils/state'; import type { InternalTheme } from '../../../types'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; type BaseProps = { theme: InternalTheme; @@ -11,10 +12,11 @@ type BaseProps = { }; export function getTextColor({ theme, disabled }: BaseProps) { - return { - color: theme.colors.onSurfaceVariant, - opacity: disabled ? stateOpacity.disabled : stateOpacity.enabled, - }; + return getStateLayer( + theme, + 'onSurfaceVariant', + disabled ? 'disabled' : 'enabled' + ); } export function getIconColor({ diff --git a/src/components/TextInput/helpers.tsx b/src/components/TextInput/helpers.tsx index 0dd1aad7e6..c633314991 100644 --- a/src/components/TextInput/helpers.tsx +++ b/src/components/TextInput/helpers.tsx @@ -19,7 +19,7 @@ import type { TextInputLabelProp } from './types'; import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; type PaddingProps = { height: number | null; diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 71107089e7..7f631d5dcb 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -10,7 +10,7 @@ import { tokens } from '../../theme/tokens'; import Button from '../Button/Button'; import { getButtonColors } from '../Button/utils'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; const styles = StyleSheet.create({ flexing: { diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index 118fe1690f..78af32e8ea 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,7 +1,7 @@ import { getTheme } from '../../../core/theming'; import { tokens } from '../../../theme/tokens'; import { getSelectionControlColor } from '../../Checkbox/utils'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; describe('getSelectionControlColor - checkbox color', () => { it('should return correct disabled color, for theme version 3', () => { diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index cf0f89ddba..7b724dd4ac 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -10,7 +10,7 @@ import { tokens } from '../../theme/tokens'; import Chip from '../Chip/Chip'; import { getChipColors } from '../Chip/helpers'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; it('renders chip with onPress', () => { const tree = render( {}}>Example Chip).toJSON(); diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index 9702fe499c..0f34aaec38 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -10,7 +10,7 @@ import { tokens } from '../../theme/tokens'; import IconButton from '../IconButton/IconButton'; import { getIconButtonColor } from '../IconButton/utils'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; const styles = StyleSheet.create({ square: { diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index 8b525a5844..2fa3f809bd 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -6,7 +6,7 @@ import { tokens } from '../../theme/tokens'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; describe('Menu Item', () => { it('renders menu item', () => { diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index f4b37bc564..77f3fe609f 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -13,7 +13,7 @@ import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import Modal from '../Modal'; -const { scrimAlpha } = tokens.md.ref; +const scrimAlpha = tokens.md.sys.scrim.alpha; jest.mock('react-native-safe-area-context', () => ({ useSafeAreaInsets: () => ({ bottom: 44, left: 0, right: 0, top: 37 }), diff --git a/src/components/__tests__/RadioButton/utils.test.tsx b/src/components/__tests__/RadioButton/utils.test.tsx index 929d0f7a2c..12486a9c3b 100644 --- a/src/components/__tests__/RadioButton/utils.test.tsx +++ b/src/components/__tests__/RadioButton/utils.test.tsx @@ -2,7 +2,7 @@ import { getTheme } from '../../../core/theming'; import { tokens } from '../../../theme/tokens'; import { getSelectionControlIOSColor } from '../../RadioButton/utils'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; describe('getSelectionControlIOSColor - checked color', () => { it('should return correct disabled color, for theme version 3', () => { diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 94ab9828b1..0904d02829 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -9,7 +9,7 @@ import { getSegmentedButtonColors, } from '../SegmentedButtons/utils'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; it('renders segmented button', () => { const tree = render( diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index 7890ce3253..97f77833aa 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -16,7 +16,7 @@ import { } from '../TextInput/helpers'; import TextInput, { Props } from '../TextInput/TextInput'; -const { stateOpacity } = tokens.md.ref; +const stateOpacity = tokens.md.sys.state.opacity; const style = StyleSheet.create({ inputStyle: { diff --git a/src/theme/schemes/DarkTheme.tsx b/src/theme/schemes/DarkTheme.tsx index dfc8e6b16b..9b7ff60ef4 100644 --- a/src/theme/schemes/DarkTheme.tsx +++ b/src/theme/schemes/DarkTheme.tsx @@ -1,12 +1,12 @@ import { themeDefaults } from './base'; import { tokens } from '../tokens'; -import { buildScheme } from '../tokens/sys/color/roles'; +import { buildScheme } from '../tokens/sys/color'; import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const DarkTheme: Theme = { ...themeDefaults, dark: true, - colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'dark' }), + colors: buildScheme(tokens.md.ref.palette, { mode: 'dark' }), shapes: defaultShapes, }; diff --git a/src/theme/schemes/LightTheme.tsx b/src/theme/schemes/LightTheme.tsx index 35926afde9..42593d5d42 100644 --- a/src/theme/schemes/LightTheme.tsx +++ b/src/theme/schemes/LightTheme.tsx @@ -1,12 +1,12 @@ import { themeDefaults } from './base'; import { tokens } from '../tokens'; -import { buildScheme } from '../tokens/sys/color/roles'; +import { buildScheme } from '../tokens/sys/color'; import { defaultShapes } from '../tokens/sys/shape'; import type { Theme } from '../types'; export const LightTheme: Theme = { ...themeDefaults, dark: false, - colors: buildScheme(tokens.md.ref.palette, tokens.md.ref, { mode: 'light' }), + colors: buildScheme(tokens.md.ref.palette, { mode: 'light' }), shapes: defaultShapes, }; diff --git a/src/theme/tokens/index.ts b/src/theme/tokens/index.ts index b0b160b080..3f41b7fec5 100644 --- a/src/theme/tokens/index.ts +++ b/src/theme/tokens/index.ts @@ -1,358 +1,22 @@ -import { Platform } from 'react-native'; - -import type { Font } from '../types'; - -const ref = { - palette: { - primary100: 'rgba(255, 255, 255, 1)', - primary99: 'rgba(255, 251, 254, 1)', - primary98: 'rgba(254, 247, 255, 1)', - primary95: 'rgba(246, 237, 255, 1)', - primary90: 'rgba(234, 221, 255, 1)', - primary80: 'rgba(208, 188, 255, 1)', - primary70: 'rgba(182, 157, 248, 1)', - primary60: 'rgba(154, 130, 219, 1)', - primary50: 'rgba(127, 103, 190, 1)', - primary40: 'rgba(103, 80, 164, 1)', - primary30: 'rgba(79, 55, 139, 1)', - primary20: 'rgba(56, 30, 114, 1)', - primary10: 'rgba(33, 0, 93, 1)', - primary0: 'rgba(0, 0, 0, 1)', - secondary100: 'rgba(255, 255, 255, 1)', - secondary99: 'rgba(255, 251, 254, 1)', - secondary98: 'rgba(254, 247, 255, 1)', - secondary95: 'rgba(246, 237, 255, 1)', - secondary90: 'rgba(232, 222, 248, 1)', - secondary80: 'rgba(204, 194, 220, 1)', - secondary70: 'rgba(176, 167, 192, 1)', - secondary60: 'rgba(149, 141, 165, 1)', - secondary50: 'rgba(122, 114, 137, 1)', - secondary40: 'rgba(98, 91, 113, 1)', - secondary30: 'rgba(74, 68, 88, 1)', - secondary20: 'rgba(51, 45, 65, 1)', - secondary10: 'rgba(29, 25, 43, 1)', - secondary0: 'rgba(0, 0, 0, 1)', - tertiary100: 'rgba(255, 255, 255, 1)', - tertiary99: 'rgba(255, 251, 250, 1)', - tertiary98: 'rgba(255, 248, 248, 1)', - tertiary95: 'rgba(255, 236, 241, 1)', - tertiary90: 'rgba(255, 216, 228, 1)', - tertiary80: 'rgba(239, 184, 200, 1)', - tertiary70: 'rgba(210, 157, 172, 1)', - tertiary60: 'rgba(181, 131, 146, 1)', - tertiary50: 'rgba(152, 105, 119, 1)', - tertiary40: 'rgba(125, 82, 96, 1)', - tertiary30: 'rgba(99, 59, 72, 1)', - tertiary20: 'rgba(73, 37, 50, 1)', - tertiary10: 'rgba(49, 17, 29, 1)', - tertiary0: 'rgba(0, 0, 0, 1)', - neutral100: 'rgba(255, 255, 255, 1)', - neutral99: 'rgba(255, 251, 255, 1)', - neutral98: 'rgba(254, 247, 255, 1)', - neutral96: 'rgba(247, 242, 250, 1)', - neutral95: 'rgba(245, 239, 247, 1)', - neutral94: 'rgba(243, 237, 247, 1)', - neutral92: 'rgba(236, 230, 240, 1)', - neutral90: 'rgba(230, 224, 233, 1)', - neutral87: 'rgba(222, 216, 225, 1)', - neutral80: 'rgba(202, 197, 205, 1)', - neutral70: 'rgba(174, 169, 177, 1)', - neutral60: 'rgba(147, 143, 150, 1)', - neutral50: 'rgba(121, 118, 125, 1)', - neutral40: 'rgba(96, 93, 100, 1)', - neutral30: 'rgba(72, 70, 76, 1)', - neutral24: 'rgba(59, 56, 62, 1)', - neutral22: 'rgba(54, 52, 59, 1)', - neutral20: 'rgba(50, 47, 53, 1)', - neutral17: 'rgba(43, 41, 48, 1)', - neutral12: 'rgba(33, 31, 38, 1)', - neutral10: 'rgba(29, 27, 32, 1)', - neutral6: 'rgba(20, 18, 24, 1)', - neutral4: 'rgba(15, 13, 19, 1)', - neutral0: 'rgba(0, 0, 0, 1)', - neutralVariant100: 'rgba(255, 255, 255, 1)', - neutralVariant99: 'rgba(255, 251, 254, 1)', - neutralVariant98: 'rgba(253, 247, 255, 1)', - neutralVariant95: 'rgba(245, 238, 250, 1)', - neutralVariant90: 'rgba(231, 224, 236, 1)', - neutralVariant80: 'rgba(202, 196, 208, 1)', - neutralVariant70: 'rgba(174, 169, 180, 1)', - neutralVariant60: 'rgba(147, 143, 153, 1)', - neutralVariant50: 'rgba(121, 116, 126, 1)', - neutralVariant40: 'rgba(96, 93, 102, 1)', - neutralVariant30: 'rgba(73, 69, 79, 1)', - neutralVariant20: 'rgba(50, 47, 55, 1)', - neutralVariant10: 'rgba(29, 26, 34, 1)', - neutralVariant0: 'rgba(0, 0, 0, 1)', - error100: 'rgba(255, 255, 255, 1)', - error99: 'rgba(255, 251, 249, 1)', - error98: 'rgba(255, 248, 247, 1)', - error95: 'rgba(252, 238, 238, 1)', - error90: 'rgba(249, 222, 220, 1)', - error80: 'rgba(242, 184, 181, 1)', - error70: 'rgba(236, 146, 142, 1)', - error60: 'rgba(228, 105, 98, 1)', - error50: 'rgba(220, 54, 46, 1)', - error40: 'rgba(179, 38, 30, 1)', - error30: 'rgba(140, 29, 24, 1)', - error20: 'rgba(96, 20, 16, 1)', - error10: 'rgba(65, 14, 11, 1)', - error0: 'rgba(0, 0, 0, 1)', - }, - - typeface: { - brandRegular: Platform.select({ - web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', - ios: 'System', - default: 'sans-serif', - }), - weightRegular: '400' as Font['fontWeight'], - - plainMedium: Platform.select({ - web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', - ios: 'System', - default: 'sans-serif-medium', - }), - weightMedium: '500' as Font['fontWeight'], - - weightBold: '700' as Font['fontWeight'], - }, - - /** State layers opacity - * @see https://m3.material.io/foundations/interaction/states/state-layers - */ - stateOpacity: { - dragged: 0.16, - pressed: 0.1, - focused: 0.1, - hovered: 0.08, - disabled: 0.38, - enabled: 1.0, - }, - - scrimAlpha: 0.32, -}; - -const regularType = { - fontFamily: ref.typeface.brandRegular, - letterSpacing: 0, - fontWeight: ref.typeface.weightRegular, -}; - -const mediumType = { - fontFamily: ref.typeface.plainMedium, - letterSpacing: 0.15, - fontWeight: ref.typeface.weightMedium, -}; - -const emphasizedMediumType = { - fontFamily: ref.typeface.plainMedium, - letterSpacing: 0, - fontWeight: ref.typeface.weightMedium, -}; - -const emphasizedBoldType = { - fontFamily: ref.typeface.plainMedium, - letterSpacing: 0, - fontWeight: ref.typeface.weightBold, -}; - -export const typescale = { - displayLarge: { - ...regularType, - letterSpacing: -0.25, - lineHeight: 64, - fontSize: 57, - }, - displayMedium: { - ...regularType, - lineHeight: 52, - fontSize: 45, - }, - displaySmall: { - ...regularType, - lineHeight: 44, - fontSize: 36, - }, - - headlineLarge: { - ...regularType, - lineHeight: 40, - fontSize: 32, - }, - headlineMedium: { - ...regularType, - lineHeight: 36, - fontSize: 28, - }, - headlineSmall: { - ...regularType, - lineHeight: 32, - fontSize: 24, - }, - - titleLarge: { - ...regularType, - lineHeight: 28, - fontSize: 22, - }, - titleMedium: { - ...mediumType, - lineHeight: 24, - fontSize: 16, - }, - titleSmall: { - ...mediumType, - letterSpacing: 0.1, - lineHeight: 20, - fontSize: 14, - }, - - labelLarge: { - ...mediumType, - letterSpacing: 0.1, - lineHeight: 20, - fontSize: 14, - }, - labelMedium: { - ...mediumType, - letterSpacing: 0.5, - lineHeight: 16, - fontSize: 12, - }, - labelSmall: { - ...mediumType, - letterSpacing: 0.5, - lineHeight: 16, - fontSize: 11, - }, - - bodyLarge: { - ...mediumType, - fontWeight: ref.typeface.weightRegular, - fontFamily: ref.typeface.brandRegular, - letterSpacing: 0.5, - lineHeight: 24, - fontSize: 16, - }, - bodyMedium: { - ...mediumType, - fontWeight: ref.typeface.weightRegular, - fontFamily: ref.typeface.brandRegular, - letterSpacing: 0.25, - lineHeight: 20, - fontSize: 14, - }, - bodySmall: { - ...mediumType, - fontWeight: ref.typeface.weightRegular, - fontFamily: ref.typeface.brandRegular, - letterSpacing: 0.4, - lineHeight: 16, - fontSize: 12, - }, - - displayLargeEmphasized: { - ...emphasizedMediumType, - letterSpacing: -0.25, - lineHeight: 64, - fontSize: 57, - }, - displayMediumEmphasized: { - ...emphasizedMediumType, - lineHeight: 52, - fontSize: 45, - }, - displaySmallEmphasized: { - ...emphasizedMediumType, - lineHeight: 44, - fontSize: 36, - }, - - headlineLargeEmphasized: { - ...emphasizedMediumType, - lineHeight: 40, - fontSize: 32, - }, - headlineMediumEmphasized: { - ...emphasizedMediumType, - lineHeight: 36, - fontSize: 28, - }, - headlineSmallEmphasized: { - ...emphasizedMediumType, - lineHeight: 32, - fontSize: 24, - }, - - titleLargeEmphasized: { - ...emphasizedMediumType, - lineHeight: 28, - fontSize: 22, - }, - titleMediumEmphasized: { - ...emphasizedBoldType, - letterSpacing: 0.15, - lineHeight: 24, - fontSize: 16, - }, - titleSmallEmphasized: { - ...emphasizedBoldType, - letterSpacing: 0.1, - lineHeight: 20, - fontSize: 14, - }, - - labelLargeEmphasized: { - ...emphasizedBoldType, - letterSpacing: 0.1, - lineHeight: 20, - fontSize: 14, - }, - labelMediumEmphasized: { - ...emphasizedBoldType, - letterSpacing: 0.5, - lineHeight: 16, - fontSize: 12, - }, - labelSmallEmphasized: { - ...emphasizedBoldType, - letterSpacing: 0.5, - lineHeight: 16, - fontSize: 11, - }, - - bodyLargeEmphasized: { - ...emphasizedMediumType, - letterSpacing: 0.5, - lineHeight: 24, - fontSize: 16, - }, - bodyMediumEmphasized: { - ...emphasizedMediumType, - letterSpacing: 0.25, - lineHeight: 20, - fontSize: 14, - }, - bodySmallEmphasized: { - ...emphasizedMediumType, - letterSpacing: 0.4, - lineHeight: 16, - fontSize: 12, - }, - - default: { - ...regularType, - }, -}; +import { palette } from './ref/palette'; +import { typeface } from './ref/typeface'; +import { state } from './sys/state'; +import { typescale } from './sys/typography'; +/** Material Design token tree: md.ref.* (raw values) and md.sys.* (semantic decisions). */ export const tokens = { md: { - ref, + ref: { + palette, + typeface, + }, sys: { typescale, + state: state, + scrim: { alpha: 0.32 }, }, }, }; -export const Palette = ref.palette; +export { typescale }; +export const Palette = palette; diff --git a/src/theme/tokens/ref/palette.ts b/src/theme/tokens/ref/palette.ts new file mode 100644 index 0000000000..f16b793f66 --- /dev/null +++ b/src/theme/tokens/ref/palette.ts @@ -0,0 +1,97 @@ +/** md.ref.palette.* — tonal palette reference tokens. */ +export const palette = { + primary100: 'rgba(255, 255, 255, 1)', + primary99: 'rgba(255, 251, 254, 1)', + primary98: 'rgba(254, 247, 255, 1)', + primary95: 'rgba(246, 237, 255, 1)', + primary90: 'rgba(234, 221, 255, 1)', + primary80: 'rgba(208, 188, 255, 1)', + primary70: 'rgba(182, 157, 248, 1)', + primary60: 'rgba(154, 130, 219, 1)', + primary50: 'rgba(127, 103, 190, 1)', + primary40: 'rgba(103, 80, 164, 1)', + primary30: 'rgba(79, 55, 139, 1)', + primary20: 'rgba(56, 30, 114, 1)', + primary10: 'rgba(33, 0, 93, 1)', + primary0: 'rgba(0, 0, 0, 1)', + secondary100: 'rgba(255, 255, 255, 1)', + secondary99: 'rgba(255, 251, 254, 1)', + secondary98: 'rgba(254, 247, 255, 1)', + secondary95: 'rgba(246, 237, 255, 1)', + secondary90: 'rgba(232, 222, 248, 1)', + secondary80: 'rgba(204, 194, 220, 1)', + secondary70: 'rgba(176, 167, 192, 1)', + secondary60: 'rgba(149, 141, 165, 1)', + secondary50: 'rgba(122, 114, 137, 1)', + secondary40: 'rgba(98, 91, 113, 1)', + secondary30: 'rgba(74, 68, 88, 1)', + secondary20: 'rgba(51, 45, 65, 1)', + secondary10: 'rgba(29, 25, 43, 1)', + secondary0: 'rgba(0, 0, 0, 1)', + tertiary100: 'rgba(255, 255, 255, 1)', + tertiary99: 'rgba(255, 251, 250, 1)', + tertiary98: 'rgba(255, 248, 248, 1)', + tertiary95: 'rgba(255, 236, 241, 1)', + tertiary90: 'rgba(255, 216, 228, 1)', + tertiary80: 'rgba(239, 184, 200, 1)', + tertiary70: 'rgba(210, 157, 172, 1)', + tertiary60: 'rgba(181, 131, 146, 1)', + tertiary50: 'rgba(152, 105, 119, 1)', + tertiary40: 'rgba(125, 82, 96, 1)', + tertiary30: 'rgba(99, 59, 72, 1)', + tertiary20: 'rgba(73, 37, 50, 1)', + tertiary10: 'rgba(49, 17, 29, 1)', + tertiary0: 'rgba(0, 0, 0, 1)', + neutral100: 'rgba(255, 255, 255, 1)', + neutral99: 'rgba(255, 251, 255, 1)', + neutral98: 'rgba(254, 247, 255, 1)', + neutral96: 'rgba(247, 242, 250, 1)', + neutral95: 'rgba(245, 239, 247, 1)', + neutral94: 'rgba(243, 237, 247, 1)', + neutral92: 'rgba(236, 230, 240, 1)', + neutral90: 'rgba(230, 224, 233, 1)', + neutral87: 'rgba(222, 216, 225, 1)', + neutral80: 'rgba(202, 197, 205, 1)', + neutral70: 'rgba(174, 169, 177, 1)', + neutral60: 'rgba(147, 143, 150, 1)', + neutral50: 'rgba(121, 118, 125, 1)', + neutral40: 'rgba(96, 93, 100, 1)', + neutral30: 'rgba(72, 70, 76, 1)', + neutral24: 'rgba(59, 56, 62, 1)', + neutral22: 'rgba(54, 52, 59, 1)', + neutral20: 'rgba(50, 47, 53, 1)', + neutral17: 'rgba(43, 41, 48, 1)', + neutral12: 'rgba(33, 31, 38, 1)', + neutral10: 'rgba(29, 27, 32, 1)', + neutral6: 'rgba(20, 18, 24, 1)', + neutral4: 'rgba(15, 13, 19, 1)', + neutral0: 'rgba(0, 0, 0, 1)', + neutralVariant100: 'rgba(255, 255, 255, 1)', + neutralVariant99: 'rgba(255, 251, 254, 1)', + neutralVariant98: 'rgba(253, 247, 255, 1)', + neutralVariant95: 'rgba(245, 238, 250, 1)', + neutralVariant90: 'rgba(231, 224, 236, 1)', + neutralVariant80: 'rgba(202, 196, 208, 1)', + neutralVariant70: 'rgba(174, 169, 180, 1)', + neutralVariant60: 'rgba(147, 143, 153, 1)', + neutralVariant50: 'rgba(121, 116, 126, 1)', + neutralVariant40: 'rgba(96, 93, 102, 1)', + neutralVariant30: 'rgba(73, 69, 79, 1)', + neutralVariant20: 'rgba(50, 47, 55, 1)', + neutralVariant10: 'rgba(29, 26, 34, 1)', + neutralVariant0: 'rgba(0, 0, 0, 1)', + error100: 'rgba(255, 255, 255, 1)', + error99: 'rgba(255, 251, 249, 1)', + error98: 'rgba(255, 248, 247, 1)', + error95: 'rgba(252, 238, 238, 1)', + error90: 'rgba(249, 222, 220, 1)', + error80: 'rgba(242, 184, 181, 1)', + error70: 'rgba(236, 146, 142, 1)', + error60: 'rgba(228, 105, 98, 1)', + error50: 'rgba(220, 54, 46, 1)', + error40: 'rgba(179, 38, 30, 1)', + error30: 'rgba(140, 29, 24, 1)', + error20: 'rgba(96, 20, 16, 1)', + error10: 'rgba(65, 14, 11, 1)', + error0: 'rgba(0, 0, 0, 1)', +} as const; diff --git a/src/theme/tokens/ref/typeface.ts b/src/theme/tokens/ref/typeface.ts new file mode 100644 index 0000000000..e4b92318ec --- /dev/null +++ b/src/theme/tokens/ref/typeface.ts @@ -0,0 +1,22 @@ +import { Platform } from 'react-native'; + +import type { Font } from '../../types'; + +/** md.ref.typeface.* — font families and weights. */ +export const typeface = { + brandRegular: Platform.select({ + web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', + ios: 'System', + default: 'sans-serif', + }), + weightRegular: '400' as Font['fontWeight'], + + plainMedium: Platform.select({ + web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', + ios: 'System', + default: 'sans-serif-medium', + }), + weightMedium: '500' as Font['fontWeight'], + + weightBold: '700' as Font['fontWeight'], +}; diff --git a/src/theme/tokens/sys/color/roles.ts b/src/theme/tokens/sys/color.ts similarity index 95% rename from src/theme/tokens/sys/color/roles.ts rename to src/theme/tokens/sys/color.ts index a3b3a49695..efdb08cbba 100644 --- a/src/theme/tokens/sys/color/roles.ts +++ b/src/theme/tokens/sys/color.ts @@ -1,11 +1,11 @@ import color from 'color'; -import type { ElevationColors, ThemeColors } from '../../../types'; -import { tokens } from '../../index'; +import { state } from './state'; +import type { ElevationColors, ThemeColors } from '../../types'; +import { palette as defaultPalette } from '../ref/palette'; -type Palette = typeof tokens.md.ref.palette; +type Palette = typeof defaultPalette; type PaletteKey = keyof Palette; -type Ref = typeof tokens.md.ref; /** Roles that map 1:1 to a palette key. Excludes the computed fields. */ type MappedRoles = Omit; @@ -148,7 +148,6 @@ const elevationToTone: Record< export function buildScheme( palette: Palette, - ref: Ref, opts: { mode: 'light' | 'dark'; contrast?: Contrast } ): ThemeColors { const contrast = opts.contrast ?? 'standard'; @@ -165,7 +164,7 @@ export function buildScheme( return { ...mapped, stateLayerPressed: color(palette[tones.onSurface]) - .alpha(ref.stateOpacity.pressed) + .alpha(state.opacity.pressed) .rgb() .string(), elevation: { diff --git a/src/theme/tokens/sys/elevation.ts b/src/theme/tokens/sys/elevation.ts index f6793771f3..153fe1141e 100644 --- a/src/theme/tokens/sys/elevation.ts +++ b/src/theme/tokens/sys/elevation.ts @@ -1,13 +1,35 @@ // M3 elevation tokens and shadow builder per spec: // https://m3.material.io/styles/elevation/tokens -import { Animated, Platform, type ColorValue } from 'react-native'; +import { + Animated, + Platform, + type ColorValue, + type ViewStyle, + type Animated as AnimatedNS, +} from 'react-native'; import color from 'color'; import { isAnimatedValue } from '../../../utils/animations'; import type { Elevation, ThemeElevation } from '../../types'; +type AnimatedNativeShadowStyle = { + shadowColor: ColorValue; + shadowOffset: { + width: AnimatedNS.Value; + height: AnimatedNS.AnimatedInterpolation; + }; + shadowOpacity: AnimatedNS.AnimatedInterpolation; + shadowRadius: AnimatedNS.AnimatedInterpolation; +}; + +type AnimatedBoxShadowStyle = { + boxShadow: AnimatedNS.AnimatedInterpolation; +}; + +type AnimatedShadowStyle = AnimatedNativeShadowStyle | AnimatedBoxShadowStyle; + export const defaultElevation: ThemeElevation = { level0: 0, level1: 1, @@ -47,10 +69,23 @@ const getShadowColor = (shadowColor: ColorValue, shadowOpacity: number) => { const getBoxShadowValue = (elevation: number, shadowColor: string) => `0px ${shadowLayers[0].height[elevation]}px ${shadowLayers[0].shadowRadius[elevation]}px ${shadowColor}`; +// eslint-disable-next-line no-redeclare +export function shadow(elevation: number, shadowColor: ColorValue): ViewStyle; +// eslint-disable-next-line no-redeclare +export function shadow( + elevation: Animated.Value, + shadowColor: ColorValue +): AnimatedShadowStyle; +// eslint-disable-next-line no-redeclare +export function shadow( + elevation: number | Animated.Value, + shadowColor: ColorValue +): ViewStyle | AnimatedShadowStyle; +// eslint-disable-next-line no-redeclare export function shadow( elevation: number | Animated.Value = 0, shadowColor: ColorValue -) { +): ViewStyle | AnimatedShadowStyle { if (Platform.OS === 'web') { const webShadowColor = getShadowColor(shadowColor, 0.3); diff --git a/src/theme/tokens/sys/motion.ts b/src/theme/tokens/sys/motion.ts index a7c96454f2..878d63e0e9 100644 --- a/src/theme/tokens/sys/motion.ts +++ b/src/theme/tokens/sys/motion.ts @@ -2,6 +2,7 @@ import type { MotionConfig, MotionDuration, MotionEasing, + RawSpring, SpringConfig, } from '../../types'; @@ -93,7 +94,20 @@ export const standardMotion: MotionConfig = { * ...toRawSpring(theme.motion.spring.fast.spatial), * useNativeDriver: true, * }); + * + * // Reanimated + * sharedValue.value = withSpring( + * target, + * toRawSpring(theme.motion.spring.fast.spatial) + * ); */ -export function toRawSpring({ stiffness, damping }: SpringConfig) { - return { stiffness, damping: damping * 2 * Math.sqrt(stiffness) }; +export function toRawSpring({ + stiffness, + damping: ratio, +}: SpringConfig): RawSpring { + return { + stiffness, + damping: ratio * 2 * Math.sqrt(stiffness), + mass: 1, // as per MD specs + }; } diff --git a/src/theme/tokens/sys/state.ts b/src/theme/tokens/sys/state.ts new file mode 100644 index 0000000000..d0742351bf --- /dev/null +++ b/src/theme/tokens/sys/state.ts @@ -0,0 +1,18 @@ +/** + * md.sys.state.* — interaction-state system tokens. + * @see https://m3.material.io/foundations/interaction/states/state-layers + */ +export const state = { + opacity: { + dragged: 0.16, + pressed: 0.1, + focused: 0.1, + hovered: 0.08, + disabled: 0.38, + enabled: 1.0, + }, + focusIndicator: { + thickness: 3, + outerOffset: 2, + }, +} as const; diff --git a/src/theme/tokens/sys/typography.ts b/src/theme/tokens/sys/typography.ts index 2ea3e08c6c..289cbdedef 100644 --- a/src/theme/tokens/sys/typography.ts +++ b/src/theme/tokens/sys/typography.ts @@ -1,4 +1,218 @@ import type { Typescale } from '../../types'; -import { typescale } from '../index'; +import { typeface } from '../ref/typeface'; + +const regularType = { + fontFamily: typeface.brandRegular, + letterSpacing: 0, + fontWeight: typeface.weightRegular, +}; + +const mediumType = { + fontFamily: typeface.plainMedium, + letterSpacing: 0.15, + fontWeight: typeface.weightMedium, +}; + +const emphasizedMediumType = { + fontFamily: typeface.plainMedium, + letterSpacing: 0, + fontWeight: typeface.weightMedium, +}; + +const emphasizedBoldType = { + fontFamily: typeface.plainMedium, + letterSpacing: 0, + fontWeight: typeface.weightBold, +}; + +/** md.sys.typescale.* */ +export const typescale = { + displayLarge: { + ...regularType, + letterSpacing: -0.25, + lineHeight: 64, + fontSize: 57, + }, + displayMedium: { + ...regularType, + lineHeight: 52, + fontSize: 45, + }, + displaySmall: { + ...regularType, + lineHeight: 44, + fontSize: 36, + }, + + headlineLarge: { + ...regularType, + lineHeight: 40, + fontSize: 32, + }, + headlineMedium: { + ...regularType, + lineHeight: 36, + fontSize: 28, + }, + headlineSmall: { + ...regularType, + lineHeight: 32, + fontSize: 24, + }, + + titleLarge: { + ...regularType, + lineHeight: 28, + fontSize: 22, + }, + titleMedium: { + ...mediumType, + lineHeight: 24, + fontSize: 16, + }, + titleSmall: { + ...mediumType, + letterSpacing: 0.1, + lineHeight: 20, + fontSize: 14, + }, + + labelLarge: { + ...mediumType, + letterSpacing: 0.1, + lineHeight: 20, + fontSize: 14, + }, + labelMedium: { + ...mediumType, + letterSpacing: 0.5, + lineHeight: 16, + fontSize: 12, + }, + labelSmall: { + ...mediumType, + letterSpacing: 0.5, + lineHeight: 16, + fontSize: 11, + }, + + bodyLarge: { + ...mediumType, + fontWeight: typeface.weightRegular, + fontFamily: typeface.brandRegular, + letterSpacing: 0.5, + lineHeight: 24, + fontSize: 16, + }, + bodyMedium: { + ...mediumType, + fontWeight: typeface.weightRegular, + fontFamily: typeface.brandRegular, + letterSpacing: 0.25, + lineHeight: 20, + fontSize: 14, + }, + bodySmall: { + ...mediumType, + fontWeight: typeface.weightRegular, + fontFamily: typeface.brandRegular, + letterSpacing: 0.4, + lineHeight: 16, + fontSize: 12, + }, + + displayLargeEmphasized: { + ...emphasizedMediumType, + letterSpacing: -0.25, + lineHeight: 64, + fontSize: 57, + }, + displayMediumEmphasized: { + ...emphasizedMediumType, + lineHeight: 52, + fontSize: 45, + }, + displaySmallEmphasized: { + ...emphasizedMediumType, + lineHeight: 44, + fontSize: 36, + }, + + headlineLargeEmphasized: { + ...emphasizedMediumType, + lineHeight: 40, + fontSize: 32, + }, + headlineMediumEmphasized: { + ...emphasizedMediumType, + lineHeight: 36, + fontSize: 28, + }, + headlineSmallEmphasized: { + ...emphasizedMediumType, + lineHeight: 32, + fontSize: 24, + }, + + titleLargeEmphasized: { + ...emphasizedMediumType, + lineHeight: 28, + fontSize: 22, + }, + titleMediumEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.15, + lineHeight: 24, + fontSize: 16, + }, + titleSmallEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.1, + lineHeight: 20, + fontSize: 14, + }, + + labelLargeEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.1, + lineHeight: 20, + fontSize: 14, + }, + labelMediumEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.5, + lineHeight: 16, + fontSize: 12, + }, + labelSmallEmphasized: { + ...emphasizedBoldType, + letterSpacing: 0.5, + lineHeight: 16, + fontSize: 11, + }, + + bodyLargeEmphasized: { + ...emphasizedMediumType, + letterSpacing: 0.5, + lineHeight: 24, + fontSize: 16, + }, + bodyMediumEmphasized: { + ...emphasizedMediumType, + letterSpacing: 0.25, + lineHeight: 20, + fontSize: 14, + }, + bodySmallEmphasized: { + ...emphasizedMediumType, + letterSpacing: 0.4, + lineHeight: 16, + fontSize: 12, + }, + + default: { + ...regularType, + }, +}; export const defaultFonts: Typescale = typescale; diff --git a/src/theme/types/color.ts b/src/theme/types/color.ts index 1e49724b3e..84879a36ec 100644 --- a/src/theme/types/color.ts +++ b/src/theme/types/color.ts @@ -2,6 +2,10 @@ import type { ColorValue } from 'react-native'; import type { ElevationColors } from './elevation'; +export type ColorRole = { + [K in keyof ThemeColors]: ThemeColors[K] extends ColorValue ? K : never; +}[keyof ThemeColors]; + export type ThemeColors = { primary: ColorValue; primaryContainer: ColorValue; diff --git a/src/theme/types/index.ts b/src/theme/types/index.ts index cc1ffe21ef..b45fea45d5 100644 --- a/src/theme/types/index.ts +++ b/src/theme/types/index.ts @@ -3,6 +3,6 @@ export * from './elevation'; export * from './motion'; export * from './navigation'; export * from './shape'; +export * from './state'; export * from './theme'; export * from './typography'; -export * from './utils'; diff --git a/src/theme/types/motion.ts b/src/theme/types/motion.ts index a0e037f524..f744e93d08 100644 --- a/src/theme/types/motion.ts +++ b/src/theme/types/motion.ts @@ -45,3 +45,9 @@ export type MotionConfig = { easing: MotionEasing; duration: MotionDuration; }; + +export type RawSpring = { + stiffness: number; + damping: number; + mass: number; +}; diff --git a/src/theme/types/state.ts b/src/theme/types/state.ts new file mode 100644 index 0000000000..7577d21ce4 --- /dev/null +++ b/src/theme/types/state.ts @@ -0,0 +1,10 @@ +import type { ColorValue } from 'react-native'; + +import type { tokens } from '../tokens'; + +export type StateOpacityKey = keyof typeof tokens.md.sys.state.opacity; + +export type StateLayer = { + color: ColorValue; + opacity: number; +}; diff --git a/src/theme/types/utils.ts b/src/theme/types/utils.ts deleted file mode 100644 index 0ea6b262b9..0000000000 --- a/src/theme/types/utils.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type * as React from 'react'; - -export type $Omit = Pick>; -export type $RemoveChildren> = $Omit< - React.ComponentPropsWithoutRef, - 'children' ->; - -export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; diff --git a/src/theme/utils/color.ts b/src/theme/utils/color.ts new file mode 100644 index 0000000000..c13e8797e3 --- /dev/null +++ b/src/theme/utils/color.ts @@ -0,0 +1,66 @@ +import type { ColorValue } from 'react-native'; + +import type { Theme } from '../types'; + +/** + * Resolve the on-color (content color) that pairs with a given background + * role from the theme's color scheme. + * + * If `background` does not match any known role color (e.g. an arbitrary hex + * value), falls back to `onSurface`. + * + * @example + * const fg = contentColorFor(theme, theme.colors.primaryContainer); + * // theme.colors.onPrimaryContainer + */ +export function contentColorFor( + theme: Theme, + background: ColorValue +): ColorValue { + const c = theme.colors; + + switch (background) { + case c.primary: + return c.onPrimary; + case c.secondary: + return c.onSecondary; + case c.tertiary: + return c.onTertiary; + case c.background: + return c.onBackground; + case c.error: + return c.onError; + case c.primaryContainer: + return c.onPrimaryContainer; + case c.secondaryContainer: + return c.onSecondaryContainer; + case c.tertiaryContainer: + return c.onTertiaryContainer; + case c.errorContainer: + return c.onErrorContainer; + case c.inverseSurface: + return c.inverseOnSurface; + case c.surfaceVariant: + return c.onSurfaceVariant; + case c.surface: + case c.surfaceBright: + case c.surfaceDim: + case c.surfaceContainerLowest: + case c.surfaceContainerLow: + case c.surfaceContainer: + case c.surfaceContainerHigh: + case c.surfaceContainerHighest: + return c.onSurface; + case c.primaryFixed: + case c.primaryFixedDim: + return c.onPrimaryFixed; + case c.secondaryFixed: + case c.secondaryFixedDim: + return c.onSecondaryFixed; + case c.tertiaryFixed: + case c.tertiaryFixedDim: + return c.onTertiaryFixed; + default: + return c.onSurface; + } +} diff --git a/src/theme/utils/state.ts b/src/theme/utils/state.ts new file mode 100644 index 0000000000..8addc54876 --- /dev/null +++ b/src/theme/utils/state.ts @@ -0,0 +1,23 @@ +import { tokens } from '../tokens'; +import type { ColorRole, StateLayer, StateOpacityKey, Theme } from '../types'; + +const stateOpacity = tokens.md.sys.state.opacity; + +/** + * Resolve a `{ color, opacity }` state-layer for a color role + interaction + * state. Reads `theme.colors[role]` live, so it stays correct under deeply-merged theme overrides. + * + * @example + * const stateLayer = getStateLayer(theme, 'primary', 'hovered'); + * // { color: theme.colors.primary, opacity: 0.08 } + */ +export function getStateLayer( + theme: Theme, + role: ColorRole, + state: StateOpacityKey +): StateLayer { + return { + color: theme.colors[role], + opacity: stateOpacity[state], + }; +} diff --git a/src/types.tsx b/src/types.tsx index 261b55f0b3..c84e3915b4 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -1 +1,11 @@ +import type * as React from 'react'; + export * from './theme/types'; + +export type $Omit = Pick>; +export type $RemoveChildren> = $Omit< + React.ComponentPropsWithoutRef, + 'children' +>; + +export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; From 316b9aeece29455888910770c18945c23ed6d589 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Tue, 26 May 2026 17:22:46 +0200 Subject: [PATCH 29/94] chore: migrate example to react navigation static config (#4965) --- .circleci/config.yml | 19 +- example/package.json | 8 +- example/src/DrawerItems.tsx | 31 +-- example/src/ExampleList.tsx | 154 ++++++------ example/src/Examples/AppbarExample.tsx | 10 +- .../Examples/BottomNavigationBarExample.tsx | 185 +++++++-------- .../src/Examples/BottomNavigationExample.tsx | 10 +- example/src/Examples/MenuExample.tsx | 10 +- example/src/Examples/SearchbarExample.tsx | 11 +- .../src/Examples/SegmentedButtonsExample.tsx | 12 +- example/src/Examples/TeamDetails.tsx | 24 +- example/src/Examples/TeamsList.tsx | 11 +- example/src/Examples/ThemeExample.tsx | 10 +- .../Examples/ThemingWithReactNavigation.tsx | 127 +++++----- example/src/Examples/TooltipExample.tsx | 10 +- example/src/RootNavigator.tsx | 120 ++++++---- example/src/index.native.tsx | 206 ---------------- example/src/index.tsx | 221 ++++++++++++------ package.json | 2 +- yarn.lock | 74 +++--- 20 files changed, 529 insertions(+), 726 deletions(-) delete mode 100644 example/src/index.native.tsx diff --git a/.circleci/config.yml b/.circleci/config.yml index c4053c5f19..ad05234255 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.1 executors: default: docker: - - image: cimg/node:20.19.0 + - image: cimg/node:22.15.0 working_directory: ~/react-native-paper commands: @@ -20,30 +20,17 @@ jobs: - attach_project - restore_cache: keys: - - dependencies-{{ checksum "package.json" }} + - dependencies-{{ checksum "yarn.lock" }} - dependencies- - - restore_cache: - keys: - - dependencies-example-{{ checksum "example/package.json" }} - - dependencies-example- - run: name: Install dependencies command: | - sudo corepack enable - yarn --cwd example --immutable - yarn --cwd docs --immutable yarn --immutable - save_cache: - key: dependencies-{{ checksum "package.json" }} + key: dependencies-{{ checksum "yarn.lock" }} paths: - node_modules - - save_cache: - key: dependencies-docs-{{ checksum "docs/package.json" }} - paths: - docs/node_modules - - save_cache: - key: dependencies-example-{{ checksum "example/package.json" }} - paths: - example/node_modules - persist_to_workspace: root: . diff --git a/example/package.json b/example/package.json index 36603ea4aa..992ead9c23 100644 --- a/example/package.json +++ b/example/package.json @@ -13,10 +13,10 @@ "dependencies": { "@react-native-async-storage/async-storage": "2.2.0", "@react-native-masked-view/masked-view": "0.3.2", - "@react-navigation/bottom-tabs": "^8.0.0-alpha.30", - "@react-navigation/drawer": "^8.0.0-alpha.31", - "@react-navigation/native": "^8.0.0-alpha.25", - "@react-navigation/native-stack": "^8.0.0-alpha.31", + "@react-navigation/bottom-tabs": "^8.0.0-alpha.33", + "@react-navigation/drawer": "^8.0.0-alpha.34", + "@react-navigation/native": "^8.0.0-alpha.28", + "@react-navigation/native-stack": "^8.0.0-alpha.34", "expo": "~56.0.0", "expo-crypto": "~56.0.3", "expo-dev-client": "~56.0.13", diff --git a/example/src/DrawerItems.tsx b/example/src/DrawerItems.tsx index f9df80433c..ea4627bb64 100644 --- a/example/src/DrawerItems.tsx +++ b/example/src/DrawerItems.tsx @@ -1,15 +1,8 @@ import * as React from 'react'; -import { - ColorValue, - I18nManager, - Platform, - StyleSheet, - View, -} from 'react-native'; +import { ColorValue, Platform, StyleSheet, View } from 'react-native'; import { DrawerContentScrollView } from '@react-navigation/drawer'; import Constants, { ExecutionEnvironment } from 'expo-constants'; -import * as Updates from 'expo-updates'; import { Badge, Button, @@ -124,16 +117,12 @@ function DrawerItems() { } = preferences; const _handleToggleRTL = () => { - if (expoGoExecution) { + if (!isWeb && expoGoExecution) { setShowRTLDialog(true); return; } toggleRTL(); - I18nManager.forceRTL(!isRTL); - if (isWeb) { - Updates.reloadAsync(); - } }; const _handleDismissRTLDialog = () => { @@ -206,16 +195,14 @@ function DrawerItems() { - {!isWeb && ( - - - RTL - - - + + + RTL + + - - )} + + diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index 7b874d4906..bdf5af7428 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { FlatList } from 'react-native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { Divider, List, useTheme } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -49,102 +49,76 @@ import ThemingWithReactNavigation from './Examples/ThemingWithReactNavigation'; import ToggleButtonExample from './Examples/ToggleButtonExample'; import TooltipExample from './Examples/TooltipExample'; import TouchableRippleExample from './Examples/TouchableRippleExample'; -export const mainExamples: Record< - string, - React.ComponentType & { title: string } -> = { - animatedFab: AnimatedFABExample, - activityIndicator: ActivityIndicatorExample, - appbar: AppbarExample, - avatar: AvatarExample, - badge: BadgeExample, - banner: BannerExample, - bottomNavigationBarExample: BottomNavigationBarExample, - bottomNavigation: BottomNavigationExample, - button: ButtonExample, - card: CardExample, - checkbox: CheckboxExample, - checkboxItem: CheckboxItemExample, - chip: ChipExample, - dataTable: DataTableExample, - dialog: DialogExample, - divider: DividerExample, - fab: FABExample, - iconButton: IconButtonExample, - icon: IconExample, - listAccordion: ListAccordionExample, - listAccordionGroup: ListAccordionExampleGroup, - listSection: ListSectionExample, - listItem: ListItemExample, - menu: MenuExample, - progressbar: ProgressBarExample, - radio: RadioButtonExample, - radioGroup: RadioButtonGroupExample, - radioItem: RadioButtonItemExample, - searchbar: SearchbarExample, - segmentedButton: SegmentedButtonExample, - snackbar: SnackbarExample, - surface: SurfaceExample, - switch: SwitchExample, - text: TextExample, - textInput: TextInputExample, - toggleButton: ToggleButtonExample, - tooltipExample: TooltipExample, - touchableRipple: TouchableRippleExample, - theme: ThemeExample, - themingWithReactNavigation: ThemingWithReactNavigation, + +export const mainExamples = { + AnimatedFAB: AnimatedFABExample, + ActivityIndicator: ActivityIndicatorExample, + Appbar: AppbarExample, + Avatar: AvatarExample, + Badge: BadgeExample, + Banner: BannerExample, + BottomNavigationBarExample, + BottomNavigation: BottomNavigationExample, + Button: ButtonExample, + Card: CardExample, + Checkbox: CheckboxExample, + CheckboxItem: CheckboxItemExample, + Chip: ChipExample, + DataTable: DataTableExample, + Dialog: DialogExample, + Divider: DividerExample, + FAB: FABExample, + IconButton: IconButtonExample, + Icon: IconExample, + ListAccordion: ListAccordionExample, + ListAccordionGroup: ListAccordionExampleGroup, + ListSection: ListSectionExample, + ListItem: ListItemExample, + Menu: MenuExample, + Progressbar: ProgressBarExample, + Radio: RadioButtonExample, + RadioGroup: RadioButtonGroupExample, + RadioItem: RadioButtonItemExample, + Searchbar: SearchbarExample, + SegmentedButton: SegmentedButtonExample, + Snackbar: SnackbarExample, + Surface: SurfaceExample, + Switch: SwitchExample, + Text: TextExample, + TextInput: TextInputExample, + ToggleButton: ToggleButtonExample, + TooltipExample, + TouchableRipple: TouchableRippleExample, + Theme: ThemeExample, + ThemingWithReactNavigation, }; -export const nestedExamples: Record< - string, - React.ComponentType & { title: string } -> = { - themingWithReactNavigation: ThemingWithReactNavigation, - teamDetails: TeamDetails, - teamsList: TeamsList, - segmentedButtonRealCase: SegmentedButtonRealCase, - segmentedButtonMultiselectRealCase: SegmentedButtonMultiselectRealCase, +export const nestedExamples = { + ThemingWithReactNavigation, + TeamDetails, + TeamsList, + SegmentedButtonRealCase, + SegmentedButtonMultiselectRealCase, }; -export const examples: Record< - string, - React.ComponentType & { title: string } -> = { +export const examples = { ...mainExamples, ...nestedExamples, }; -type Props = { - navigation: NativeStackNavigationProp<{ [key: string]: undefined }>; -}; - -type Item = { - id: string; - data: (typeof mainExamples)[string]; -}; +type MainExampleId = keyof typeof mainExamples; -const data = Object.keys(mainExamples).map( - (id): Item => ({ id, data: mainExamples[id] }) -); +const data = (Object.keys(mainExamples) as MainExampleId[]).map((id) => ({ + id, + data: mainExamples[id], +})); -export default function ExampleList({ navigation }: Props) { - const keyExtractor = (item: { id: string }) => item.id; +export default function ExampleList() { + const navigation = useNavigation('ExampleList'); const { colors } = useTheme(); const safeArea = useSafeAreaInsets(); - const renderItem = ({ item }: { item: Item }) => { - const { data, id } = item; - - return ( - navigation.navigate(id)} - /> - ); - }; - return ( ( + { + // @ts-expect-error TypeScript can't call overloaded functions with union arguments. + // https://github.com/microsoft/TypeScript/issues/40803 + navigation.navigate(item.id); + }} + /> + )} + keyExtractor={({ id }) => id} data={data} /> ); diff --git a/example/src/Examples/AppbarExample.tsx b/example/src/Examples/AppbarExample.tsx index a8d07e581e..846dd61182 100644 --- a/example/src/Examples/AppbarExample.tsx +++ b/example/src/Examples/AppbarExample.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { Appbar, FAB, @@ -17,16 +17,14 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import ScreenWrapper from '../ScreenWrapper'; -type Props = { - navigation: NativeStackNavigationProp<{}>; -}; - type AppbarModes = 'small' | 'medium' | 'large' | 'center-aligned'; const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; const MEDIUM_FAB_HEIGHT = 56; -const AppbarExample = ({ navigation }: Props) => { +const AppbarExample = () => { + const navigation = useNavigation('Appbar'); + const [showLeftIcon, setShowLeftIcon] = React.useState(true); const [showSubtitle, setShowSubtitle] = React.useState(true); const [showSearchIcon, setShowSearchIcon] = React.useState(true); diff --git a/example/src/Examples/BottomNavigationBarExample.tsx b/example/src/Examples/BottomNavigationBarExample.tsx index 083d6172fb..9fbddf7f23 100644 --- a/example/src/Examples/BottomNavigationBarExample.tsx +++ b/example/src/Examples/BottomNavigationBarExample.tsx @@ -2,7 +2,10 @@ import React from 'react'; import { View, StyleSheet, Image } from 'react-native'; import Icon from '@react-native-vector-icons/material-design-icons'; -import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { + createBottomTabNavigator, + createBottomTabScreen, +} from '@react-navigation/bottom-tabs'; import { CommonActions, SFSymbol, @@ -10,8 +13,6 @@ import { } from '@react-navigation/native'; import { Text, BottomNavigation } from 'react-native-paper'; -const Tab = createBottomTabNavigator(); - function HomeScreen() { return ( @@ -28,106 +29,98 @@ function SettingsScreen() { ); } -export default function BottomNavigationBarExample() { - return ( - ( - { - const event = navigation.emit({ - type: 'tabPress', - target: route.key, - canPreventDefault: true, - }); +const BottomNavigationBarExample = createBottomTabNavigator({ + screenOptions: { + headerShown: false, + }, + tabBar: ({ navigation, state, descriptors }) => ( + { + const event = navigation.emit({ + type: 'tabPress', + target: route.key, + canPreventDefault: true, + }); - if (event.defaultPrevented) { - preventDefault(); - } else { - navigation.dispatch({ - ...CommonActions.navigate(route.name, route.params), - target: state.key, - }); - } - }} - renderIcon={({ route, focused, color }) => { - const tabBarIcon = descriptors[route.key].options.tabBarIcon; + if (event.defaultPrevented) { + preventDefault(); + } else { + // Custom tab bars must target the tab navigator state. + navigation.dispatch({ + ...CommonActions.navigate(route.name, route.params), + target: state.key, + }); + } + }} + renderIcon={({ route, focused, color }) => { + const tabBarIcon = descriptors[route.key].options.tabBarIcon; - const size = 24; - const icon = - typeof tabBarIcon === 'function' - ? tabBarIcon({ focused, color, size }) - : tabBarIcon; + const size = 24; + const icon = + typeof tabBarIcon === 'function' + ? tabBarIcon({ focused, color, size }) + : tabBarIcon; - if (icon == null) { - return null; - } + if (icon == null) { + return null; + } - if (React.isValidElement(icon)) { - return icon; - } + if (React.isValidElement(icon)) { + return icon; + } - if (typeof icon === 'object' && icon !== null && 'type' in icon) { - switch (icon.type) { - case 'sfSymbol': - return ( - - ); - case 'materialSymbol': - return ( - - ); - case 'image': - return ( - - ); - } - } + if (typeof icon === 'object' && icon !== null && 'type' in icon) { + switch (icon.type) { + case 'sfSymbol': + return ; + case 'materialSymbol': + return ( + + ); + case 'image': + return ( + + ); + } + } - throw new Error( - 'Tab bar icon is not a valid React element, SFSymbol, MaterialSymbol, or Image.' - ); - }} - getLabelText={({ route }) => descriptors[route.key].route.name} - /> - )} - > - { - return ; - }, - }} - /> - { - return ; - }, - }} - /> - - ); -} + throw new Error( + 'Tab bar icon is not a valid React element, SFSymbol, MaterialSymbol, or Image.' + ); + }} + getLabelText={({ route }) => descriptors[route.key].route.name} + /> + ), + screens: { + Home: createBottomTabScreen({ + screen: HomeScreen, + options: { + tabBarIcon: ({ color, size }) => { + return ; + }, + }, + }), + Settings: createBottomTabScreen({ + screen: SettingsScreen, + options: { + tabBarIcon: ({ color, size }) => { + return ; + }, + }, + }), + }, +}); -BottomNavigationBarExample.title = 'Bottom Navigation Bar'; +export default Object.assign(BottomNavigationBarExample, { + title: 'Bottom Navigation Bar', +}); const styles = StyleSheet.create({ container: { diff --git a/example/src/Examples/BottomNavigationExample.tsx b/example/src/Examples/BottomNavigationExample.tsx index e12bb58ad1..0c0fc8d4cc 100644 --- a/example/src/Examples/BottomNavigationExample.tsx +++ b/example/src/Examples/BottomNavigationExample.tsx @@ -8,7 +8,7 @@ import { View, } from 'react-native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { Appbar, BottomNavigation, @@ -21,10 +21,6 @@ import ScreenWrapper from '../ScreenWrapper'; type Route = { route: { key: string } }; -type Props = { - navigation: NativeStackNavigationProp<{}>; -}; - const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; const PhotoGallery = ({ route }: Route) => { @@ -48,7 +44,9 @@ const PhotoGallery = ({ route }: Route) => { ); }; -const BottomNavigationExample = ({ navigation }: Props) => { +const BottomNavigationExample = () => { + const navigation = useNavigation('BottomNavigation'); + const insets = useSafeAreaInsets(); const [index, setIndex] = React.useState(0); const [menuVisible, setMenuVisible] = React.useState(false); diff --git a/example/src/Examples/MenuExample.tsx b/example/src/Examples/MenuExample.tsx index a263b7ee39..831e811574 100644 --- a/example/src/Examples/MenuExample.tsx +++ b/example/src/Examples/MenuExample.tsx @@ -6,7 +6,7 @@ import { View, } from 'react-native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { Appbar, Button, @@ -20,17 +20,15 @@ import ScreenWrapper from '../ScreenWrapper'; type ContextualMenuCoord = { x: number; y: number }; -type Props = { - navigation: NativeStackNavigationProp<{}>; -}; - type MenuVisibility = { [key: string]: boolean | undefined; }; const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; -const MenuExample = ({ navigation }: Props) => { +const MenuExample = () => { + const navigation = useNavigation('Menu'); + const [visible, setVisible] = React.useState({}); const [contextualMenuCoord, setContextualMenuCoor] = React.useState({ x: 0, y: 0 }); diff --git a/example/src/Examples/SearchbarExample.tsx b/example/src/Examples/SearchbarExample.tsx index a293f2c85a..739002156b 100644 --- a/example/src/Examples/SearchbarExample.tsx +++ b/example/src/Examples/SearchbarExample.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; import { Keyboard, StyleSheet } from 'react-native'; -import type { DrawerNavigationProp } from '@react-navigation/drawer'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { Avatar, List, @@ -14,11 +13,9 @@ import { import ScreenWrapper from '../ScreenWrapper'; -type Props = { - navigation: NativeStackNavigationProp<{}>; -}; +const SearchExample = () => { + const navigation = useNavigation('Searchbar'); -const SearchExample = ({ navigation }: Props) => { const [isVisible, setIsVisible] = React.useState(false); const [searchQueries, setSearchQuery] = React.useState({ searchBarMode: '', @@ -194,7 +191,7 @@ const SearchExample = ({ navigation }: Props) => { value={searchQueries.clickableDrawer} onIconPress={() => { Keyboard.dismiss(); - (navigation as any as DrawerNavigationProp<{}>).openDrawer(); + navigation.openDrawer(); }} icon="menu" style={styles.searchbar} diff --git a/example/src/Examples/SegmentedButtonsExample.tsx b/example/src/Examples/SegmentedButtonsExample.tsx index 5bfe3ba8b5..d6f2d610a4 100644 --- a/example/src/Examples/SegmentedButtonsExample.tsx +++ b/example/src/Examples/SegmentedButtonsExample.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { List } from 'react-native-paper'; import { @@ -16,25 +16,23 @@ import { } from './SegmentedButtons'; import ScreenWrapper from '../ScreenWrapper'; -type Props = { - navigation: NativeStackNavigationProp<{ [key: string]: undefined }>; -}; +const SegmentedButtonExample = () => { + const navigation = useNavigation('SegmentedButton'); -const SegmentedButtonExample = ({ navigation }: Props) => { return ( navigation.navigate('segmentedButtonRealCase')} + onPress={() => navigation.navigate('SegmentedButtonRealCase')} right={(props) => } /> - navigation.navigate('segmentedButtonMultiselectRealCase') + navigation.navigate('SegmentedButtonMultiselectRealCase') } right={(props) => } /> diff --git a/example/src/Examples/TeamDetails.tsx b/example/src/Examples/TeamDetails.tsx index a4b1f41a0b..5970274f31 100644 --- a/example/src/Examples/TeamDetails.tsx +++ b/example/src/Examples/TeamDetails.tsx @@ -8,7 +8,7 @@ import { View, } from 'react-native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation, useRoute } from '@react-navigation/native'; import { Appbar, BottomNavigation, @@ -37,20 +37,6 @@ type RoutesState = Array<{ getTestID?: string; }>; -type Route = { - route: { key: string }; - params: { - sourceColor: string; - headerTitle: string; - darkMode: boolean; - }; -}; - -type Props = { - navigation: NativeStackNavigationProp<{}>; - route: Route; -}; - type Item = { host: string; guest: string; @@ -177,12 +163,14 @@ const Results = () => { ); }; -const ThemeBasedOnSourceColor = ({ navigation, route }: Props) => { +const ThemeBasedOnSourceColor = () => { + const navigation = useNavigation('TeamDetails'); + const route = useRoute('TeamDetails'); + const insets = useSafeAreaInsets(); const [index, setIndex] = React.useState(0); - const { params } = route; - const { sourceColor, headerTitle, darkMode } = params; + const { sourceColor, headerTitle, darkMode } = route.params ?? {}; const [routes] = React.useState([ { diff --git a/example/src/Examples/TeamsList.tsx b/example/src/Examples/TeamsList.tsx index 69e07b93aa..98abd9cbbb 100644 --- a/example/src/Examples/TeamsList.tsx +++ b/example/src/Examples/TeamsList.tsx @@ -1,16 +1,15 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import type { ParamListBase } from '@react-navigation/native'; -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { DataTable } from 'react-native-paper'; import { teamsList } from '../../utils'; import ScreenWrapper from '../ScreenWrapper'; -type Props = NativeStackScreenProps; +const TeamsList = () => { + const navigation = useNavigation('TeamsList'); -const TeamsList = ({ navigation }: Props) => { return ( @@ -25,10 +24,10 @@ const TeamsList = ({ navigation }: Props) => { - navigation.navigate('teamDetails', { + navigation.navigate('TeamDetails', { sourceColor: item.name.split(' ')[1].toLowerCase(), headerTitle: item.name, - darkMode: item.darkMode, + darkMode: item.darkMode ?? false, }) } > diff --git a/example/src/Examples/ThemeExample.tsx b/example/src/Examples/ThemeExample.tsx index 54d4e9ba41..39f033aa06 100644 --- a/example/src/Examples/ThemeExample.tsx +++ b/example/src/Examples/ThemeExample.tsx @@ -1,16 +1,14 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { List, PaperProvider, Banner } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; -type Props = { - navigation: NativeStackNavigationProp<{ [key: string]: undefined }>; -}; +const ThemeExample = () => { + const navigation = useNavigation('Theme'); -const ThemeExample = ({ navigation }: Props) => { return ( @@ -23,7 +21,7 @@ const ThemeExample = ({ navigation }: Props) => { navigation.navigate('teamsList')} + onPress={() => navigation.navigate('TeamsList')} right={(props) => } /> diff --git a/example/src/Examples/ThemingWithReactNavigation.tsx b/example/src/Examples/ThemingWithReactNavigation.tsx index 633af0bd76..394640ae6c 100644 --- a/example/src/Examples/ThemingWithReactNavigation.tsx +++ b/example/src/Examples/ThemingWithReactNavigation.tsx @@ -4,15 +4,13 @@ import { View, StyleSheet, Platform } from 'react-native'; import Icon from '@react-native-vector-icons/material-design-icons'; import { createBottomTabNavigator, + createBottomTabScreen, type BottomTabNavigationOptions, } from '@react-navigation/bottom-tabs'; import { PlatformPressable } from '@react-navigation/elements'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { Text } from 'react-native-paper'; -const Stack = createNativeStackNavigator(); -const Tab = createBottomTabNavigator(); - function HomeScreen() { return ( @@ -29,72 +27,63 @@ function SettingsScreen() { ); } -const HomeTab = () => { - return ( - ( - - ), - }} - > - ( - { - android: { - type: 'materialSymbol', - name: 'home', - }, - ios: { - type: 'sfSymbol', - name: 'house', - }, - default: ({ color, size }) => { - return ; - }, - } - ), - }} - /> - ( - { - android: { - type: 'materialSymbol', - name: 'settings', - }, - ios: { - type: 'sfSymbol', - name: 'gear', - }, - default: ({ color, size }) => { - return ; - }, - } - ), - }} - /> - - ); -}; +const HomeTab = createBottomTabNavigator({ + screenOptions: { + headerShown: false, + tabBarButton: (props) => ( + + ), + }, + screens: { + ThemingHome: createBottomTabScreen({ + screen: HomeScreen, + options: { + tabBarIcon: Platform.select({ + android: { + type: 'materialSymbol', + name: 'home', + }, + ios: { + type: 'sfSymbol', + name: 'house', + }, + default: ({ color, size }) => { + return ; + }, + }), + }, + }), + ThemingSettings: createBottomTabScreen({ + screen: SettingsScreen, + options: { + tabBarIcon: Platform.select({ + android: { + type: 'materialSymbol', + name: 'settings', + }, + ios: { + type: 'sfSymbol', + name: 'gear', + }, + default: ({ color, size }) => { + return ; + }, + }), + }, + }), + }, +}); -function ThemingWithReactNavigation() { - return ( - - - - ); -} +const ThemingWithReactNavigation = Object.assign( + createNativeStackNavigator({ + screens: { + 'React Navigation': HomeTab, + }, + }), + { + title: 'Theming With React Navigation', + } +); const styles = StyleSheet.create({ container: { @@ -104,6 +93,4 @@ const styles = StyleSheet.create({ }, }); -ThemingWithReactNavigation.title = 'Theming With React Navigation'; - export default ThemingWithReactNavigation; diff --git a/example/src/Examples/TooltipExample.tsx b/example/src/Examples/TooltipExample.tsx index d499892e31..ebe86e74f2 100644 --- a/example/src/Examples/TooltipExample.tsx +++ b/example/src/Examples/TooltipExample.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Platform, StyleSheet, View, Image } from 'react-native'; -import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; import { Appbar, Avatar, @@ -18,10 +18,6 @@ import { import { isWeb } from '../../utils'; import ScreenWrapper from '../ScreenWrapper'; -type Props = { - navigation: NativeStackNavigationProp<{}>; -}; - const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; const DURATION_MEDIUM = 1500; @@ -41,7 +37,9 @@ const formOfTransport = [ }, ]; -const TooltipExample = ({ navigation }: Props) => { +const TooltipExample = () => { + const navigation = useNavigation('TooltipExample'); + const [textAlign, setTextAlign] = React.useState('bold'); React.useLayoutEffect(() => { navigation.setOptions({ diff --git a/example/src/RootNavigator.tsx b/example/src/RootNavigator.tsx index dcdbd73359..7e0876dcd0 100644 --- a/example/src/RootNavigator.tsx +++ b/example/src/RootNavigator.tsx @@ -1,62 +1,90 @@ import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; -import type { DrawerNavigationProp } from '@react-navigation/drawer'; -import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import { useNavigation } from '@react-navigation/native'; +import { + createNativeStackNavigator, + createNativeStackScreen, + type NativeStackHeaderProps, +} from '@react-navigation/native-stack'; import { Appbar } from 'react-native-paper'; import ExampleList, { examples } from './ExampleList'; -const Stack = createNativeStackNavigator(); +const { TeamDetails, ...examplesWithoutParams } = examples; + +type ExampleRouteName = keyof typeof examplesWithoutParams; + +const fromEntries = ( + entries: Array<[Key, Value]> +) => Object.fromEntries(entries) as Record; + +function Header({ navigation, route, options, back }: NativeStackHeaderProps) { + const drawerNavigation = useNavigation('Home'); -export default function Root() { return ( - - ({ - header: ({ navigation, route, options, back }) => ( - - {back ? ( - navigation.goBack()} /> - ) : (navigation as any).openDrawer ? ( - - (navigation as any as DrawerNavigationProp<{}>).openDrawer() - } - /> - ) : null} - - - ), - })} - > - + {back ? ( + navigation.goBack()} /> + ) : ( + drawerNavigation.openDrawer()} /> - {(Object.keys(examples) as Array).map((id) => { - return ( - - ); - })} - - + )} + + ); } +const Root = createNativeStackNavigator({ + layout: ({ children }) => {children}, + screenOptions: { + header: (props) =>
, + }, + screens: { + ExampleList: createNativeStackScreen({ + screen: ExampleList, + options: { + title: 'Examples', + }, + linking: '', + }), + ...fromEntries( + ( + Object.entries(examplesWithoutParams) as [ + ExampleRouteName, + (typeof examplesWithoutParams)[ExampleRouteName] + ][] + ).map(([id, screen]) => [ + id, + createNativeStackScreen({ + screen: screen, + options: { + title: screen.title, + headerShown: id !== 'ThemingWithReactNavigation', + }, + }), + ]) + ), + TeamDetails: createNativeStackScreen({ + screen: TeamDetails, + options: { + title: TeamDetails.title, + }, + linking: { + path: 'team-details/:sourceColor?/:headerTitle?/:darkMode?', + parse: { + darkMode: (value) => value === 'true', + }, + }, + }), + }, +}); + +export default Root; + const styles = StyleSheet.create({ stackWrapper: { flex: 1, diff --git a/example/src/index.native.tsx b/example/src/index.native.tsx deleted file mode 100644 index 23b734fe4d..0000000000 --- a/example/src/index.native.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import * as React from 'react'; -import { I18nManager } from 'react-native'; - -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { createDrawerNavigator } from '@react-navigation/drawer'; -import { InitialState, NavigationContainer } from '@react-navigation/native'; -import { useFonts } from 'expo-font'; -import { useKeepAwake } from 'expo-keep-awake'; -import { StatusBar } from 'expo-status-bar'; -import * as Updates from 'expo-updates'; -import { - PaperProvider, - DarkTheme, - LightTheme, - DynamicLightTheme, - DynamicDarkTheme, -} from 'react-native-paper'; -import { SafeAreaInsetsContext } from 'react-native-safe-area-context'; - -import DrawerItems from './DrawerItems'; -import { PreferencesContext } from './PreferencesContext'; -import App from './RootNavigator'; -import { dynamicThemeSupported } from '../utils'; -import { - CombinedDefaultTheme, - CombinedDarkTheme, - createConfiguredFontTheme, - createConfiguredFontNavigationTheme, -} from '../utils/themes'; - -const PERSISTENCE_KEY = 'NAVIGATION_STATE'; -const PREFERENCES_KEY = 'APP_PREFERENCES'; - -const Drawer = createDrawerNavigator<{ Home: undefined }>(); - -export default function PaperExample() { - useKeepAwake(); - - const [fontsLoaded] = useFonts({ - Abel: require('../assets/fonts/Abel-Regular.ttf'), - }); - - const [isReady, setIsReady] = React.useState(false); - const [initialState, setInitialState] = React.useState< - InitialState | undefined - >(); - - const [shouldUseDynamicTheme, setShouldUseDynamicTheme] = - React.useState(true); - const [isDarkMode, setIsDarkMode] = React.useState(false); - const [rtl, setRtl] = React.useState( - I18nManager.getConstants().isRTL - ); - const [collapsed, setCollapsed] = React.useState(false); - const [customFontLoaded, setCustomFont] = React.useState(false); - const [rippleEffectEnabled, setRippleEffectEnabled] = React.useState(true); - - const theme = React.useMemo(() => { - if (dynamicThemeSupported && shouldUseDynamicTheme) { - return isDarkMode ? DynamicDarkTheme : DynamicLightTheme; - } - - return isDarkMode ? DarkTheme : LightTheme; - }, [isDarkMode, shouldUseDynamicTheme]); - - React.useEffect(() => { - const restoreState = async () => { - try { - const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY); - const state = JSON.parse(savedStateString || ''); - - setInitialState(state); - } catch (e) { - // ignore error - } finally { - setIsReady(true); - } - }; - - if (!isReady) { - restoreState(); - } - }, [isReady]); - - React.useEffect(() => { - const restorePrefs = async () => { - try { - const prefString = await AsyncStorage.getItem(PREFERENCES_KEY); - const preferences = JSON.parse(prefString || ''); - - if (preferences) { - setIsDarkMode(preferences.theme === 'dark'); - - if (typeof preferences.rtl === 'boolean') { - setRtl(preferences.rtl); - } - } - } catch (e) { - // ignore error - } - }; - - restorePrefs(); - }, []); - - React.useEffect(() => { - const savePrefs = async () => { - try { - await AsyncStorage.setItem( - PREFERENCES_KEY, - JSON.stringify({ - theme: isDarkMode ? 'dark' : 'light', - rtl, - }) - ); - } catch (e) { - // ignore error - } - - if (I18nManager.getConstants().isRTL !== rtl) { - I18nManager.forceRTL(rtl); - Updates.reloadAsync(); - } - }; - - savePrefs(); - }, [rtl, isDarkMode]); - - const preferences = React.useMemo( - () => ({ - toggleShouldUseDynamicTheme: () => - setShouldUseDynamicTheme((oldValue) => !oldValue), - toggleTheme: () => setIsDarkMode((oldValue) => !oldValue), - toggleRtl: () => setRtl((rtl) => !rtl), - toggleCollapsed: () => setCollapsed(!collapsed), - toggleCustomFont: () => setCustomFont(!customFontLoaded), - toggleRippleEffect: () => setRippleEffectEnabled(!rippleEffectEnabled), - customFontLoaded, - rippleEffectEnabled, - collapsed, - rtl, - theme, - shouldUseDynamicTheme: shouldUseDynamicTheme, - }), - [ - rtl, - theme, - collapsed, - customFontLoaded, - shouldUseDynamicTheme, - rippleEffectEnabled, - ] - ); - - if (!isReady && !fontsLoaded) { - return null; - } - - const combinedTheme = isDarkMode ? CombinedDarkTheme : CombinedDefaultTheme; - const configuredFontTheme = createConfiguredFontTheme(combinedTheme); - const configuredFontNavigationTheme = - createConfiguredFontNavigationTheme(combinedTheme); - - return ( - - - - AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)) - } - > - - {(insets) => { - const { left, right } = insets || { left: 0, right: 0 }; - const collapsedDrawerWidth = 100 + Math.max(left, right); - return ( - } - > - - - ); - }} - - - - - - ); -} diff --git a/example/src/index.tsx b/example/src/index.tsx index df291baf3f..688297b059 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -1,16 +1,30 @@ import * as React from 'react'; +import { I18nManager, Platform } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import { createDrawerNavigator } from '@react-navigation/drawer'; -import { InitialState, NavigationContainer } from '@react-navigation/native'; +import { + createDrawerNavigator, + createDrawerScreen, +} from '@react-navigation/drawer'; +import { createStaticNavigation } from '@react-navigation/native'; import { useFonts } from 'expo-font'; import { useKeepAwake } from 'expo-keep-awake'; -import { PaperProvider, DarkTheme, LightTheme } from 'react-native-paper'; -import { SafeAreaInsetsContext } from 'react-native-safe-area-context'; +import * as SplashScreen from 'expo-splash-screen'; +import { StatusBar } from 'expo-status-bar'; +import * as Updates from 'expo-updates'; +import { + PaperProvider, + DarkTheme, + LightTheme, + DynamicLightTheme, + DynamicDarkTheme, +} from 'react-native-paper'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import DrawerItems from './DrawerItems'; import { PreferencesContext } from './PreferencesContext'; import App from './RootNavigator'; +import { dynamicThemeSupported } from '../utils'; import { CombinedDarkTheme, CombinedDefaultTheme, @@ -20,9 +34,53 @@ import { const PERSISTENCE_KEY = 'NAVIGATION_STATE'; const PREFERENCES_KEY = 'APP_PREFERENCES'; -const noop = () => {}; -const Drawer = createDrawerNavigator<{ Home: undefined }>(); +const getInitialRtl = () => { + if (Platform.OS === 'web' && typeof document !== 'undefined') { + return document.documentElement.dir === 'rtl'; + } + + return I18nManager.getConstants().isRTL; +}; + +const Drawer = createDrawerNavigator({ + screens: { + Home: createDrawerScreen({ + screen: App, + options: { + headerShown: false, + }, + linking: { + path: '', + initialRouteName: 'ExampleList', + }, + }), + }, +}).with(({ Navigator }) => { + const preferences = React.useContext(PreferencesContext); + const insets = useSafeAreaInsets(); + const { left, right } = insets || { left: 0, right: 0 }; + const collapsedDrawerWidth = 100 + Math.max(left, right); + + return ( + } + /> + ); +}); + +const Navigation = createStaticNavigation(Drawer); + +type RootDrawerType = typeof Drawer; + +declare module '@react-navigation/core' { + interface RootNavigator extends RootDrawerType {} +} export default function PaperExample() { useKeepAwake(); @@ -32,38 +90,25 @@ export default function PaperExample() { }); const [isReady, setIsReady] = React.useState(false); - const [initialState, setInitialState] = React.useState< - InitialState | undefined - >(); + const [shouldUseDynamicTheme, setShouldUseDynamicTheme] = + React.useState(true); const [isDarkMode, setIsDarkMode] = React.useState(false); + const [rtl, setRtl] = React.useState(getInitialRtl); const [collapsed, setCollapsed] = React.useState(false); const [customFontLoaded, setCustomFont] = React.useState(false); const [rippleEffectEnabled, setRippleEffectEnabled] = React.useState(true); - const theme = React.useMemo( - () => (isDarkMode ? DarkTheme : LightTheme), - [isDarkMode] - ); - - React.useEffect(() => { - const restoreState = async () => { - try { - const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY); - const state = JSON.parse(savedStateString || ''); + const theme = + dynamicThemeSupported && shouldUseDynamicTheme + ? isDarkMode + ? DynamicDarkTheme + : DynamicLightTheme + : isDarkMode + ? DarkTheme + : LightTheme; - setInitialState(state); - } catch (e) { - // ignore error - } finally { - setIsReady(true); - } - }; - - if (!isReady) { - restoreState(); - } - }, [isReady]); + const direction = rtl ? 'rtl' : 'ltr'; React.useEffect(() => { const restorePrefs = async () => { @@ -73,9 +118,15 @@ export default function PaperExample() { if (preferences) { setIsDarkMode(preferences.theme === 'dark'); + + if (typeof preferences.rtl === 'boolean') { + setRtl(preferences.rtl); + } } } catch (e) { // ignore error + } finally { + setIsReady(true); } }; @@ -84,41 +135,65 @@ export default function PaperExample() { React.useEffect(() => { const savePrefs = async () => { + if (!isReady) { + return; + } + try { await AsyncStorage.setItem( PREFERENCES_KEY, JSON.stringify({ theme: isDarkMode ? 'dark' : 'light', + rtl, }) ); } catch (e) { // ignore error } + + if (Platform.OS === 'web' && typeof document !== 'undefined') { + document.documentElement.dir = direction; + } + + if (I18nManager.getConstants().isRTL !== rtl) { + I18nManager.forceRTL(rtl); + + if (Platform.OS !== 'web') { + Updates.reloadAsync(); + } + } }; savePrefs(); - }, [isDarkMode]); + }, [direction, isDarkMode, isReady, rtl]); const preferences = React.useMemo( () => ({ + toggleShouldUseDynamicTheme: () => + setShouldUseDynamicTheme((oldValue) => !oldValue), toggleTheme: () => setIsDarkMode((oldValue) => !oldValue), - toggleCollapsed: () => setCollapsed(!collapsed), - toggleCustomFont: () => setCustomFont(!customFontLoaded), - toggleRippleEffect: () => setRippleEffectEnabled(!rippleEffectEnabled), + toggleRtl: () => setRtl((oldValue) => !oldValue), + toggleCollapsed: () => setCollapsed((oldValue) => !oldValue), + toggleCustomFont: () => setCustomFont((oldValue) => !oldValue), + toggleRippleEffect: () => setRippleEffectEnabled((oldValue) => !oldValue), + customFontLoaded, + rippleEffectEnabled, + shouldUseDynamicTheme, + theme, + collapsed, + rtl, + }), + [ + rtl, theme, collapsed, customFontLoaded, + shouldUseDynamicTheme, rippleEffectEnabled, - // noop for web, specified to avoid type errors - toggleRtl: noop, - toggleShouldUseDynamicTheme: noop, - rtl: false, - shouldUseDynamicTheme: false, - }), - [theme, collapsed, customFontLoaded, rippleEffectEnabled] + ] ); - if (!isReady && !fontsLoaded) { + if (!isReady || !fontsLoaded) { return null; } @@ -126,45 +201,41 @@ export default function PaperExample() { const configuredFontTheme = createConfiguredFontTheme(combinedTheme); const configuredFontNavigationTheme = createConfiguredFontNavigationTheme(combinedTheme); + const paperTheme = customFontLoaded ? configuredFontTheme : theme; + const navigationTheme = customFontLoaded + ? configuredFontNavigationTheme + : combinedTheme; return ( - - AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)) - } - > - - {(insets) => { - const { left, right } = insets || { left: 0, right: 0 }; - const collapsedDrawerWidth = 100 + Math.max(left, right); - return ( - } - > - - + - + }, + async restore() { + const state = await AsyncStorage.getItem(PERSISTENCE_KEY); + + return state ? JSON.parse(state) : undefined; + }, + }} + onReady={() => { + SplashScreen.hideAsync(); + }} + /> + {Platform.OS !== 'web' ? ( + + ) : null} ); diff --git a/package.json b/package.json index 6a8cffabb2..34ae7e3d9e 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@react-native-vector-icons/material-design-icons": "^12.0.0", "@react-native/babel-preset": "^0.85.3", "@react-native/jest-preset": "^0.85.3", - "@react-navigation/native": "^8.0.0-alpha.25", + "@react-navigation/native": "^8.0.0-alpha.28", "@release-it/conventional-changelog": "^1.1.0", "@testing-library/jest-native": "^5.4.1", "@testing-library/react-native": "11.5.0", diff --git a/yarn.lock b/yarn.lock index bc18b34207..63b967951e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5325,24 +5325,24 @@ __metadata: languageName: node linkType: hard -"@react-navigation/bottom-tabs@npm:^8.0.0-alpha.30": - version: 8.0.0-alpha.30 - resolution: "@react-navigation/bottom-tabs@npm:8.0.0-alpha.30" +"@react-navigation/bottom-tabs@npm:^8.0.0-alpha.33": + version: 8.0.0-alpha.33 + resolution: "@react-navigation/bottom-tabs@npm:8.0.0-alpha.33" dependencies: - "@react-navigation/elements": "npm:^3.0.0-alpha.28" + "@react-navigation/elements": "npm:^3.0.0-alpha.31" peerDependencies: - "@react-navigation/native": ^8.0.0-alpha.25 + "@react-navigation/native": ^8.0.0-alpha.28 react: ">= 19.2.0" react-native: "*" react-native-safe-area-context: ">= 5.5.0" react-native-screens: ">= 4.25.0" - checksum: 10c0/e0c3399f1fc160667fe2c37b2a8ae72d65bdcd0fc2ea0009d3d1eeccaf4336f1c97db21af02f29b07d0ba9bd278a341ac8c6c3ab4b93d709eb0432e6fa0c40ff + checksum: 10c0/94a2f3c6ec0f266d0bd53066069bdcfc8e59563530cec4519bb34aca6f248e79295d367fb1a63299658081411fb4b93718927a04d7d41fa399c3b6fbdf874f26 languageName: node linkType: hard -"@react-navigation/core@npm:^8.0.0-alpha.16": - version: 8.0.0-alpha.16 - resolution: "@react-navigation/core@npm:8.0.0-alpha.16" +"@react-navigation/core@npm:^8.0.0-alpha.19": + version: 8.0.0-alpha.19 + resolution: "@react-navigation/core@npm:8.0.0-alpha.19" dependencies: "@react-navigation/routers": "npm:^8.0.0-alpha.10" escape-string-regexp: "npm:^5.0.0" @@ -5354,19 +5354,19 @@ __metadata: use-sync-external-store: "npm:^1.6.0" peerDependencies: react: ">= 19.2.0" - checksum: 10c0/f376844b9d506200e74c53834d3246c935c932c335a764cedc5724bdf566d174bb0f8247f995447480ca0bad2a4d72e9d33506aa7b58076a69711171eef12928 + checksum: 10c0/bb0ce7e49b063cf423effcc8b668c5b73ad83ea61abcdd807aebe04cf1ededfe4042e13a1ba92fae09d8c1ed5f4fcc8faf51e8603c8a9f7a710d66be051b5ec0 languageName: node linkType: hard -"@react-navigation/drawer@npm:^8.0.0-alpha.31": - version: 8.0.0-alpha.31 - resolution: "@react-navigation/drawer@npm:8.0.0-alpha.31" +"@react-navigation/drawer@npm:^8.0.0-alpha.34": + version: 8.0.0-alpha.34 + resolution: "@react-navigation/drawer@npm:8.0.0-alpha.34" dependencies: - "@react-navigation/elements": "npm:^3.0.0-alpha.28" + "@react-navigation/elements": "npm:^3.0.0-alpha.31" react-native-drawer-layout: "npm:^5.0.0-alpha.10" use-latest-callback: "npm:^0.3.3" peerDependencies: - "@react-navigation/native": ^8.0.0-alpha.25 + "@react-navigation/native": ^8.0.0-alpha.28 react: ">= 19.2.0" react-native: "*" react-native-gesture-handler: ">= 2.0.0" @@ -5374,49 +5374,49 @@ __metadata: react-native-safe-area-context: ">= 5.5.0" react-native-screens: ">= 4.25.0" react-native-worklets: ">= 0.4.0" - checksum: 10c0/4d7e6bb701475e918f50a78f5c88032bdc159ef7c1f7b3d7c33e3ba07be943a93a872d8dc32118d14bb04e9225df78eeff0bafb9c37c673c614af10ede1c734d + checksum: 10c0/0e1b6a0b34b06c4d42e7495619a4871a7cee4f2fa798259607f4399c7c8716ff0d6a1a0da0025d1a83b6c7097f252ac67b136a1d7de533ce35d0d61f11e8b0c2 languageName: node linkType: hard -"@react-navigation/elements@npm:^3.0.0-alpha.28": - version: 3.0.0-alpha.28 - resolution: "@react-navigation/elements@npm:3.0.0-alpha.28" +"@react-navigation/elements@npm:^3.0.0-alpha.31": + version: 3.0.0-alpha.31 + resolution: "@react-navigation/elements@npm:3.0.0-alpha.31" dependencies: sf-symbols-typescript: "npm:^2.2.0" use-latest-callback: "npm:^0.3.3" use-sync-external-store: "npm:^1.6.0" peerDependencies: "@callstack/liquid-glass": ">= 0.6.0" - "@react-navigation/native": ^8.0.0-alpha.25 + "@react-navigation/native": ^8.0.0-alpha.28 react: ">= 19.2.0" react-native: "*" react-native-safe-area-context: ">= 5.5.0" react-native-screens: ">= 4.25.0" - checksum: 10c0/b08b7d37fcdb08b3baa63491460576d808839b294d979c157ac7c26f5eab94901bba1d493cdfe83f65aab1995d2c08aaa4ac69edc425437b910878ec59a907b6 + checksum: 10c0/a3a4433fba4cb15a7cb043fdd6033ee09c834e03a34b91f2fdee02527f5f19951ff2841f583dc569a7b0b44f5030c6313cd5ebc10a2743700a33fc404bcdc906 languageName: node linkType: hard -"@react-navigation/native-stack@npm:^8.0.0-alpha.31": - version: 8.0.0-alpha.31 - resolution: "@react-navigation/native-stack@npm:8.0.0-alpha.31" +"@react-navigation/native-stack@npm:^8.0.0-alpha.34": + version: 8.0.0-alpha.34 + resolution: "@react-navigation/native-stack@npm:8.0.0-alpha.34" dependencies: - "@react-navigation/elements": "npm:^3.0.0-alpha.28" + "@react-navigation/elements": "npm:^3.0.0-alpha.31" warn-once: "npm:^0.1.1" peerDependencies: - "@react-navigation/native": ^8.0.0-alpha.25 + "@react-navigation/native": ^8.0.0-alpha.28 react: ">= 19.2.0" react-native: "*" react-native-safe-area-context: ">= 5.5.0" react-native-screens: ">= 4.25.0" - checksum: 10c0/4595225a7ad5b94563486ac331a3c6d1c9045591c9a258f7bafed6e140d1ba1b6a43bd03b2f0cc348baa160e30769879638f90baf256d106a045c9efa6b9bc52 + checksum: 10c0/322de7b9e0ad1ddcec95d96c793807a11b8d20cb0c008d70863d8401d97b8620277c39c6f93eac65e854f583d1d078542979cfa52ce30b6e188c13eaa001b842 languageName: node linkType: hard -"@react-navigation/native@npm:^8.0.0-alpha.25": - version: 8.0.0-alpha.25 - resolution: "@react-navigation/native@npm:8.0.0-alpha.25" +"@react-navigation/native@npm:^8.0.0-alpha.28": + version: 8.0.0-alpha.28 + resolution: "@react-navigation/native@npm:8.0.0-alpha.28" dependencies: - "@react-navigation/core": "npm:^8.0.0-alpha.16" + "@react-navigation/core": "npm:^8.0.0-alpha.19" escape-string-regexp: "npm:^5.0.0" fast-deep-equal: "npm:^3.1.3" nanoid: "npm:^5.1.7" @@ -5425,7 +5425,7 @@ __metadata: peerDependencies: react: ">= 19.2.0" react-native: "*" - checksum: 10c0/146d0c8b752b8bafb975892f2efb103ff75b58462adc9ae0bd54a9c2a286a878fdcb32fb9008252f56304387608e43e5f4d6e2924ea3797b2141648ed315412e + checksum: 10c0/2221366589b1fce39578c7dc25e420902585ea0eed02214deca7795a407f56dc607a97bc0e346e4e01502fe181269d3075380b3c7af75a8b70f73e0ddb5585c4 languageName: node linkType: hard @@ -20557,10 +20557,10 @@ __metadata: "@babel/core": "npm:^7.29.0" "@react-native-async-storage/async-storage": "npm:2.2.0" "@react-native-masked-view/masked-view": "npm:0.3.2" - "@react-navigation/bottom-tabs": "npm:^8.0.0-alpha.30" - "@react-navigation/drawer": "npm:^8.0.0-alpha.31" - "@react-navigation/native": "npm:^8.0.0-alpha.25" - "@react-navigation/native-stack": "npm:^8.0.0-alpha.31" + "@react-navigation/bottom-tabs": "npm:^8.0.0-alpha.33" + "@react-navigation/drawer": "npm:^8.0.0-alpha.34" + "@react-navigation/native": "npm:^8.0.0-alpha.28" + "@react-navigation/native-stack": "npm:^8.0.0-alpha.34" babel-preset-expo: "npm:~56.0.0" expo: "npm:~56.0.0" expo-crypto: "npm:~56.0.3" @@ -20597,7 +20597,7 @@ __metadata: "@react-native-vector-icons/material-design-icons": "npm:^12.0.0" "@react-native/babel-preset": "npm:^0.85.3" "@react-native/jest-preset": "npm:^0.85.3" - "@react-navigation/native": "npm:^8.0.0-alpha.25" + "@react-navigation/native": "npm:^8.0.0-alpha.28" "@release-it/conventional-changelog": "npm:^1.1.0" "@testing-library/jest-native": "npm:^5.4.1" "@testing-library/react-native": "npm:11.5.0" From fb06c27947b3af30a784245e1fb29164d96356db Mon Sep 17 00:00:00 2001 From: Ruben Rebelo <39693995+ruben-rebelo@users.noreply.github.com> Date: Tue, 26 May 2026 16:40:59 +0100 Subject: [PATCH 30/94] ci: convert CircleCi to Github workflows (#4884) Co-authored-by: Satyajit Sahoo --- .circleci/comment-artifacts.sh | 22 ---- .circleci/config.yml | 141 -------------------------- .circleci/deploy-docs.sh | 72 ------------- .github/actions/setup/action.yml | 36 +++++++ .github/workflows/ci.yml | 96 ++++++++++++++++++ .github/workflows/deploy-docs.yml | 44 ++++++++ .github/workflows/publish-each-pr.yml | 93 ----------------- .github/workflows/semantic-pr.yml | 2 +- .github/workflows/stale.yml | 3 +- .github/workflows/triage.yaml | 8 +- .github/workflows/updates.yml | 34 +------ .github/workflows/versions.yml | 2 +- 12 files changed, 187 insertions(+), 366 deletions(-) delete mode 100644 .circleci/comment-artifacts.sh delete mode 100644 .circleci/config.yml delete mode 100755 .circleci/deploy-docs.sh create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy-docs.yml delete mode 100644 .github/workflows/publish-each-pr.yml diff --git a/.circleci/comment-artifacts.sh b/.circleci/comment-artifacts.sh deleted file mode 100644 index 7991e99012..0000000000 --- a/.circleci/comment-artifacts.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -URL="https://callstack-github-bot.herokuapp.com/comment" -LINK="https://output.circle-artifacts.com/output/job/$CIRCLE_WORKFLOW_JOB_ID/artifacts/$CIRCLE_NODE_INDEX/docs/index.html" -TEMPLATE="Hey @{{user.login}}, thank you for your pull request 🤗. The documentation from this branch can be viewed [here]($LINK)." - -read -r -d '' DATA << EOM -{ - "pull_request": "$CIRCLE_PULL_REQUEST", - "template": "$TEMPLATE", - "test": { - "type": "string", - "data": "The documentation from this branch can be viewed" - }, - "update": true -} -EOM - -curl \ - -H "Content-Type: application/json" \ - -d "$DATA" \ - -X POST $URL diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index ad05234255..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,141 +0,0 @@ -version: 2.1 - -executors: - default: - docker: - - image: cimg/node:22.15.0 - working_directory: ~/react-native-paper - -commands: - attach_project: - steps: - - attach_workspace: - at: ~/react-native-paper - -jobs: - install-dependencies: - executor: default - steps: - - checkout - - attach_project - - restore_cache: - keys: - - dependencies-{{ checksum "yarn.lock" }} - - dependencies- - - run: - name: Install dependencies - command: | - yarn --immutable - - save_cache: - key: dependencies-{{ checksum "yarn.lock" }} - paths: - - node_modules - - docs/node_modules - - example/node_modules - - persist_to_workspace: - root: . - paths: - - . - lint: - executor: default - steps: - - attach_project - - run: - name: Lint files - command: | - yarn lint-no-fix - - typescript: - executor: default - steps: - - attach_project - - run: - name: Typecheck files - command: | - yarn typescript - - unit-tests: - executor: default - steps: - - attach_project - - restore_cache: - name: Restore Jest Cache - keys: - - jest-cache-{{ .Branch }} - - jest-cache - - run: - name: Run unit tests - command: | - yarn test --maxWorkers=2 --coverage - - store_artifacts: - path: coverage - destination: coverage - - save_cache: - paths: - - ./cache/jest - key: jest-cache-{{ .Branch }} - - build-package: - executor: default - steps: - - attach_project - - run: - name: Build package - command: | - yarn prepack - node ./scripts/typescript-output-lint - - build-docs: - executor: default - steps: - - attach_project - - run: - name: Build docs - command: | - export DOCUSAURUS_BASE_URL="/output/job/$CIRCLE_WORKFLOW_JOB_ID/artifacts/$CIRCLE_NODE_INDEX/docs/" - cd docs && yarn build && cd .. - bash .circleci/comment-artifacts.sh - - store_artifacts: - path: docs/build - destination: docs - - deploy-docs: - executor: default - steps: - - checkout - - attach_project - - add_ssh_keys: - fingerprints: - - '2f:b4:ed:e1:25:88:5c:4a:03:c2:16:40:56:70:29:29' - - run: - name: Deploy docs to GitHub pages - command: | - bash .circleci/deploy-docs.sh - -workflows: - build-and-test: - jobs: - - install-dependencies - - lint: - requires: - - install-dependencies - - typescript: - requires: - - install-dependencies - - unit-tests: - requires: - - install-dependencies - - build-package: - requires: - - install-dependencies - - build-docs: - requires: - - install-dependencies - filters: - branches: - ignore: main - - deploy-docs: - filters: - branches: - only: - - main diff --git a/.circleci/deploy-docs.sh b/.circleci/deploy-docs.sh deleted file mode 100755 index 5546f8bb2c..0000000000 --- a/.circleci/deploy-docs.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# Based on domenic's tutorial: https://gist.github.com/domenic/ec8b0fc8ab45f39403dd - -set -euxo pipefail # Exit with nonzero exit code if anything fails - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -SOURCE_BRANCH="main" -TARGET_BRANCH="gh-pages" - -git checkout $SOURCE_BRANCH - -cd $DIR/.. - -# Install React Native Paper dependencies for examples -yarn - -cd docs - -# Save some useful information -REPO=`git config remote.origin.url` -SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:} -SHA=`git rev-parse --verify HEAD` - -# Clone the existing gh-pages for this repo into dist/ -# Create a new empty branch if gh-pages doesn't exist yet (should only happen on first deploy) -git clone $REPO dist -cd dist -git checkout $TARGET_BRANCH || git checkout --orphan $TARGET_BRANCH -cd .. - -# Clean existing dist/ contents -rm -rf dist/**/* || : -rm -f dist/*.{html,css,js,json,map,xml} || : -rmdir dist/* || : - -# Run our build script. -yarn -yarn build - -# Move the built docs to cloned `gh-pages` directory -cp -R build/. dist - -rm -rf build - -# Change directory to the one using `gh-pages` branch -cd dist - -# Configure git. -git config user.name "$COMMIT_AUTHOR_NAME" -git config user.email "$COMMIT_AUTHOR_EMAIL" - -git add -A . - -# If there are no changes to the compiled dist (e.g. this is a README update) then just bail. -if git diff --cached --quiet; then - echo "No changes to the output on this push; exiting." - exit 0 -fi - -# Commit the "changes", i.e. the new version. -# The delta will show diffs between new and old versions. -git commit -m "Deploy to GitHub Pages: ${SHA}" - -# Now that we're all set up, we can push. -git push $SSH_REPO $TARGET_BRANCH - -# Change back to original branch -cd .. -git checkout $SOURCE_BRANCH -yarn diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000000..66e709c9e8 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,36 @@ +name: Setup +description: Setup Node.js and install dependencies + +runs: + using: composite + steps: + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e + with: + node-version-file: .nvmrc + + - name: Restore dependencies + id: yarn-cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae + with: + path: | + **/node_modules + .yarn/install-state.gz + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} + restore-keys: | + ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + ${{ runner.os }}-yarn- + + - name: Install dependencies + if: steps.yarn-cache.outputs.cache-hit != 'true' + run: yarn install --immutable + shell: bash + + - name: Cache dependencies + if: steps.yarn-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae + with: + path: | + **/node_modules + .yarn/install-state.gz + key: ${{ steps.yarn-cache.outputs.cache-primary-key }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..8096174f9a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,96 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Lint + run: yarn lint-no-fix + + typescript: + name: TypeScript + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Check types + run: yarn typescript + + unit-tests: + name: Unit tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Restore Jest cache + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ./cache/jest + key: jest-cache-${{ github.ref_name }} + restore-keys: jest-cache- + + - name: Run unit tests + run: yarn test --maxWorkers=2 --coverage + + - name: Upload coverage + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: always() + with: + name: coverage + path: coverage + + build-package: + name: Build package + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build package + run: | + yarn prepack + node ./scripts/typescript-output-lint + + build-docs: + name: Build docs + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build docs + run: yarn --cwd docs build + + - name: Upload docs + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: docs + path: docs/build diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000000..d6eb089477 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,44 @@ +name: Deploy docs + +on: + push: + branches: + - main + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + deploy: + name: Deploy docs + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build docs + run: yarn --cwd docs build + + - name: Configure Pages + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: docs/build + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/.github/workflows/publish-each-pr.yml b/.github/workflows/publish-each-pr.yml deleted file mode 100644 index 0fa519e55f..0000000000 --- a/.github/workflows/publish-each-pr.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Expo Preview -on: [pull_request] - -permissions: - contents: read - pull-requests: write - -jobs: - publish: - name: Install and publish - runs-on: ubuntu-latest - if: github.event.pull_request.head.repo.full_name == 'callstack/react-native-paper' - steps: - - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - - name: Setup Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version-file: .nvmrc - - - name: Setup Expo - uses: expo/expo-github-action@d300b960e9f91a8c59b2aaca92e89ad70b0785ac # v7 - with: - eas-version: latest - token: ${{ secrets.EXPO_TOKEN }} - - - name: Restore dependencies - id: yarn-cache - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4 - with: - path: '**/node_modules' - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} - restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - ${{ runner.os }}-yarn- - - - name: Install dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - run: | - sudo corepack enable - yarn --cwd example --immutable - yarn --immutable - - - name: Cache dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4 - with: - path: '**/node_modules' - key: ${{ steps.yarn-cache.outputs.cache-primary-key }} - - - name: Publish Expo app - working-directory: ./example - run: CI=1 rm -rf android && eas update --branch=pr-${{ github.event.number }} --message="$(git log -1 --pretty=%B)" - - - name: Get expo config - working-directory: ./example - id: expo - run: echo "EXPO_CONFIG=$(npx expo config --json)" >> $GITHUB_OUTPUT - - - name: Comment on PR - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const config = JSON.parse('${{ steps.expo.outputs.EXPO_CONFIG }}'); - - const { sdkVersion } = config; - const { projectId } = config.extra.eas; - - const channel = 'pr-${{ github.event.number }}'; - - const url = `https://expo.dev/@react-native-paper/react-native-paper-example?serviceType=eas&distribution=expo-go&scheme=exp+react-native-paper-example&channel=${channel}&sdkVersion=${sdkVersion}`; - const body = `The mobile version of example app from this branch is ready! You can [see it here.](${url}) - - `; - - const comments = await github.rest.issues.listComments({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - }); - - if (comments.data.some(comment => comment.body === body)) { - return; - } - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body - }) diff --git a/.github/workflows/semantic-pr.yml b/.github/workflows/semantic-pr.yml index 632323c6a5..371b3c504a 100644 --- a/.github/workflows/semantic-pr.yml +++ b/.github/workflows/semantic-pr.yml @@ -6,7 +6,7 @@ jobs: name: Validate PR title runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@91682d013dea3ff257520b9b68c9cb93ced4fe9b # v4.5.0 + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 7b6949f2fb..1b4619fbde 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -5,7 +5,6 @@ on: - cron: '30 1 * * *' permissions: - contents: read issues: write pull-requests: write @@ -13,7 +12,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 diff --git a/.github/workflows/triage.yaml b/.github/workflows/triage.yaml index c9c69eb8bb..26e574ef24 100644 --- a/.github/workflows/triage.yaml +++ b/.github/workflows/triage.yaml @@ -12,8 +12,8 @@ jobs: runs-on: ubuntu-latest if: github.event.label.name == 'needs more info' steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | @@ -40,8 +40,8 @@ jobs: runs-on: ubuntu-latest if: github.event.label.name == 'needs repro' steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/updates.yml b/.github/workflows/updates.yml index fa753fd5bf..336a01184e 100644 --- a/.github/workflows/updates.yml +++ b/.github/workflows/updates.yml @@ -10,44 +10,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Setup Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version-file: .nvmrc + - name: Setup + uses: ./.github/actions/setup - name: Setup Expo - uses: expo/expo-github-action@d300b960e9f91a8c59b2aaca92e89ad70b0785ac # v7 + uses: expo/expo-github-action@c7b66a9c327a43a8fa7c0158e7f30d6040d2481e # v8.2.1 with: expo-version: latest eas-version: latest token: ${{ secrets.EXPO_TOKEN }} - - name: Restore dependencies - id: yarn-cache - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4 - with: - path: '**/node_modules' - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} - restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - ${{ runner.os }}-yarn- - - - name: Install dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - run: | - sudo corepack enable - yarn --cwd example --immutable - yarn --immutable - - - name: Cache dependencies - if: steps.yarn-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4 - with: - path: '**/node_modules' - key: ${{ steps.yarn-cache.outputs.cache-primary-key }} - - name: Publish Expo app update working-directory: ./example run: eas update --auto diff --git a/.github/workflows/versions.yml b/.github/workflows/versions.yml index a84b362955..bcadaaeab1 100644 --- a/.github/workflows/versions.yml +++ b/.github/workflows/versions.yml @@ -12,7 +12,7 @@ jobs: if: ${{ github.event.label.name == 'bug' }} runs-on: ubuntu-latest steps: - - uses: react-navigation/check-versions-action@deac0a153b834fdda425028be69b2cf786dacc31 # v1.1.0 + - uses: react-navigation/check-versions-action@1f7edac18020a4dc38c4d174bb04311b196f9b75 # v1.2.2 with: github-token: ${{ secrets.GITHUB_TOKEN }} required-packages: | From db27e5abc632a867150619f4ebbc48c085253118 Mon Sep 17 00:00:00 2001 From: Bogusz Kaszowski Date: Sat, 9 May 2026 16:31:25 +0200 Subject: [PATCH 31/94] fix(animated-fab): correct Android shadow/content stacking (#4903) --- src/components/FAB/AnimatedFAB.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx index 84b14a6a92..5ca2348614 100644 --- a/src/components/FAB/AnimatedFAB.tsx +++ b/src/components/FAB/AnimatedFAB.tsx @@ -118,6 +118,8 @@ export type Props = $Omit<$RemoveChildren, 'mode'> & { }; const SIZE = 56; +const SHADOW_LAYER_Z_INDEX = 1; +const CONTENT_LAYER_Z_INDEX = 2; /** * An animated, extending horizontally floating action button represents the primary action in an application. @@ -553,9 +555,19 @@ const styles = StyleSheet.create({ innerWrapper: { flexDirection: 'row', overflow: 'hidden', + ...Platform.select({ + android: { + zIndex: CONTENT_LAYER_Z_INDEX, + }, + }), }, shadowWrapper: { elevation: 0, + ...Platform.select({ + android: { + zIndex: SHADOW_LAYER_Z_INDEX, + }, + }), }, shadow: { elevation: 3, From a4eded629c6d5de8b71dd5cfdac401b51eba6ae5 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Thu, 28 May 2026 03:02:06 +0200 Subject: [PATCH 32/94] chore: throw on broken doc links and fix existing broken links --- docs/docs/guides/05-react-native-web.md | 2 +- docs/docusaurus.config.js | 6 ++++-- src/components/Dialog/Dialog.tsx | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/docs/guides/05-react-native-web.md b/docs/docs/guides/05-react-native-web.md index d9b69b8250..747fee8b05 100644 --- a/docs/docs/guides/05-react-native-web.md +++ b/docs/docs/guides/05-react-native-web.md @@ -6,7 +6,7 @@ title: Using on the Web React Native Paper supports web via [React Native for Web](https://necolas.github.io/react-native-web/), which lets you run React Native components in a browser using React DOM. -Before continuing, make sure you have React Native Paper installed and configured by following the [Getting Started guide](getting-started.md). +Before continuing, make sure you have React Native Paper installed and configured by following the [Getting Started guide](01-getting-started.mdx). ## Setting up web support with Expo diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index bc75f57c94..55de22c833 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -37,8 +37,6 @@ const config = { tagline: 'Material Design for React Native', url, baseUrl, - onBrokenLinks: 'log', - onBrokenMarkdownLinks: 'log', favicon: 'images/favicon.ico', // GitHub pages deployment config. @@ -54,6 +52,10 @@ const config = { locales: ['en'], }, + onBrokenLinks: 'throw', + onDuplicateRoutes: 'throw', + onBrokenMarkdownLinks: 'throw', + plugins: [ [ './component-docs-plugin', diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 717445aed7..209d606afc 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -55,7 +55,7 @@ const DIALOG_ELEVATION: number = 24; /** * Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. - * To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../../Portal) component. + * To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component. * * ## Usage * ```js From de574b5d7542b431a352222f6ab63e08532a109c Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 19 May 2026 14:25:12 +0300 Subject: [PATCH 33/94] feat: modernize Switch to MD3 * BREAKING CHANGE: Switch props no longer extend native Switch. color, ios_backgroundColor, trackColor, thumbColor etc. are removed. Use the theme prop for color customization. --- example/src/Examples/SwitchExample.tsx | 154 +- jest/testSetup.js | 4 + src/components/Switch/Switch.tsx | 562 ++++++- src/components/Switch/tokens.ts | 53 + src/components/Switch/utils.ts | 203 +-- src/components/__tests__/Switch.test.tsx | 239 +-- .../__snapshots__/Switch.test.tsx.snap | 1291 ++++++++++++++++- src/utils/isKeyboardFocusEvent.ts | 23 + 8 files changed, 2061 insertions(+), 468 deletions(-) create mode 100644 src/components/Switch/tokens.ts create mode 100644 src/utils/isKeyboardFocusEvent.ts diff --git a/example/src/Examples/SwitchExample.tsx b/example/src/Examples/SwitchExample.tsx index da959eda47..545dbf8dd3 100644 --- a/example/src/Examples/SwitchExample.tsx +++ b/example/src/Examples/SwitchExample.tsx @@ -1,73 +1,103 @@ import * as React from 'react'; -import { Platform, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; -import { Palette, Switch, Text, TouchableRipple } from 'react-native-paper'; +import { Switch, Text, useTheme } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; +const Row = ({ + label, + children, +}: { + label: string; + children: React.ReactNode; +}) => ( + + {label} + {children} + +); + const SwitchExample = () => { - const [valueNormal, setNormalValue] = React.useState(true); - const [valueCustom, setCustomValue] = React.useState(true); + const theme = useTheme(); + const [defaultOn, setDefaultOn] = React.useState(true); + const [defaultCheckedIconOn, setDefaultCheckedIconOn] = React.useState(true); + const [defaultIconOn, setDefaultIconOn] = React.useState(true); + const [customOn, setCustomOn] = React.useState(true); + const [customIconOn, setCustomIconOn] = React.useState(true); + const [disableAll, setDisableAll] = React.useState(false); - const switchValueNormalLabel = `switch ${ - valueNormal === true ? 'on' : 'off' - }`; - const switchValueCustomlLabel = `switch ${ - valueCustom === true ? 'on' : 'off' - }`; + const tertiaryTheme = React.useMemo( + () => ({ + colors: { + primary: theme.colors.tertiary, + onPrimary: theme.colors.onTertiary, + primaryContainer: theme.colors.tertiaryContainer, + secondary: theme.colors.tertiary, + }, + }), + [theme] + ); - return Platform.OS === 'android' ? ( + return ( - setNormalValue(!valueNormal)}> - - Normal {switchValueNormalLabel} - - - - - - setCustomValue(!valueCustom)}> - - Custom {switchValueCustomlLabel} - - - - - - - Switch on (disabled) - - - - Switch off (disabled) - - - - ) : ( - - - Normal {switchValueNormalLabel} + + + + + + + + + setNormalValue(!valueNormal)} + value={defaultIconOn} + onValueChange={setDefaultIconOn} + checkedIcon="check" + uncheckedIcon="close" + disabled={disableAll} /> - - - Custom {switchValueCustomlLabel} + + + setCustomValue(!valueCustom)} - color={Palette.tertiary50} + value={customOn} + onValueChange={setCustomOn} + theme={tertiaryTheme} + disabled={disableAll} /> - - - Switch on (disabled) - - - - Switch off (disabled) - - + + + + + + + + + + + ); }; @@ -85,6 +115,16 @@ const styles = StyleSheet.create({ paddingVertical: 8, paddingHorizontal: 16, }, + right: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + }, + separator: { + height: 1, + marginHorizontal: 16, + marginVertical: 16, + }, }); export default SwitchExample; diff --git a/jest/testSetup.js b/jest/testSetup.js index 5088ab5585..5110c85551 100644 --- a/jest/testSetup.js +++ b/jest/testSetup.js @@ -8,6 +8,10 @@ jest.mock('react-native-worklets', () => require('react-native-worklets/lib/module/mock') ); +jest.mock('react-native-reanimated', () => + require('react-native-reanimated/mock') +); + jest.mock('@react-native-vector-icons/material-design-icons', () => { const React = require('react'); const { Text } = require('react-native'); diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index 04b0fef0c6..1eb0c23de6 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -1,108 +1,540 @@ import * as React from 'react'; import { - NativeModules, Platform, - StyleProp, - Switch as NativeSwitch, - ViewStyle, + Pressable, + StyleSheet, + type StyleProp, + View, + type ViewStyle, } from 'react-native'; -import { getSwitchColor } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import Animated, { + Easing, + ReduceMotion, + useAnimatedReaction, + useAnimatedStyle, + useDerivedValue, + useSharedValue, + withDelay, + withSequence, + withSpring, + withTiming, +} from 'react-native-reanimated'; -const version = NativeModules.PlatformConstants - ? NativeModules.PlatformConstants.reactNativeVersion - : undefined; +import { SwitchTokens } from './tokens'; +import { getDefaultSwitchColors, resolveSwitchPaint } from './utils'; +import { useLocale } from '../../core/locale'; +import { useInternalTheme } from '../../core/theming'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; +import { tokens } from '../../theme/tokens'; +import { toRawSpring } from '../../theme/tokens/sys/motion'; +import { cornerFull } from '../../theme/tokens/sys/shape'; +import type { StateOpacityKey, ThemeProp } from '../../types'; +import { isKeyboardFocusEvent } from '../../utils/isKeyboardFocusEvent'; +import Icon, { type IconSource } from '../Icon'; -export type Props = React.ComponentPropsWithRef & { +export type Props = { /** - * Disable toggling the switch. + * Whether the switch is on. */ - disabled?: boolean; + value?: boolean; /** - * Value of the switch, true means 'on', false means 'off'. + * Called with the new value when the user toggles the switch. */ - value?: boolean; + onValueChange?: (value: boolean) => void; /** - * Custom color for switch. + * Disables interaction and renders the disabled visual state. */ - color?: string; + disabled?: boolean; /** - * Callback called with the new value when it changes. + * Icon shown inside the handle when checked */ - onValueChange?: Function; - style?: StyleProp; + checkedIcon?: IconSource; /** - * @optional + * Icon shown inside the handle when unchecked. */ + uncheckedIcon?: IconSource; + style?: StyleProp; + testID?: string; theme?: ThemeProp; + accessibilityLabel?: string; }; +const { + trackWidth: TRACK_WIDTH, + trackHeight: TRACK_HEIGHT, + trackOutlineWidth: TRACK_OUTLINE_WIDTH, + stateLayerSize: STATE_LAYER_SIZE, + selectedHandleSize: SELECTED_HANDLE, + unselectedHandleSize: UNSELECTED_HANDLE, + iconHandleSize: ICON_HANDLE, + pressedHandleSize: PRESSED_HANDLE, + selectedIconSize: SELECTED_ICON, + unselectedIconSize: UNSELECTED_ICON, + disabledSelectedHandleOpacity: DISABLED_SELECTED_HANDLE_OPACITY, + disabledUnselectedHandleOpacity: DISABLED_UNSELECTED_HANDLE_OPACITY, + disabledSelectedIconOpacity: DISABLED_SELECTED_ICON_OPACITY, + disabledUnselectedIconOpacity: DISABLED_UNSELECTED_ICON_OPACITY, + disabledTrackOpacity: DISABLED_TRACK_OPACITY, +} = SwitchTokens; + +const { state: stateTokens } = tokens.md.sys; +const stateOpacity = stateTokens.opacity; +const { thickness: FOCUS_THICKNESS, outerOffset: FOCUS_OUTER_OFFSET } = + stateTokens.focusIndicator; +const FOCUS_RING_INSET = -(FOCUS_OUTER_OFFSET + FOCUS_THICKNESS); +const OVERLAY_TOP = (STATE_LAYER_SIZE - TRACK_HEIGHT) / 2; + +// Hold-then-grow: a brief delay before snapping to PRESSED_HANDLE so a quick +// tap doesn't flash the press-grow visual. +const PRESS_GROW_DELAY = 100; + +function restingHandleSize(checked: boolean, hasIcon: boolean): number { + if (hasIcon) return ICON_HANDLE; + return checked ? SELECTED_HANDLE : UNSELECTED_HANDLE; +} + +const HANDLE_PADDING = (TRACK_HEIGHT - SELECTED_HANDLE) / 2; +const UNCHECKED_CENTER = TRACK_HEIGHT / 2; +const CHECKED_CENTER = TRACK_WIDTH - HANDLE_PADDING - SELECTED_HANDLE / 2; + /** - * Switch is a visual toggle between two mutually exclusive states — on and off. + * Material 3 toggle between two mutually exclusive states (on / off). * * ## Usage * ```js * import * as React from 'react'; * import { Switch } from 'react-native-paper'; * - * const MyComponent = () => { - * const [isSwitchOn, setIsSwitchOn] = React.useState(false); - * - * const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn); - * - * return ; + * const Example = () => { + * const [on, setOn] = React.useState(false); + * return ; * }; - * - * export default MyComponent; * ``` + * + * ## Theming + * Customize by overriding these `theme.colors` roles: + * - `primary` / `onPrimary`: selected track + icon / selected handle + * - `primaryContainer`: selected handle on hover, press + * - `surfaceContainerHighest`: unselected track + icon + * - `outline`: unselected resting handle, unselected track outline + * - `onSurfaceVariant`: unselected handle on hover, press + * - `onSurface`: disabled track, handle, and icon fills + * - `surface`: disabled selected handle + * - `secondary`: focus indicator */ const Switch = ({ value, disabled, onValueChange, - color, + checkedIcon, + uncheckedIcon, + style, + testID, theme: themeOverrides, - ...rest + accessibilityLabel, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { checkedColor, onTintColor, thumbTintColor } = getSwitchColor({ - theme, - disabled, - value, - color, - }); - - const props = - version && version.major === 0 && version.minor <= 56 - ? { - onTintColor, - thumbTintColor, - } - : Platform.OS === 'web' - ? { - activeTrackColor: onTintColor, - thumbColor: thumbTintColor, - activeThumbColor: checkedColor, - } - : { - thumbColor: thumbTintColor, - trackColor: { - true: onTintColor, - false: onTintColor, - }, - }; + const reduceMotion = useReduceMotion(); + const { direction } = useLocale(); + const xSign = direction === 'ltr' ? 1 : -1; + + const checked = !!value; + const isDisabled = !!disabled; + const isEnabled = !isDisabled; + const iconSource = checked ? checkedIcon : uncheckedIcon; + const hasIcon = iconSource !== undefined; + + const anchorStyle: ViewStyle = + Platform.OS === 'web' && direction === 'rtl' ? { right: 0 } : { left: 0 }; + + const pressedSV = useSharedValue(0); + const hoveredSV = useSharedValue(0); + const focusedSV = useSharedValue(0); + const checkedSV = useSharedValue(checked ? 1 : 0); + const hasIconSV = useSharedValue(hasIcon ? 1 : 0); + const isDisabledSV = useSharedValue(isDisabled ? 1 : 0); + + React.useEffect(() => { + checkedSV.value = checked ? 1 : 0; + hasIconSV.value = hasIcon ? 1 : 0; + isDisabledSV.value = isDisabled ? 1 : 0; + }, [checked, hasIcon, isDisabled, checkedSV, hasIconSV, isDisabledSV]); + + const colors = React.useMemo(() => getDefaultSwitchColors(theme), [theme]); + + const reanimatedReduceMotion = reduceMotion + ? ReduceMotion.Always + : ReduceMotion.Never; + + const springConfig = React.useMemo( + () => ({ + ...toRawSpring(theme.motion.spring.fast.spatial), + reduceMotion: reanimatedReduceMotion, + }), + [theme.motion.spring.fast.spatial, reanimatedReduceMotion] + ); + const stateLayerRiseConfig = React.useMemo( + () => ({ + duration: theme.motion.duration.medium3, + easing: Easing.bezier(...theme.motion.easing.standard), + reduceMotion: reanimatedReduceMotion, + }), + [ + theme.motion.duration.medium3, + theme.motion.easing.standard, + reanimatedReduceMotion, + ] + ); + const stateLayerPulseFallConfig = React.useMemo( + () => ({ + duration: theme.motion.duration.medium3, + easing: Easing.bezier(...theme.motion.easing.standardAccelerate), + reduceMotion: reanimatedReduceMotion, + }), + [ + theme.motion.duration.medium3, + theme.motion.easing.standardAccelerate, + reanimatedReduceMotion, + ] + ); + + const handleSize = useSharedValue(restingHandleSize(checked, hasIcon)); + const handleCenter = useSharedValue( + checked ? CHECKED_CENTER : UNCHECKED_CENTER + ); + const stateLayerAlpha = useSharedValue(0); + + useAnimatedReaction( + () => checkedSV.value, + (c, prev) => { + if (c === prev) return; + handleCenter.value = withSpring( + c === 1 ? CHECKED_CENTER : UNCHECKED_CENTER, + springConfig + ); + }, + [springConfig] + ); + + useAnimatedReaction( + () => ({ + p: pressedSV.value, + c: checkedSV.value, + hi: hasIconSV.value, + }), + ({ p, c, hi }) => { + const restingSize = + hi === 1 ? ICON_HANDLE : c === 1 ? SELECTED_HANDLE : UNSELECTED_HANDLE; + if (p === 1) { + handleSize.value = withDelay( + PRESS_GROW_DELAY, + withTiming(PRESSED_HANDLE, { duration: 0 }) + ); + } else { + handleSize.value = withSpring(restingSize, springConfig); + } + }, + [springConfig] + ); + + useAnimatedReaction( + (): StateOpacityKey | null => { + if (pressedSV.value === 1) return 'pressed'; + if (focusedSV.value === 1) return 'focused'; + if (hoveredSV.value === 1) return 'hovered'; + return null; + }, + (current, prev) => { + if (current === prev) return; + const wasPressed = prev === 'pressed'; + const target = + current === 'pressed' + ? stateOpacity.pressed + : current === 'hovered' + ? stateOpacity.hovered + : 0; + if (wasPressed && current !== 'pressed') { + // On release: rise to peak, then fall to the next state. + stateLayerAlpha.value = withSequence( + withTiming(stateOpacity.pressed, stateLayerRiseConfig), + withTiming(target, stateLayerPulseFallConfig) + ); + } else if (current === null) { + stateLayerAlpha.value = 0; + } else { + stateLayerAlpha.value = withTiming(target, stateLayerRiseConfig); + } + }, + [stateLayerRiseConfig, stateLayerPulseFallConfig] + ); + + const handleColorSV = useDerivedValue(() => { + const isCheckedNow = checkedSV.value === 1; + if (isDisabledSV.value === 1) { + return isCheckedNow + ? colors.disabledCheckedHandleColor + : colors.disabledUncheckedHandleColor; + } + if (pressedSV.value === 1) { + return isCheckedNow + ? colors.checkedPressedHandleColor + : colors.uncheckedPressedHandleColor; + } + if (hoveredSV.value === 1) { + return isCheckedNow + ? colors.checkedHoverHandleColor + : colors.uncheckedHoverHandleColor; + } + return isCheckedNow + ? colors.checkedHandleColor + : colors.uncheckedHandleColor; + }, [colors]); + + const handleAnimatedStyle = useAnimatedStyle(() => ({ + width: handleSize.value, + height: handleSize.value, + top: (STATE_LAYER_SIZE - handleSize.value) / 2, + transform: [ + { translateX: xSign * (handleCenter.value - handleSize.value / 2) }, + ], + })); + + const handleFillAnimatedStyle = useAnimatedStyle(() => ({ + backgroundColor: handleColorSV.value, + })); + + const stateLayerAnimatedStyle = useAnimatedStyle(() => ({ + opacity: stateLayerAlpha.value, + transform: [ + { translateX: xSign * (handleCenter.value - STATE_LAYER_SIZE / 2) }, + ], + })); + + // Icon translateX follows the handle's center. + // Vertical position is fixed so the icon never moves vertically + // as the spring oscillates around its target. + const iconAnimatedStyle = useAnimatedStyle(() => ({ + transform: [ + { translateX: xSign * (handleCenter.value - SELECTED_ICON / 2) }, + ], + })); + + const focusRingAnimatedStyle = useAnimatedStyle(() => ({ + opacity: focusedSV.value, + })); + + const paint = resolveSwitchPaint(colors, isEnabled, checked); + const stateLayerColor = checked + ? colors.checkedStateLayerColor + : colors.uncheckedStateLayerColor; + const showOutline = paint.border !== 'transparent'; + const handleOpacity = isDisabled + ? checked + ? DISABLED_SELECTED_HANDLE_OPACITY + : DISABLED_UNSELECTED_HANDLE_OPACITY + : 1; + const iconOpacity = isDisabled + ? checked + ? DISABLED_SELECTED_ICON_OPACITY + : DISABLED_UNSELECTED_ICON_OPACITY + : 1; + const trackOpacityValue = isDisabled ? DISABLED_TRACK_OPACITY : 1; + const iconSize = checked ? SELECTED_ICON : UNSELECTED_ICON; return ( - + + onValueChange?.(!checked)} + onPressIn={() => { + pressedSV.value = 1; + }} + onPressOut={() => { + pressedSV.value = 0; + }} + onHoverIn={() => { + hoveredSV.value = 1; + }} + onHoverOut={() => { + hoveredSV.value = 0; + }} + onFocus={(e) => { + if (!isKeyboardFocusEvent(e)) return; + focusedSV.value = 1; + }} + onBlur={() => { + focusedSV.value = 0; + }} + android_ripple={{ color: 'transparent' }} + accessibilityRole="switch" + accessibilityState={{ disabled: isDisabled, checked }} + accessibilityLabel={accessibilityLabel} + testID={testID} + style={[ + styles.touchable, + Platform.OS === 'web' ? webNoOutline : undefined, + ]} + > + + {showOutline ? ( + + ) : null} + + + + + + + {/* Disabled-only: opaque `surface` backdrop. The tinted fill above + composites over it, reproducing the native math avoiding the PlatformColor alpha limitation. */} + {isDisabled ? ( + + ) : null} + + + {iconSource ? ( + + {/* The same icon twice; one tinted fill on top of the other, + reproducing the native math avoiding the PlatformColor alpha limitation. */} + {isDisabled ? ( + + + + ) : null} + + + + + ) : null} + + + ); }; +const styles = StyleSheet.create({ + wrapper: { + width: TRACK_WIDTH, + height: STATE_LAYER_SIZE, + alignItems: 'center', + justifyContent: 'center', + overflow: 'visible', + }, + touchable: { + width: TRACK_WIDTH, + height: STATE_LAYER_SIZE, + alignItems: 'center', + justifyContent: 'center', + }, + track: { + width: TRACK_WIDTH, + height: TRACK_HEIGHT, + borderRadius: cornerFull, + overflow: 'visible', + }, + outline: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + borderWidth: TRACK_OUTLINE_WIDTH, + borderRadius: cornerFull, + pointerEvents: 'none', + }, + stateLayer: { + position: 'absolute', + top: 0, + width: STATE_LAYER_SIZE, + height: STATE_LAYER_SIZE, + borderRadius: cornerFull, + pointerEvents: 'none', + }, + handle: { + position: 'absolute', + alignItems: 'center', + justifyContent: 'center', + pointerEvents: 'none', + }, + handleFill: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + borderRadius: cornerFull, + }, + iconWrap: { + position: 'absolute', + top: (STATE_LAYER_SIZE - SELECTED_ICON) / 2, + width: SELECTED_ICON, + height: SELECTED_ICON, + pointerEvents: 'none', + }, + focusRing: { + position: 'absolute', + pointerEvents: 'none', + }, + absoluteFill: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, +}); + +// Web-only style; not in StyleSheet because `outline` is outside ViewStyle. +const webNoOutline = { outline: 'none' } as unknown as ViewStyle; + export default Switch; diff --git a/src/components/Switch/tokens.ts b/src/components/Switch/tokens.ts new file mode 100644 index 0000000000..ccde1a9a3f --- /dev/null +++ b/src/components/Switch/tokens.ts @@ -0,0 +1,53 @@ +import type { ColorRole } from '../../theme/types'; + +const sizes = { + trackWidth: 52, + trackHeight: 32, + trackOutlineWidth: 2, + stateLayerSize: 40, + + selectedHandleSize: 24, + unselectedHandleSize: 16, + iconHandleSize: 24, + pressedHandleSize: 28, + selectedIconSize: 16, + unselectedIconSize: 16, + + disabledSelectedHandleOpacity: 1.0, + disabledUnselectedHandleOpacity: 0.38, + disabledSelectedIconOpacity: 0.38, + disabledUnselectedIconOpacity: 0.38, + disabledTrackOpacity: 0.12, +} as const; + +const colors = { + selectedHandleColor: 'onPrimary', + selectedHoverHandleColor: 'primaryContainer', + selectedPressedHandleColor: 'primaryContainer', + + unselectedHandleColor: 'outline', + unselectedHoverHandleColor: 'onSurfaceVariant', + unselectedPressedHandleColor: 'onSurfaceVariant', + + selectedIconColor: 'primary', + unselectedIconColor: 'surfaceContainerHighest', + + selectedTrackColor: 'primary', + unselectedTrackColor: 'surfaceContainerHighest', + unselectedTrackOutlineColor: 'outline', + + disabledSelectedHandleColor: 'surface', + disabledSelectedIconColor: 'onSurface', + disabledSelectedTrackColor: 'onSurface', + disabledUnselectedHandleColor: 'onSurface', + disabledUnselectedIconColor: 'surfaceContainerHighest', + disabledUnselectedTrackColor: 'surfaceContainerHighest', + disabledUnselectedTrackOutlineColor: 'onSurface', + + selectedStateLayerColor: 'primary', + unselectedStateLayerColor: 'onSurface', + + focusIndicatorColor: 'secondary', +} as const satisfies Record; + +export const SwitchTokens = { ...sizes, ...colors }; diff --git a/src/components/Switch/utils.ts b/src/components/Switch/utils.ts index cebf7264f2..e0c7908ded 100644 --- a/src/components/Switch/utils.ts +++ b/src/components/Switch/utils.ts @@ -1,106 +1,115 @@ -import { Platform, type ColorValue } from 'react-native'; +import type { ColorValue } from 'react-native'; -import setColor from 'color'; - -import { - grey400, - grey800, - grey50, - grey700, - white, - black, -} from '../../theme/colors'; +import { SwitchTokens } from './tokens'; import type { InternalTheme } from '../../types'; -type BaseProps = { - theme: InternalTheme; - disabled?: boolean; - value?: boolean; +export type SwitchColors = { + // Enabled, base + checkedHandleColor: ColorValue; + checkedTrackColor: ColorValue; + checkedBorderColor: ColorValue; + checkedIconColor: ColorValue; + uncheckedHandleColor: ColorValue; + uncheckedTrackColor: ColorValue; + uncheckedBorderColor: ColorValue; + uncheckedIconColor: ColorValue; + + // Enabled, interacted handle (track stays constant per spec) + checkedHoverHandleColor: ColorValue; + checkedPressedHandleColor: ColorValue; + uncheckedHoverHandleColor: ColorValue; + uncheckedPressedHandleColor: ColorValue; + + // State layer tints + checkedStateLayerColor: ColorValue; + uncheckedStateLayerColor: ColorValue; + + // Focus indicator ring + focusIndicatorColor: ColorValue; + + // Disabled + disabledCheckedHandleColor: ColorValue; + disabledCheckedTrackColor: ColorValue; + disabledCheckedBorderColor: ColorValue; + disabledCheckedIconColor: ColorValue; + disabledUncheckedHandleColor: ColorValue; + disabledUncheckedTrackColor: ColorValue; + disabledUncheckedBorderColor: ColorValue; + disabledUncheckedIconColor: ColorValue; }; -const getCheckedColor = ({ - theme, - color, -}: { - theme: InternalTheme; - color?: ColorValue; -}) => { - if (color) { - return color; - } +export function getDefaultSwitchColors(theme: InternalTheme): SwitchColors { + const t = SwitchTokens; + const c = theme.colors; + return { + checkedHandleColor: c[t.selectedHandleColor], + checkedTrackColor: c[t.selectedTrackColor], + checkedBorderColor: 'transparent', + checkedIconColor: c[t.selectedIconColor], + + uncheckedHandleColor: c[t.unselectedHandleColor], + uncheckedTrackColor: c[t.unselectedTrackColor], + uncheckedBorderColor: c[t.unselectedTrackOutlineColor], + uncheckedIconColor: c[t.unselectedIconColor], + + checkedHoverHandleColor: c[t.selectedHoverHandleColor], + checkedPressedHandleColor: c[t.selectedPressedHandleColor], + uncheckedHoverHandleColor: c[t.unselectedHoverHandleColor], + uncheckedPressedHandleColor: c[t.unselectedPressedHandleColor], + + checkedStateLayerColor: c[t.selectedStateLayerColor], + uncheckedStateLayerColor: c[t.unselectedStateLayerColor], + + focusIndicatorColor: c[t.focusIndicatorColor], + + disabledCheckedHandleColor: c[t.disabledSelectedHandleColor], + disabledCheckedTrackColor: c[t.disabledSelectedTrackColor], + disabledCheckedBorderColor: 'transparent', + disabledCheckedIconColor: c[t.disabledSelectedIconColor], + disabledUncheckedHandleColor: c[t.disabledUnselectedHandleColor], + disabledUncheckedTrackColor: c[t.disabledUnselectedTrackColor], + disabledUncheckedBorderColor: c[t.disabledUnselectedTrackOutlineColor], + disabledUncheckedIconColor: c[t.disabledUnselectedIconColor], + }; +} - return theme.colors.primary; +export type SwitchPaint = { + track: ColorValue; + border: ColorValue; + icon: ColorValue; }; -const getThumbTintColor = ({ - theme, - disabled, - value, - checkedColor, -}: BaseProps & { checkedColor: ColorValue }) => { - const isIOS = Platform.OS === 'ios'; - - if (isIOS) { - return undefined; - } - - if (disabled) { - if (theme.dark) { - return grey800; - } - return grey400; +/** + * Resolve track / border / icon paint for the current state. Handle color is + * interaction-dependent and animated on the UI thread; resolve it separately. + */ +export function resolveSwitchPaint( + c: SwitchColors, + isEnabled: boolean, + checked: boolean +): SwitchPaint { + if (!isEnabled) { + return checked + ? { + track: c.disabledCheckedTrackColor, + border: c.disabledCheckedBorderColor, + icon: c.disabledCheckedIconColor, + } + : { + track: c.disabledUncheckedTrackColor, + border: c.disabledUncheckedBorderColor, + icon: c.disabledUncheckedIconColor, + }; } - - if (value) { - return checkedColor; - } - - if (theme.dark) { - return grey400; - } - return grey50; -}; - -const getOnTintColor = ({ - theme, - disabled, - value, - checkedColor, -}: BaseProps & { checkedColor: ColorValue }) => { - const isIOS = Platform.OS === 'ios'; - - if (isIOS) { - return checkedColor; - } - - if (disabled) { - if (theme.dark) { - return setColor(white).alpha(0.06).rgb().string(); - } - return setColor(black).alpha(0.12).rgb().string(); - } - - if (value) { - return theme.colors.surfaceContainerHighest; - } - - if (theme.dark) { - return grey700; - } - return 'rgb(178, 175, 177)'; -}; - -export const getSwitchColor = ({ - theme, - disabled, - value, - color, -}: BaseProps & { color?: ColorValue }) => { - const checkedColor = getCheckedColor({ theme, color }); - - return { - onTintColor: getOnTintColor({ theme, disabled, value, checkedColor }), - thumbTintColor: getThumbTintColor({ theme, disabled, value, checkedColor }), - checkedColor, - }; -}; + return checked + ? { + track: c.checkedTrackColor, + border: c.checkedBorderColor, + icon: c.checkedIconColor, + } + : { + track: c.uncheckedTrackColor, + border: c.uncheckedBorderColor, + icon: c.uncheckedIconColor, + }; +} diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index 27637c085b..0c781d67f2 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -1,224 +1,67 @@ import * as React from 'react'; -import { Platform } from 'react-native'; -import color from 'color'; - -import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; -import { - white, - black, - grey400, - grey50, - grey800, - pink500, - grey700, -} from '../../theme/colors'; +import { fireEvent, render } from '../../test-utils'; import Switch from '../Switch/Switch'; -import { getSwitchColor } from '../Switch/utils'; - -it('renders on switch', () => { - const tree = render().toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders off switch', () => { - const tree = render().toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders disabled switch', () => { - const tree = render().toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders switch with color', () => { - const tree = render().toJSON(); - expect(tree).toMatchSnapshot(); -}); - -describe('getSwitchColor - checked color', () => { - it('should return custom color', () => { - expect( - getSwitchColor({ - theme: getTheme(), - color: 'purple', - }) - ).toMatchObject({ - checkedColor: 'purple', - }); - }); - - it('should return theme color, for theme version 3', () => { - expect( - getSwitchColor({ - theme: getTheme(), - }) - ).toMatchObject({ - checkedColor: getTheme().colors.primary, - }); +describe('Switch render', () => { + it('renders on', () => { + expect(render().toJSON()).toMatchSnapshot(); }); -}); - -describe('getSwitchColor - thumb tint color', () => { - it('should return undefined for iOS platform', () => { - Platform.OS = 'ios'; - - expect( - getSwitchColor({ - theme: getTheme(true), - }) - ).toMatchObject({ - thumbTintColor: undefined, - }); - }); - - it('should return correct disabled color, dark mode', () => { - Platform.OS = 'android'; - expect( - getSwitchColor({ - theme: getTheme(true), - disabled: true, - }) - ).toMatchObject({ - thumbTintColor: grey800, - }); + it('renders off', () => { + expect(render().toJSON()).toMatchSnapshot(); }); - it('should return correct disabled color, light mode', () => { - expect( - getSwitchColor({ - theme: getTheme(), - disabled: true, - }) - ).toMatchObject({ - thumbTintColor: grey400, - }); + it('renders disabled on', () => { + expect(render().toJSON()).toMatchSnapshot(); }); - it('should return correct checked color if there is value', () => { - Platform.OS = 'android'; - + it('renders disabled off', () => { expect( - getSwitchColor({ - theme: getTheme(), - value: true, - color: 'purple', - }) - ).toMatchObject({ - thumbTintColor: 'purple', - }); + render().toJSON() + ).toMatchSnapshot(); }); - it('should return theme checked color, dark mode', () => { - Platform.OS = 'android'; - + it('renders with checked icon', () => { expect( - getSwitchColor({ - theme: getTheme(true), - }) - ).toMatchObject({ - thumbTintColor: grey400, - }); + render().toJSON() + ).toMatchSnapshot(); }); - it('should return theme checked color, light mode', () => { - Platform.OS = 'android'; - + it('renders with per-state icons', () => { expect( - getSwitchColor({ - theme: getTheme(), - }) - ).toMatchObject({ - thumbTintColor: grey50, - }); + render( + + ).toJSON() + ).toMatchSnapshot(); }); }); -describe('getSwitchColor - on tint color', () => { - it('should return checked color for iOS platform, for theme version 3', () => { - Platform.OS = 'ios'; - - expect( - getSwitchColor({ - theme: getTheme(), - }) - ).toMatchObject({ - onTintColor: getTheme().colors.primary, - }); +describe('Switch interaction', () => { + it('toggles to true when off and pressed', () => { + const onValueChange = jest.fn(); + const { getByRole } = render( + + ); + fireEvent.press(getByRole('switch')); + expect(onValueChange).toHaveBeenCalledWith(true); }); - it('should return custom color for iOS platform', () => { - Platform.OS = 'ios'; - - expect( - getSwitchColor({ - theme: getTheme(), - color: 'purple', - }) - ).toMatchObject({ - onTintColor: 'purple', - }); + it('toggles to false when on and pressed', () => { + const onValueChange = jest.fn(); + const { getByRole } = render( + + ); + fireEvent.press(getByRole('switch')); + expect(onValueChange).toHaveBeenCalledWith(false); }); - it('should return correct disabled color, for theme version 3, dark mode', () => { - Platform.OS = 'android'; - - expect( - getSwitchColor({ - theme: getTheme(true), - disabled: true, - }) - ).toMatchObject({ - onTintColor: color(white).alpha(0.06).rgb().string(), - }); - }); - - it('should return correct disabled color, light mode', () => { - expect( - getSwitchColor({ - theme: getTheme(), - disabled: true, - }) - ).toMatchObject({ - onTintColor: color(black).alpha(0.12).rgb().string(), - }); - }); - - it('should return correct checked color if there is value', () => { - expect( - getSwitchColor({ - theme: getTheme(), - value: true, - color: 'purple', - }) - ).toMatchObject({ - checkedColor: 'purple', - }); - }); - - it('should return theme checked color, dark mode', () => { - expect( - getSwitchColor({ - theme: getTheme(true), - }) - ).toMatchObject({ - onTintColor: grey700, - }); - }); - - it('should return theme checked color, light mode', () => { - expect( - getSwitchColor({ - theme: getTheme(), - }) - ).toMatchObject({ - onTintColor: 'rgb(178, 175, 177)', - }); + it('does not fire onValueChange when disabled', () => { + const onValueChange = jest.fn(); + const { getByRole } = render( + + ); + fireEvent.press(getByRole('switch')); + expect(onValueChange).not.toHaveBeenCalled(); }); }); diff --git a/src/components/__tests__/__snapshots__/Switch.test.tsx.snap b/src/components/__tests__/__snapshots__/Switch.test.tsx.snap index f7839d9be4..41fce74fcc 100644 --- a/src/components/__tests__/__snapshots__/Switch.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Switch.test.tsx.snap @@ -1,70 +1,1259 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders disabled switch 1`] = ` - +> + + + + + + + + + + + + `; -exports[`renders off switch 1`] = ` - +> + + + + + + + + + + `; -exports[`renders on switch 1`] = ` - + + + + + + + + + + + +`; + +exports[`Switch render renders on 1`] = ` + +> + + + + + + + + + `; -exports[`renders switch with color 1`] = ` - + + + + + + + + + + + check + + + + + +`; + +exports[`Switch render renders with per-state icons 1`] = ` + +> + + + + + + + + + + + check + + + + + `; diff --git a/src/utils/isKeyboardFocusEvent.ts b/src/utils/isKeyboardFocusEvent.ts new file mode 100644 index 0000000000..630bd36182 --- /dev/null +++ b/src/utils/isKeyboardFocusEvent.ts @@ -0,0 +1,23 @@ +import { + type NativeSyntheticEvent, + Platform, + type TargetedEvent, +} from 'react-native'; + +/** True when a focus event came from keyboard navigation. Native: always true. */ +export const isKeyboardFocusEvent = ( + e: NativeSyntheticEvent +): boolean => { + if (Platform.OS !== 'web') return true; + try { + const target = e.currentTarget as unknown as { + matches?: (selector: string) => boolean; + }; + if (typeof target.matches === 'function') { + return target.matches(':focus-visible'); + } + } catch { + // older browsers throw on unknown selectors + } + return true; +}; From 8720eac542159b0294a6b39b6a33f4ac21aba41d Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Tue, 26 May 2026 16:16:11 +0300 Subject: [PATCH 34/94] fix: dynamic theme broken deep merge when using PlatformColor * useInternalTheme now uses a custom deep merge that keeps PlatformColor / DynamicColorIOS / Android resource sentinels intact (upstream deepmerge corrupted them). No other behavioral change. --- src/theme/__tests__/provider.test.ts | 114 +++++++++++++++++++++++++++ src/theme/provider.tsx | 44 ++++++++++- 2 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/theme/__tests__/provider.test.ts diff --git a/src/theme/__tests__/provider.test.ts b/src/theme/__tests__/provider.test.ts new file mode 100644 index 0000000000..bf4f162418 --- /dev/null +++ b/src/theme/__tests__/provider.test.ts @@ -0,0 +1,114 @@ +import { isPlatformColorSentinel, safeMerge } from '../provider'; + +describe('isPlatformColorSentinel', () => { + it('detects iOS PlatformColor (semantic)', () => { + expect(isPlatformColorSentinel({ semantic: ['label'] })).toBe(true); + }); + + it('detects iOS DynamicColorIOS (dynamic)', () => { + expect( + isPlatformColorSentinel({ dynamic: { light: '#fff', dark: '#000' } }) + ).toBe(true); + }); + + it('detects Android PlatformColor (resource_paths)', () => { + expect( + isPlatformColorSentinel({ resource_paths: ['@android:color/black'] }) + ).toBe(true); + }); + + it('rejects plain objects, primitives, null, and arrays', () => { + expect(isPlatformColorSentinel({ primary: '#fff' })).toBe(false); + expect(isPlatformColorSentinel('#fff')).toBe(false); + expect(isPlatformColorSentinel(null)).toBe(false); + expect(isPlatformColorSentinel(undefined)).toBe(false); + expect(isPlatformColorSentinel([1, 2, 3])).toBe(false); + }); +}); + +describe('safeMerge', () => { + it('deep-merges plain objects, overrides win at leaves', () => { + const base = { a: 1, nested: { x: 1, y: 2 } }; + const overrides = { nested: { y: 20, z: 30 } }; + + expect(safeMerge(base, overrides)).toEqual({ + a: 1, + nested: { x: 1, y: 20, z: 30 }, + }); + }); + + it('returns a new object reference (does not mutate base)', () => { + const base = { nested: { x: 1 } }; + const overrides = { nested: { y: 2 } }; + const result = safeMerge(base, overrides); + + expect(result).not.toBe(base); + expect(result.nested).not.toBe(base.nested); + expect(base).toEqual({ nested: { x: 1 } }); + }); + + it('falls back to base when overrides is null/undefined', () => { + const base = { a: 1 }; + expect(safeMerge(base, null)).toEqual(base); + expect(safeMerge(base, undefined)).toEqual(base); + }); + + it('replaces arrays instead of merging', () => { + const base = { list: [1, 2, 3] }; + const overrides = { list: [9] }; + expect(safeMerge(base, overrides)).toEqual({ list: [9] }); + }); + + it('treats iOS semantic sentinel as a leaf (no recursion into array)', () => { + const sentinel = { semantic: ['label'] }; + const base = { colors: { primary: '#000' } }; + const overrides = { colors: { primary: sentinel } }; + + const result = safeMerge( + base, + overrides + ); + expect(result.colors.primary).toBe(sentinel); + }); + + it('treats DynamicColorIOS sentinel as a leaf', () => { + const sentinel = { dynamic: { light: '#fff', dark: '#000' } }; + const base = { colors: { primary: sentinel } }; + const overrides = { colors: { primary: '#abc' } }; + + const result = safeMerge( + base, + overrides + ); + expect(result.colors.primary).toBe('#abc'); + }); + + it('treats Android resource_paths sentinel as a leaf', () => { + const sentinelBase = { resource_paths: ['@android:color/black'] }; + const sentinelOverride = { resource_paths: ['@android:color/white'] }; + const base = { colors: { primary: sentinelBase } }; + const overrides = { colors: { primary: sentinelOverride } }; + + const result = safeMerge( + base, + overrides + ); + expect(result.colors.primary).toBe(sentinelOverride); + }); + + it('preserves sentinel siblings when merging a colors map', () => { + const sentinel = { semantic: ['label'] }; + const base = { + colors: { primary: sentinel, secondary: '#111', tertiary: '#222' }, + }; + const overrides = { colors: { secondary: '#999' } }; + + const result = safeMerge }>( + base, + overrides + ); + expect(result.colors.primary).toBe(sentinel); + expect(result.colors.secondary).toBe('#999'); + expect(result.colors.tertiary).toBe('#222'); + }); +}); diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index bd970221b5..6017c6a6da 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -1,3 +1,4 @@ +import * as React from 'react'; import type { ComponentType } from 'react'; import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; @@ -17,9 +18,50 @@ export function useTheme(overrides?: $DeepPartial) { return useThemeBase(overrides); } +// Upstream `deepmerge` corrupts PlatformColor objects, so we recurse manually +// and treat sentinels as leaves. Three shapes: +// `semantic` — iOS PlatformColor +// `dynamic` — DynamicColorIOS +// `resource_paths` — Android PlatformColor +export const isPlatformColorSentinel = (v: unknown): boolean => + !!v && + typeof v === 'object' && + ('resource_paths' in v || 'semantic' in v || 'dynamic' in v); + +export const safeMerge = (base: T, overrides: unknown): T => { + if ( + !base || + !overrides || + typeof base !== 'object' || + typeof overrides !== 'object' || + Array.isArray(base) || + Array.isArray(overrides) || + isPlatformColorSentinel(base) || + isPlatformColorSentinel(overrides) + ) { + // leaf: override wins, fall back to base + return (overrides ?? base) as T; + } + const out: Record = { ...(base as Record) }; + for (const key of Object.keys(overrides as Record)) { + out[key] = safeMerge( + (base as Record)[key], + (overrides as Record)[key] + ); + } + return out as T; +}; + +/** Memoize `themeOverrides` at the call site; inline object literals defeat the memo. */ export const useInternalTheme = ( themeOverrides: $DeepPartial | undefined -) => useThemeBase(themeOverrides); +): Theme => { + const theme = useThemeBase(); + return React.useMemo( + () => (themeOverrides ? safeMerge(theme, themeOverrides) : theme), + [theme, themeOverrides] + ); +}; export const withInternalTheme = ( WrappedComponent: ComponentType & C From 490a5022c84a71f8cfcc265ebd0e091a093d0166 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Thu, 28 May 2026 16:42:32 +0300 Subject: [PATCH 35/94] fix(docs): resolve docs build and icon issues - define `__DEV__` with webpack `DefinePlugin` - standardize docs icons on `@react-native-vector-icons/material-design-icons` - update docs examples and icon guidance - alias `@react-native-vector-icons/get-image` to `false` - flatten CSS nesting in `custom.css` - add `isDynamicColorSupported = false` to the non-Android `DynamicTheme` fallback - fix the `TypescaleKey` type re-export --- docs/docs/guides/01-getting-started.mdx | 6 ++-- docs/docs/guides/03-icons.mdx | 2 +- .../plugins/docusaurus-react-native-plugin.js | 10 ++++-- docs/src/css/custom.css | 24 +++++++------ .../extendedExamples/BottomNavigationBar.js | 4 +-- .../BottomNavigation/BottomNavigationBar.tsx | 2 +- src/components/MaterialCommunityIcon.tsx | 36 ++++++------------- src/index.tsx | 2 +- src/theme/schemes/DynamicTheme.tsx | 2 ++ 9 files changed, 40 insertions(+), 48 deletions(-) diff --git a/docs/docs/guides/01-getting-started.mdx b/docs/docs/guides/01-getting-started.mdx index 898bf8da18..53c3c3519e 100644 --- a/docs/docs/guides/01-getting-started.mdx +++ b/docs/docs/guides/01-getting-started.mdx @@ -49,16 +49,16 @@ Minimum requirements: Follow the [React Native Reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/). -If you're on a vanilla React Native project, you also need to install and link [@react-native-vector-icons/material-design-icons](https://github.com/oblador/react-native-vector-icons). +If you're on a vanilla React Native project, you also need to install [@react-native-vector-icons/material-design-icons](https://github.com/oblador/react-native-vector-icons/tree/master/packages/material-design-icons). -Specifically `MaterialDesignIcons` icon pack needs to be included in the project, because some components use those internally (e.g. `AppBar.BackAction` on Android). +Specifically, the `MaterialDesignIcons` icon pack needs to be included in the project because some components use those icons internally, such as `AppBar.BackAction` on Android. ```bash npm2yarn npm install @react-native-vector-icons/material-design-icons ``` :::note -The `react-native-vector-icons` library requires some additional setup steps for each platform. To ensure proper use of icon fonts, please follow their [installation guide](https://github.com/oblador/react-native-vector-icons?tab=readme-ov-file#setup). +`@react-native-vector-icons/material-design-icons` may require some additional setup steps depending on platform. To ensure the icon font is configured correctly, please follow the upstream [installation guide](https://github.com/oblador/react-native-vector-icons?tab=readme-ov-file#setup). ::: :::info diff --git a/docs/docs/guides/03-icons.mdx b/docs/docs/guides/03-icons.mdx index c773ce7a56..5b6b4ba724 100644 --- a/docs/docs/guides/03-icons.mdx +++ b/docs/docs/guides/03-icons.mdx @@ -10,7 +10,7 @@ import IconsList from '@site/src/components/IconsList.tsx'; ## Configuring icons -Many of the components require the [react-native-vector-icons](https://github.com/oblador/react-native-vector-icons) library to render correctly. If you're using Expo, you don't need to do anything extra, but if it's a vanilla React Native project, you need to link the library as described in the [getting started guide](./01-getting-started.mdx). +Many of the components render icon names using [`@react-native-vector-icons/material-design-icons`](https://github.com/oblador/react-native-vector-icons/tree/master/packages/material-design-icons). In Expo projects this usually works without extra setup. In vanilla React Native projects, install the package and complete any required platform setup as described in the [getting started guide](./01-getting-started.mdx). :::note If you opted out of vector icons support using [babel-plugin-optional-require](https://github.com/satya164/babel-plugin-optional-require), you won't be able to use icon names for the icon prop. Some components may not look correct without vector icons and might need extra configuration. diff --git a/docs/plugins/docusaurus-react-native-plugin.js b/docs/plugins/docusaurus-react-native-plugin.js index 3a05c223bc..252d0c37e0 100644 --- a/docs/plugins/docusaurus-react-native-plugin.js +++ b/docs/plugins/docusaurus-react-native-plugin.js @@ -1,4 +1,5 @@ const path = require('path'); +const webpack = require('webpack'); module.exports = function () { return { @@ -11,12 +12,15 @@ module.exports = function () { react: path.resolve('node_modules/react'), 'react-native$': 'react-native-web', 'react-native-paper': path.resolve('../src'), - 'react-native-vector-icons/MaterialCommunityIcons': path.resolve( - 'node_modules/@react-native-vector-icons/material-design-icons' - ), + '@react-native-vector-icons/get-image': false, }, extensions: ['.web.js'], }, + plugins: [ + new webpack.DefinePlugin({ + __DEV__: JSON.stringify(process.env.NODE_ENV !== 'production'), + }), + ], }; }, }; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 6594e61fb7..a382d6268e 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -128,8 +128,10 @@ html[data-theme='light'] .gallery-dark { height: 124px; align-items: center; text-align: center; +} - @media (max-width: 680px) { +@media (max-width: 680px) { + .icons-list-icon-container { width: 96px; } } @@ -153,10 +155,10 @@ html[data-theme='light'] .gallery-dark { flex-wrap: wrap; justify-content: center; margin: 24px 0; +} - &:last-child { - justify-content: flex-start; - } +.icons-list-results:last-child { + justify-content: flex-start; } .icons-list-searchbar { @@ -171,15 +173,15 @@ html[data-theme='light'] .gallery-dark { border-radius: 3px; transition: background-color 0.3s; outline: 0; +} - &:focus, - &:hover { - background-color: #e7e7e7; - } +.icons-list-searchbar:focus, +.icons-list-searchbar:hover { + background-color: #e7e7e7; +} - *:focus-visible { - outline: auto; - } +.icons-list-searchbar:focus-visible { + outline: auto; } @font-face { diff --git a/docs/src/data/extendedExamples/BottomNavigationBar.js b/docs/src/data/extendedExamples/BottomNavigationBar.js index 81bb261a96..4a5cd4bf24 100644 --- a/docs/src/data/extendedExamples/BottomNavigationBar.js +++ b/docs/src/data/extendedExamples/BottomNavigationBar.js @@ -1,7 +1,7 @@ const staticCode = `import { Text, View } from 'react-native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Provider, BottomNavigation } from 'react-native-paper'; -import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'; +import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; import { CommonActions, createStaticNavigation, @@ -103,7 +103,7 @@ const dynamicCode = `import { Text, View } from 'react-native'; import { NavigationContainer, CommonActions } from '@react-navigation/native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Provider, BottomNavigation } from 'react-native-paper'; -import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'; +import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; function HomeScreen() { return ( diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index 4d2930d435..57e96f67cc 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -241,7 +241,7 @@ const Touchable = ({ * import { useState } from 'react'; * import { View } from 'react-native'; * import { BottomNavigation, Text, Provider } from 'react-native-paper'; - * import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'; + * import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; * * function HomeScreen() { * return ( diff --git a/src/components/MaterialCommunityIcon.tsx b/src/components/MaterialCommunityIcon.tsx index d5468a0d07..ce2e73a743 100644 --- a/src/components/MaterialCommunityIcon.tsx +++ b/src/components/MaterialCommunityIcon.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { ComponentProps } from 'react'; import { ColorValue, Platform, @@ -12,7 +11,7 @@ import { import { black } from '../theme/colors'; export type IconProps = { - name: ComponentProps['name']; + name: string; color?: ColorValue; size: number; direction: 'rtl' | 'ltr'; @@ -41,31 +40,22 @@ export const accessibilityProps: AccessibilityProps = importantForAccessibility: 'no-hide-descendants', }; -/** - * Loads the appropriate icon module based on available dependencies - */ const loadIconModule = () => { try { return require('@react-native-vector-icons/material-design-icons').default; } catch (e) { - try { - return require('@expo/vector-icons/MaterialCommunityIcons').default; - } catch (e) { - try { - return require('react-native-vector-icons/MaterialCommunityIcons') - .default; - } catch (e) { - return null; - } - } + return null; } }; type IconModuleType = React.ComponentType< - React.ComponentProps< - | typeof import('@react-native-vector-icons/material-design-icons').default - | typeof import('react-native-vector-icons/MaterialCommunityIcons').default + Omit< + React.ComponentProps< + typeof import('@react-native-vector-icons/material-design-icons').default + >, + 'name' > & { + name: string; color: ColorValue; pointerEvents?: ViewProps['pointerEvents']; } @@ -73,16 +63,10 @@ type IconModuleType = React.ComponentType< const IconModule = loadIconModule(); -/** - * Fallback component displayed when no icon library is available - */ const FallbackIcon = ({ name, color, size, ...rest }: IconProps) => { console.warn( - `Tried to use the icon '${name}' in a component from 'react-native-paper', but none of the required icon libraries are installed.`, - `To fix this, please install one of the following:\n` + - `- @expo/vector-icons\n` + - `- @react-native-vector-icons/material-design-icons\n` + - `- react-native-vector-icons\n\n` + + `Tried to use the icon '${name}' in a component from 'react-native-paper', but the required icon library is not installed.`, + `To fix this, please install '@react-native-vector-icons/material-design-icons'.\n\n` + `You can also use another method to specify icon: https://callstack.github.io/react-native-paper/docs/guides/icons` ); diff --git a/src/index.tsx b/src/index.tsx index b5ecadea41..71f105295e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -137,4 +137,4 @@ export type { Props as SegmentedButtonsProps } from './components/SegmentedButto export type { Props as ListImageProps } from './components/List/ListImage'; export type { Props as TooltipProps } from './components/Tooltip/Tooltip'; -export { TypescaleKey, type Theme, type Elevation } from './types'; +export { type TypescaleKey, type Theme, type Elevation } from './types'; diff --git a/src/theme/schemes/DynamicTheme.tsx b/src/theme/schemes/DynamicTheme.tsx index d932f5a624..a9049b86bf 100644 --- a/src/theme/schemes/DynamicTheme.tsx +++ b/src/theme/schemes/DynamicTheme.tsx @@ -1,2 +1,4 @@ export { DarkTheme as DynamicDarkTheme } from './DarkTheme'; export { LightTheme as DynamicLightTheme } from './LightTheme'; + +export const isDynamicColorSupported = false; From 8b2b1eb9d3014a42f49da3cfd2a66c723293976e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lul?= <56554355+wonderlul@users.noreply.github.com> Date: Wed, 3 Jun 2026 11:50:48 +0200 Subject: [PATCH 36/94] feat: rework TextInput component (#4909) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: `TextInput` has been rewritten for Material Design 3. The public API, adornments, variant naming, and styling overrides have changed. See the migration section below for required updates. ## Summary Replaces the Paper 5.x `TextInput` with a new MD3-aligned implementation. The component name stays **`TextInput`**, but most props and composition patterns have changed. The new `TextInput` supports **`filled`** and **`outlined`** variants, floating labels, **`supportingText`**, a built-in character **`counter`**, **`prefix`** / **`suffix`**, leading/trailing **`startAccessory`** / **`endAccessory`** render props, a custom **`render`** prop for masked inputs, and improved accessibility. Colors and layout follow the theme; per-prop color overrides for outlines and underlines were removed. ## What changed | Before (v5) | After (v6) | | --- | --- | | `mode="flat"` | `variant="filled"` (default) | | `mode="outlined"` | `variant="outlined"` | | `left` / `right` element props | `startAccessory` / `endAccessory` render props | | `TextInput.Affix` | `prefix` / `suffix` strings, or custom accessories | | `HelperText` below the field | `supportingText` on the field | | `label` as `string` or `ReactNode` | `label` as `string` only | | `dense`, `contentStyle`, `outlineStyle`, `underlineStyle` | removed — use `style` and `theme` | | `textColor`, `underlineColor`, `activeUnderlineColor`, `outlineColor`, `activeOutlineColor` | removed — use `style` and `theme` | | `disabled` blocked interaction | `disabled` blocks interaction; use `editable={false}` for read-only fields that can still be focused | ### Still supported - **`TextInput.Icon`** — use inside `startAccessory` / `endAccessory` - **`render`** — swap the native input (e.g. masked inputs) - **`selectionColor`** and **`cursorColor`** — inherited from React Native `TextInput` props - **`ref`** — exposes `TextInputHandles` (`focus`, `blur`, `clear`, `isFocused`, `setNativeProps`, `setSelection`); `clear()` also syncs internal state and label animation ## What was removed - **`TextInput.Affix`** - **`HelperText`** as the recommended companion for text fields (use `supportingText` instead) - **`mode`**, **`left`**, **`right`** - **`dense`**, **`contentStyle`**, **`outlineStyle`**, **`underlineStyle`** - **`textColor`**, **`underlineColor`**, **`activeUnderlineColor`**, **`outlineColor`**, **`activeOutlineColor`** ## Migration Import stays the same: ```tsx import { TextInput, type TextInputProps } from 'react-native-paper'; ``` ### Variant ```tsx // Before (v5) // After (v6) ``` ### Icons and adornments `left` / `right` element props become **`startAccessory`** / **`endAccessory`** render functions. Spread the props TextInput passes in — they include layout `style`, plus `error`, `disabled`, and `multiline`. ```tsx // Before (v5) } right={} /> // After (v6) } endAccessory={(props) => ( setText('')} /> )} /> ``` Replace **`TextInput.Affix`** with **`prefix`** / **`suffix`** for inline text, or a custom **`endAccessory`**: ```tsx // Before (v5) — affix as trailing adornment } maxLength={100} /> // After (v6) — prefix/suffix and/or counter ``` > **Note:** `prefix` and `suffix` are shown only when the label is floated (field focused or has a value). ### Supporting text and errors ```tsx // Before (v5) <> Enter a valid email // After (v6) ``` When `error` is `true` and no `endAccessory` is provided, a default error icon is shown on the trailing side. ### Disabled vs read-only ```tsx // Disabled — not editable, not focusable, disabled styling // Read-only — can focus and select text, but not edit ``` ### Styling and colors Outline, underline, label, and disabled colors now come from the **theme**. Override input text with the standard **`style`** prop; override accent colors via **`theme`** or, where applicable, inherited props: ```tsx // Before (v5) // After (v6) ``` ### Custom input (`render` prop) ```tsx } /> ``` ### Imperative handle ```tsx const ref = React.useRef(null); ; ref.current?.clear(); // clears text, syncs counter/label animation, calls onChangeText('') ``` ## New props reference | Prop | Description | | --- | --- | | `variant` | `'filled'` (default) or `'outlined'` | | `supportingText` | Helper/error text below the field | | `counter` | Shows `currentLength/maxLength`; requires `maxLength` | | `prefix` / `suffix` | Short inline strings inside the field | | `startAccessory` / `endAccessory` | Render props for leading/trailing content | | `disabled` | Disables the field (separate from `editable={false}`) | | `render` | Custom native input renderer | ## Related - Depends on: https://github.com/callstack/react-native-paper/pull/4912 - Closes: https://github.com/callstack/react-native-paper/issues/4878, https://github.com/callstack/react-native-paper/issues/4329, https://github.com/callstack/react-native-paper/issues/4235 - Migration guide: `docs/docs/guides/12-migration.md` ## Test plan Run the Example app and verify: - [ ] **`filled`** variant — label animation, underline indicator, disabled overlay, error state - [ ] **`outlined`** variant — outline, label notch behavior, error state - [ ] **`startAccessory`** / **`endAccessory`** with `TextInput.Icon` and custom content - [ ] **`prefix`** / **`suffix`** visibility when focused vs blurred - [ ] **`counter`** with `maxLength` - [ ] **`supportingText`** with and without `error` - [ ] **`disabled`** vs **`editable={false}`** - [ ] **`render`** prop with a custom input - [ ] **`ref.clear()`** on controlled and uncontrolled fields - [ ] RTL layout (accessories swap sides) - [ ] Multiline fields **Screenshots** `filled` ![text-field-filled](https://github.com/user-attachments/assets/5333c5f7-bc2e-46f2-b740-765ea84b1757) `outlined` ![text-field-outlined](https://github.com/user-attachments/assets/209dfe58-dab8-45ff-8d3f-998243f4f4de) --- docs/babel.config.js | 1 + docs/docs/guides/12-migration.md | 128 + docs/docusaurus.config.js | 20 +- docs/package.json | 1 + .../plugins/docusaurus-react-native-plugin.js | 3 + docs/src/components/BannerExample.tsx | 2 +- docs/src/components/PropTable.tsx | 18 +- docs/src/data/screenshots.js | 9 +- docs/src/data/themeColors.js | 11 - docs/static/screenshots/text-input-filled.png | Bin 0 -> 40005 bytes .../screenshots/text-input-outlined.png | Bin 0 -> 25143 bytes .../screenshots/textinput-flat.disabled.png | Bin 482253 -> 0 bytes .../screenshots/textinput-flat.focused.png | Bin 613699 -> 0 bytes .../screenshots/textinput-flat.icon.png | Bin 605386 -> 0 bytes .../screenshots/textinput-outline.affix.png | Bin 332637 -> 0 bytes .../textinput-outlined.disabled.png | Bin 484745 -> 0 bytes .../textinput-outlined.focused.png | Bin 642064 -> 0 bytes example/src/Examples/TextInputExample.tsx | 1037 +---- jest/testSetup.js | 7 +- src/components/HelperText/HelperText.tsx | 175 - src/components/HelperText/utils.ts | 19 - src/components/TextInput/Addons/Outline.tsx | 60 - src/components/TextInput/Addons/Underline.tsx | 81 - .../Adornment/TextInputAdornment.tsx | 201 - .../TextInput/Adornment/TextInputAffix.tsx | 219 - .../TextInput/Adornment/TextInputIcon.tsx | 188 - src/components/TextInput/Adornment/enums.tsx | 12 - src/components/TextInput/Adornment/types.tsx | 11 - src/components/TextInput/Adornment/utils.ts | 42 - src/components/TextInput/Label/InputLabel.tsx | 224 - .../TextInput/Label/LabelBackground.tsx | 100 - src/components/TextInput/TextInput.tsx | 897 ++-- .../TextInput/TextInputErrorIcon.tsx | 31 + src/components/TextInput/TextInputFlat.tsx | 468 -- src/components/TextInput/TextInputIcon.tsx | 98 + .../TextInput/TextInputOutlined.tsx | 447 -- src/components/TextInput/constants.ts | 137 + src/components/TextInput/constants.tsx | 39 - src/components/TextInput/helpers.tsx | 503 --- src/components/TextInput/hooks.ts | 470 ++ src/components/TextInput/index.ts | 13 + src/components/TextInput/styles.ts | 128 + src/components/TextInput/types.tsx | 155 - src/components/TextInput/utils.ts | 642 +++ src/components/__tests__/HelperText.test.tsx | 31 - src/components/__tests__/TextInput.test.tsx | 1901 ++++---- .../__snapshots__/TextInput.test.tsx.snap | 4001 ++++++++--------- src/index.tsx | 17 +- yarn.lock | 8 + 49 files changed, 5243 insertions(+), 7312 deletions(-) create mode 100644 docs/docs/guides/12-migration.md create mode 100644 docs/static/screenshots/text-input-filled.png create mode 100644 docs/static/screenshots/text-input-outlined.png delete mode 100644 docs/static/screenshots/textinput-flat.disabled.png delete mode 100644 docs/static/screenshots/textinput-flat.focused.png delete mode 100644 docs/static/screenshots/textinput-flat.icon.png delete mode 100644 docs/static/screenshots/textinput-outline.affix.png delete mode 100644 docs/static/screenshots/textinput-outlined.disabled.png delete mode 100644 docs/static/screenshots/textinput-outlined.focused.png delete mode 100644 src/components/HelperText/HelperText.tsx delete mode 100644 src/components/HelperText/utils.ts delete mode 100644 src/components/TextInput/Addons/Outline.tsx delete mode 100644 src/components/TextInput/Addons/Underline.tsx delete mode 100644 src/components/TextInput/Adornment/TextInputAdornment.tsx delete mode 100644 src/components/TextInput/Adornment/TextInputAffix.tsx delete mode 100644 src/components/TextInput/Adornment/TextInputIcon.tsx delete mode 100644 src/components/TextInput/Adornment/enums.tsx delete mode 100644 src/components/TextInput/Adornment/types.tsx delete mode 100644 src/components/TextInput/Adornment/utils.ts delete mode 100644 src/components/TextInput/Label/InputLabel.tsx delete mode 100644 src/components/TextInput/Label/LabelBackground.tsx create mode 100644 src/components/TextInput/TextInputErrorIcon.tsx delete mode 100644 src/components/TextInput/TextInputFlat.tsx create mode 100644 src/components/TextInput/TextInputIcon.tsx delete mode 100644 src/components/TextInput/TextInputOutlined.tsx create mode 100644 src/components/TextInput/constants.ts delete mode 100644 src/components/TextInput/constants.tsx delete mode 100644 src/components/TextInput/helpers.tsx create mode 100644 src/components/TextInput/hooks.ts create mode 100644 src/components/TextInput/index.ts create mode 100644 src/components/TextInput/styles.ts delete mode 100644 src/components/TextInput/types.tsx create mode 100644 src/components/TextInput/utils.ts delete mode 100644 src/components/__tests__/HelperText.test.tsx diff --git a/docs/babel.config.js b/docs/babel.config.js index b34f4c9f0f..1a907e925f 100644 --- a/docs/babel.config.js +++ b/docs/babel.config.js @@ -1,3 +1,4 @@ module.exports = { presets: ['@docusaurus/core/lib/babel/preset'], + plugins: ['react-native-reanimated/plugin'], }; diff --git a/docs/docs/guides/12-migration.md b/docs/docs/guides/12-migration.md new file mode 100644 index 0000000000..1fcd32bd25 --- /dev/null +++ b/docs/docs/guides/12-migration.md @@ -0,0 +1,128 @@ +--- +title: Migration from Paper 5.x to 6.x +--- + +TBC + +## Components + +### TextInput + +The Paper 6.x `TextInput` is a complete rewrite with a new API. Import the component the same way, but note that the props and behavior have changed significantly. + +#### Types + +```tsx +import { TextInput, type TextInputProps } from 'react-native-paper'; +``` + +#### Variant + +- **`mode="flat"`** → **`variant="filled"`** +- **`mode="outlined"`** → **`variant="outlined"`** + +```tsx +// Before (v5) + + + +// After (v6) + + +``` + +#### Adornments + +- **`left` / `right`** → **`startAccessory` / `endAccessory`** +- **`TextInput.Affix`** → **`prefix` / `suffix`**, or **`TextInput.Icon`**, or **`startAccessory` / `endAccessory`** + +```tsx +// Before (v5) +} + right={} +/> + +// After (v6) + } + endAccessory={(p) => } + maxLength={100} + prefix="$" + suffix="/100" + counter +/> +``` + +#### Label and supporting text + +- **`label: React.Element | string`** → **`string`** +- **`HelperText`** was removed; use **`supportingText`**. + +```tsx +// Before (v5) +<> + + + Enter a valid email + + + +// After (v6) + +``` + +#### Removed props + +No direct `TextInput` equivalents for: + +- **`dense`**, **`contentStyle`**, **`underlineStyle`** +- **`underlineColor`**, **`activeUnderlineColor`**, **`outlineColor`**, **`activeOutlineColor`**, **`textColor`** + +Use **`style`** on the inner input and the **`theme`** for colors. + +```tsx +import { MD3LightTheme, TextInput } from 'react-native-paper'; + +const theme = { + ...MD3LightTheme, + colors: { + ...MD3LightTheme.colors, + outline: '#79747E', + primary: '#6750A4', + }, +}; + +// Before (v5) + + +// After (v6) + +``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 55de22c833..99056b2426 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -126,7 +126,6 @@ const config = { AnimatedFAB: 'FAB/AnimatedFAB', FABGroup: 'FAB/FABGroup', }, - HelperText: { HelperText: 'HelperText/HelperText' }, IconButton: { IconButton: 'IconButton/IconButton', }, @@ -167,8 +166,7 @@ const config = { }, TextInput: { TextInput: 'TextInput/TextInput', - TextInputAffix: 'TextInput/Adornment/TextInputAffix', - TextInputIcon: 'TextInput/Adornment/TextInputIcon', + TextInputIcon: 'TextInput/TextInputIcon', }, ToggleButton: { ToggleButton: 'ToggleButton/ToggleButton', @@ -206,10 +204,9 @@ const config = { } const customUrls = { - TextInputAffix: - 'src/components/TextInput/Adornment/TextInputAffix.tsx', - TextInputIcon: - 'src/components/TextInput/Adornment/TextInputIcon.tsx', + TextInput: 'src/components/TextInput/TextInput.tsx', + TextInputIcon: 'src/components/TextInput/TextInputIcon.tsx', + Text: 'src/components/Typography/Text.tsx', showcase: 'docs/src/components/Showcase.tsx', }; @@ -328,14 +325,7 @@ const config = { 'https://snack.expo.dev/@react-native-paper/more-examples---snackbar-rendered-regardless-of-the-parent-positioning', }, }, - knownIssues: { - TextInput: { - 'Outline overlaps label': - 'https://github.com/callstack/react-native-paper/issues/3759#issuecomment-1601235262', - 'Long text wraps to a second line': - 'https://github.com/callstack/react-native-paper/issues/2581#issuecomment-790251987', - }, - }, + knownIssues: {}, themeColors, screenshots, extendedExamples, diff --git a/docs/package.json b/docs/package.json index e8dc71c7e5..994a05fe16 100644 --- a/docs/package.json +++ b/docs/package.json @@ -29,6 +29,7 @@ "color": "^4.2.3", "marked": "^4.1.1", "prism-react-renderer": "^1.3.5", + "process": "^0.11.10", "react": "17.0.2", "react-color": "^2.19.3", "react-dom": "17.0.2", diff --git a/docs/plugins/docusaurus-react-native-plugin.js b/docs/plugins/docusaurus-react-native-plugin.js index 252d0c37e0..229325015a 100644 --- a/docs/plugins/docusaurus-react-native-plugin.js +++ b/docs/plugins/docusaurus-react-native-plugin.js @@ -17,6 +17,9 @@ module.exports = function () { extensions: ['.web.js'], }, plugins: [ + new webpack.ProvidePlugin({ + process: 'process/browser.js', + }), new webpack.DefinePlugin({ __DEV__: JSON.stringify(process.env.NODE_ENV !== 'production'), }), diff --git a/docs/src/components/BannerExample.tsx b/docs/src/components/BannerExample.tsx index 59ec60634a..3e21a11ee4 100644 --- a/docs/src/components/BannerExample.tsx +++ b/docs/src/components/BannerExample.tsx @@ -113,7 +113,7 @@ const BannerExample = () => { /> setText(text)} /> diff --git a/docs/src/components/PropTable.tsx b/docs/src/components/PropTable.tsx index 35f5069433..317d1e652e 100644 --- a/docs/src/components/PropTable.tsx +++ b/docs/src/components/PropTable.tsx @@ -11,17 +11,29 @@ const typeDefinitions = { 'https://github.com/callstack/react-native-paper/blob/main/src/components/Icon.tsx#L16', ThemeProp: 'https://callstack.github.io/react-native-paper/docs/guides/theming#theme-properties', + '(props: TextInputAccessoryProps) => React.ReactNode': + 'https://github.com/callstack/react-native-paper/blob/main/src/components/TextInput/TextInputIcon.tsx#L11', + '(props: TextInputRenderProps) => React.ReactNode': + 'https://github.com/callstack/react-native-paper/blob/main/src/components/TextInput/TextInput.tsx#L168', + 'React.Ref': + 'https://github.com/callstack/react-native-paper/blob/main/src/components/TextInput/TextInput.tsx#L172', AccessibilityState: 'https://reactnative.dev/docs/accessibility#accessibilitystate', 'StyleProp': 'https://reactnative.dev/docs/view-style-props', 'StyleProp': 'https://reactnative.dev/docs/text-style-props', + TextProps: 'https://reactnative.dev/docs/text#props', + AccessibilityProps: + 'https://reactnative.dev/docs/accessibility#accessibilityprops', }; const renderBadge = (annotation: string) => { const [annotType, ...annotLabel] = annotation.split(' '); // eslint-disable-next-line prettier/prettier - return `${annotLabel.join(' ')}`; + return `${annotLabel.join(' ')}`; }; export default function PropTable({ @@ -56,7 +68,9 @@ export default function PropTable({ if (line.includes('@')) { const annotIndex = line.indexOf('@'); // eslint-disable-next-line prettier/prettier - return `${line.substr(0, annotIndex)} ${renderBadge(line.substr(annotIndex))}`; + return `${line.substr(0, annotIndex)} ${renderBadge( + line.substr(annotIndex) + )}`; } else { return line; } diff --git a/docs/src/data/screenshots.js b/docs/src/data/screenshots.js index c1afa99a6a..f25cef6294 100644 --- a/docs/src/data/screenshots.js +++ b/docs/src/data/screenshots.js @@ -79,7 +79,6 @@ const screenshots = { }, AnimatedFAB: 'screenshots/animated-fab.gif', 'FAB.Group': 'screenshots/fab-group.gif', - HelperText: 'screenshots/helper-text.gif', Icon: 'screenshots/icon.png', IconButton: { default: 'screenshots/icon-button-1.png', @@ -147,13 +146,9 @@ const screenshots = { }, Text: 'screenshots/typography.png', TextInput: { - 'flat (focused)': 'screenshots/textinput-flat.focused.png', - 'flat (disabled)': 'screenshots/textinput-flat.disabled.png', - 'outlined (focused)': 'screenshots/textinput-outlined.focused.png', - 'outlined (disabled)': 'screenshots/textinput-outlined.disabled.png', + filled: 'screenshots/text-input-filled.png', + outlined: 'screenshots/text-input-outlined.png', }, - 'TextInput.Affix': 'screenshots/textinput-outline.affix.png', - 'TextInput.Icon': 'screenshots/textinput-flat.icon.png', ToggleButton: 'screenshots/toggle-button.png', 'ToggleButton.Group': 'screenshots/toggle-button-group.gif', 'ToggleButton.Row': 'screenshots/toggle-button-row.gif', diff --git a/docs/src/data/themeColors.js b/docs/src/data/themeColors.js index f12b955341..2e97dd1bce 100644 --- a/docs/src/data/themeColors.js +++ b/docs/src/data/themeColors.js @@ -190,17 +190,6 @@ const themeColors = { 'textColor/iconColor': 'theme.colors.primary', }, }, - HelperText: { - disabled: { - textColor: 'theme.colors.onSurfaceDisabled', - }, - default: { - textColor: 'theme.colors.onSurfaceVariant', - }, - error: { - textColor: 'theme.colors.error', - }, - }, IconButton: { selected: { default: { diff --git a/docs/static/screenshots/text-input-filled.png b/docs/static/screenshots/text-input-filled.png new file mode 100644 index 0000000000000000000000000000000000000000..03ab10d37eb94c5e60b825fbdac149fcae4e7924 GIT binary patch literal 40005 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

w2 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 Swl<>x0000g#C%~n|ML|I!Pyl3MC@AOv3JR7X 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

P9hfvwG&=nSMyK9 zKVcp0F|+upifHE~QU{7$6v9c&4tL>96x{rL%`hAqbXU;$yg&JpfKGIK4H|SLqy8LH z_H#v{6;)8jJ8a*|-{(9la~2RrM;k)>gnU@flIG@ksb|MI zG+FCg!}Ee%!8mO1Q|91_yL4>qt@5!{P;+X7l)>Bp$#^moMlq^mo7wVQ`zF-farhma zz1pCcNQ5`;=UWwYvKZf&(a|-e`w`DE_8tzef-HrPO%0=X$p0J)0B76yq^`oBz7<*L z3a;|eTSx0nHLgu6J{yoxpk*L>ZJ)B@?c||YH1zzpoi&`~OTT~oT@!obD2GR@w+u(+PZoIkNx z6vdJfru2Oi{X&KovaTcvwpo*}SvB8?JHkFN-5UYv3l)UJvDH3`(!P;nR)(*3k4Nr3 z!alL2C2%63u0)hHKbEb2>}OXyS{Y%q$6_BBEJhELO|j8O>!ha9m%Pd?nlz24<5w|7 zFk|8=ER#~;L8Y6%rH7|~Cd_g6Zu!jj^n)H5RLn&9V}L5SSvLoD-FlDPZ$pw|9ze~k ze?x|R-?NzW)I-_`$lez15WmQX^sEG(plUS_<*uyDstQSAiI?u%{>?bABcAeCo7yRd z_RyhX!$}B}M?hoNa-*0WjlU;2!SyzY)=8Krjelr6Ie5O!|=o36t$3Vk+ta^qdr-4pm1s}fFp9uO{_@7 zLGq^OFXGail}sdudi6OhSV05`Rq2G7tUFM{ zTf-hnNuN;#A;{o2ruu92Lfz*t#dr<9_BUWX)k(KDOFuN?s$=SgZ+AEDt!v%k?qU2x z{KvGZnxXy44;0vQmU1Xqh8bY5_r##@F-AY&ebt0J2l7J?iLD$ML=LuH%NZJ!u$vyB zp2DbAv?Z}8xZ_rJANAY`1&Ng12A=imm7Twc^r*LlFKcV`e5gNs#G`d`6Ck=*7Ce9r ze8g3#VU1n(N6bYnCX9ud>0qZ)qXoK(7yhk_>wcqbSA=Crwq&#y?%?blv82aD`^6|= zGQrSmna)2v#x~He$Fv_hE@BayRM7Nf%j3O*1#*lr7hQZ~wtHPhcrd$TO=jCcV5yY> zDJExESHzwfW6eW!6`Sd&4q+f6xV5Qb?s`r0ERXOx1VF#E+Vnhe!@uG&#-eYquc-2v z(xI;&&-7^gw7KHd;M8%>g8XW4$cS%q&+XiUEqfm^JlC4lJoH2^!L?`M^`nE2lR^kU zkw9cg8&hRP*Lr#X1{ATODWl+VnXlx;X)w)o+*nPQoWA8A@Ul9idX`i-u%i!ZbZ*iQ zZ^ilu3E1`O>$TD?3i4lQHBxle1ZJ^i8Fr+G&O009|N0le|G*Laz|RxerbwZ5yNc-p zMXTyg&{WE#r+#-^GkrT@Hm*DXm6uSDbnkIB`^)Icus-{Y*l4;2Q0tA+*VE_OC literal 0 HcmV?d00001 diff --git a/docs/static/screenshots/textinput-flat.disabled.png b/docs/static/screenshots/textinput-flat.disabled.png deleted file mode 100644 index 5c47f3dcb1005f1d8f666c84039040ab7d284603..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482253 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 z 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 GuKy2X-T+qs diff --git a/docs/static/screenshots/textinput-flat.focused.png b/docs/static/screenshots/textinput-flat.focused.png deleted file mode 100644 index 209ad285391f2c94f85d4b978fe415e5b0deeddc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 613699 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 R=S=dCEs68-!p#=VTUovy_D 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%0A17a<3K=anR&Z@a*Vutjr2Kow2&lgm8K1j*@x)Pd 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�vaA|m~Ftc-g7Z(VSTYt*! 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 B8-BtKW=%W~;&n4nr4B5u zpr+4-ol$vJ{0gw&sNJBWMPq8e2*7%3BpP)^!A?zX?!5fd>qt=K$kA(qy*U0{~Z%N7zjIg7o)cis z`BFy~PBqa5^{_(v(}{twO|kIgp-=64Pi&I9o^~aA4|)$<7$*a&jX~$)LL~8hfyvv$ z{>S>x#Thyk=%S_&!xmtj6yNR6$XpTP&R5mhCNm-{V80L9iH?1BLwT27Jgc_aujK-B zSWxi(-u|6$K(ewg$1U@_*3ZYkc(&3|SV><>kzV*kr-?;>`r0-sl2YgKsMaWW{h6P$ zN!_XbK1az)b6>HI-x8QVcRUZ;40B#aHGi_*&&P^PDEge7%^5J4K%18i-MKiZEI2xn zW(djE-Mng`EF;GN1@-StzfyxBLHG3!Yw==8XQ?5D%9H*s#-FXlEKme{oRGbW<;HmZ zR@+zRMv#~T{b{FQUMQ3Ez6Q(va(y2?qGWZu7qQ9;6sFH>V#wLZ5wb4IM)y~~L8zGM5hWWDwQp1Z z1WjGGDhxc#wR-$=a<8V+%ig=}!dm2KfrR4?b`^t#aal_h%0|R-(B{VXmcBbjtBZAY z)ocC7NxBoqN4teb7bLP5w7Ze4+!l~H?8}Dzi9v(eVH*Vs`+{U%w;#YqG4E6b(J$@Q zhF?8Uy5etfaDSsAckSL5FgHNUd*QPk&fj5uu=xH<1>-nV2yhvC9P804Id>^yO4HNS)w-OlY;YZ0JafzIuAbyYVrA}EZQ{euzz9eEP2*|Ik z-1Cy(hYBNM>d&2{#J(X)m5CVHzPfcyKFCA+AH2AA)V@kTR_ywFU?hlS_qPVyw$D-d zCIH&i4wE*J4O�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 z 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

?$q7c3N| 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

Tz!JoBaYSxjr=B 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 M|FL~}d(K?@U!R!6VE_OC diff --git a/docs/static/screenshots/textinput-flat.icon.png b/docs/static/screenshots/textinput-flat.icon.png deleted file mode 100644 index 9de68a4bc98117cacfff10203677bbd96668ac34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 605386 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

--4sACwRjx2E>Qf{OtGMX%N6(m@ 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

8x+XG1_4gAO z4Jk{^)IMNO52z4305OD0?zUuzwtKwT#X5Y>s!ZQ^D=G%P@Se)%pl&X@QwjqJtmnZE zy(%c7SGO+c@sl`ZDcYD~^a9gIhSiea*5X6R$8GTiMo=x?wG7}GMUqeS)upKtWfJI- zGl=6eT;fHB+*Q%P^3m;5gt3`3w6;<0Psfa~`~Oz7<&wx7@?Q4EJ^6pWOaOb0i}}52 z)IM8D{bxo?=x-lVDSnXV5CqOn7 zJJ+?}?Xb6*gJ+CY9@gqInY>qlN(&_Kro7vSY~An&Xn%f-&Tm!k6XQ)h4x!8v#>SqN z6LxMl+UD1$>yfv^Q>sOQ{9Y|e9wz5a3=Qp=#I^<)Il$neo$z*U;7}{@Z-O*@nmjW1 z1;DkDdm0$NIji`g%(yk((mm*Ou|LuBpWkxHh~v6`#dWc4arWKk*EE_m{3`NnI%shj zxIX;#^eY(j-`wn~x96=Qzm@ zU(xNwQoiW~S3T|uD#(CRw;(1>PKo!=clMnsDY@G`kd{!pdpdmomUA^JUrP5i3am^V z0~4;dMwW3#My?y_)yG#5Us1Bv_9_y|BS*WD+ARTKfAN>DzZ=^dS3|^vV}}G;dL5iY zp#i4G1^J(2=2Nrt)6@Fou>jFmSGigvR6Po#@f7!``E{eH0CpVabUXAt!ca^G`O&dF z!0P04&&&0u6_;0%g;>YrZQy@7wIRg0W1#rK*`!n*-M#o!pDY%0AYNwM6u_nh#fX&M z8W8pc;oj(T(L5rMnW5WpS7d3O!8b7(7AM{V?{z>PMWX#vZD|yi4W!3qUbNjbpHOo! zhWkXyw|Ew2jRm4^e6v;z6rY6%LPrOkCq$XtvM6ybURi6iD2d`sCl!LxRj=!w=+SrB z8I}2dPd)y=`hqt4{3cUS?fR7y-%K8n+;2WBb)TLf7v%+7miN4nwmiB;nr`X>8=D#0 zI_M{AaxGN`O^&)htZ07Q=UT#>?K{=h>IVR4=`9}$Y?qG|s4W#fTeNRNy8gr=cv{Q2 za!fM&MWiUeoMC@-(SC+Fkv&+gP?#(rg z*;QvBW@qX8tmnx_c3lB9jqu!|MO*E2RG|ObFDGY$xmR4B4jDag3X}$T%gcPb%g1va z=UTsznH^#ccqs@NvlbK(T}*G(2X)W88<_-FVWU3?P|MRFtMt_n57yTI1~>PKXKcG{ zrj<$&yDtj@MR$PeUTM7XeW=w0fwXUjc_jE$qiD%Z&g+pZZ0^g_2*#!4bSK_jPJ5St zqfA*ez2OOsVD6CQ&v}}y)9+pVkuTXQczx@k9O#;ZTVHOrx&5P;Rv_ZLk{8JW9CKJh-Y^g4&v4GG~FxHdYC z-Sffw4WuUVdR(9hbhTnzStE0$!1S? zWV6l*&<$im%Mw#3&TwrGUUx^^iExfljiUk32?kH9>6$Dfd1$6jblRS?FS*_YHdhFi zlA$BMxqfbo7ue#ZiO;kFqe+&BW9#2nZR#eopf0XT_w07bDp`$;{ptJ)5bF-5-?8t9 zuG8f7-TLoJ!hgQ|^nBCu*4t{F&wjOu2vy&@-)&&I;IA)-jSNP z8dYO%3_a_Rs<4bzry#ceJYROo`oHn{e^B~A&z|qKqO{6fy=me6h$6abcvlYm`F(|256Nd#`p!sJyB@EZGr7=EP5vDM07^_R}OsA&Uv+_d}SXnS2yQEvH!XeVE#_IO5b0iYKmNzRE)28B$_x86%ED_vi1<}R=` zjup529wGHWZf>6Nodp?h!xRkp10x}2hxpw7@?DVDE;^((uS|HlDf`XR0}zQ?IQV$} zfJy)!rXty)4FLIbDLYOi)kuCUPm-+luENSiJ0a6K!s%x5@*7H_?NP6lrt2HPL`P3k zLwovN0L(b=AaU6#!eL)FF4!h=Rc6wE6hYt9qfG4o|JF4SujItVt{v^}f0<;hX5wg+ z(#KxtjS;NBau|jjmYDNWPaXw~&yQTN6Y%;UUzDR>f|o9G#ZQ$3nbqQt6zN5@Z@U|F zX!9k=(Nx#G&&oD8pWk% z+MgnWG3;%_poZr_dD_a>xq(Oj-Msx@XI85g^kCYgpJ8!&{kZbX`j4o6^&IpNl+05z zdsJuglE=mdg%2mT67)9U{l0r?STA;Ycufbx4les^i04E(7IRzmpMxb0(4AV8yYEeP z^}VG_H*M#7POSJ~VL^Gof>;`0C1`Un?gseJqWGfYVXp(j2Bw}eUb<=D+o3pJvOWLJ zw^>&*`A>f*RF|7q1uWsFg9ka8U%Woj_D4ELa8tlhg6BM_y|>m_e@Yk5B-Db}mF)#V zZvbN~LUx~mhgfim8s%4l>n1u)OB<%b;!IDe{x`|uUugQzD=StVwRSW@xB?*5A>zEe z4QL^x5X&cB!NV(8vnOrL3>^aHPGf(&2a2Dj9?u2_8INnbswYiO-F-?o+j>-ScWd#O zlS9@>>P4CV^T~0k$eB}CQHtV`dLzO$qR6SBBJkb<4C;fxGYUPW{q_a&P3oS)MU`2# zm*tx>YJQ)N@4*=x>oCF{%QO=vxLQv?=UKr&L>{O*51;qXv-zMG!haBvdBFA}`SST` zs5>C?i(Xml$1L70(#WSWvDT2wdd5N~pcQYWq#S)_|Ne=*(`ZKpy5#><0{)k$!D^jD z#0#*@`+hy7PpbV94XS+_KIo>;fi|5%P{y}9LBM95Zmwo?=SkP9Cs7lwl8Tel=ZC!w zE&sG=)h&AYGT_T#rkFI@u{@Nmi5vGnfHe#hXLcp%rv55$Jjc7>l{d4-Jn@BEue!n2 z2fj}(o*Wo93sqN1nENE(bj;ei9&y<|IkAz~WFo$xxyA42NH&zdGzj* zYpC3*l6<<4_}nEh`Um-x|JnBdCy*{uS)-LayaVo4PS!LH8^jGG;wt~|c=1ovP{xdt z1!{rV+ehyt&M~?LOC>86t)#MAR#HXxnsEOQGam%4%@?^HzOTw|+Ns$c0DGZ3qR>6R z3$7w7IZl>I6LjC=AUpCyld79B6&iP4UhI={N6c=Yd=1sb;(@7Gx&Yl44?%v9(l08(a~0s zV{qZ#A;-K&A@CHs1@HJgcfA*6-XJp7mi$H=M;A8Ahl1-0hGW#D!u|fQVEA8OZlOtM zwyLLv<`qKP_6F6J-@&yZZsV5@u%8I1Tcc#$|`SpdE%c zfS!JMaJCnu5B2(*@UD0R(#f-DmkD^>VQfIh`qS}J-H)CVEzZ;IsFaIgU9Q1a;VB*m z%QB*TN{x7Fxna-orB~#;SXMe7pKj`j#VORVxcT4Rtw;iwa4z?*a{gtp7n`Jl=WjB~ zxOv-r=GC?TmHPb8Uy^lJ{XHVias7OUU+q$459`md2trq|-IwW?zZ%pSJB%@d?#n%H z;#u7)#N+zT0N2F*!=7r_0&7W}{WDvHQh&`D_oL`!V2;-9uN-2Qn-i5v=T9AM?0uJ4^gMuu(zd(nHV4vBm0&=mxSnn&Xu zngrz}2ER0Tsz^UBcI|43edV41$GIwR+qotk#-Fkj(^5(hic-0LoZ5ER1s=<~PB*Tu z-tk9AvgV_Eb{hLsRc^Z#|Y=FaLRjWgYm!(NDg zHc#avEP@TX()vRQuBW0b(-#q;Hikb#=3W-08={lz$Lz~&8f-#(%v)OymZw}ZLTz!; z)h%=vCpqSCHaGf|voR|Tl)B%!qDz^|rgrv$mkjTzrvoh;`n!3V6d-?E^xW;>Y~ zM3@xRskNSHFiu#Td4>W|CDiG6<&z9zTc1io1?y8!6`(Zb2sC&w_eS zW)Xe1SFKYL+Uf&gnj_xe+KFP z_j8Sd(F@~*<;08$#?n!@q`7eIbEfbGqa1MA=b|Xc7aUvkvW%iBrET@AQ z1>f3!HGDL(P96fv!#*#1Xj}ICZzIQV=H^}~--OpDeX(ka=y!^k+{wHcCB7ipae{mb zCNC4nPR1Lxif1n!^cf=iN8|;5pol3RWkzm#$OO|(m&VqQjyPi{yW#Vc89kr*0t?of zSf-?_+_^3Q?&G{~)X`@c z_d02D7rL#7I}U!e#`A{FUuAvA|K?}3H``EB-k6*XyijZr-r8(xW(K_+#t9LZXJ?uv z`fLMZ>Ow|3p|3y-Pt^B*an{Ta9D1vD`To@Z$JcvDvmL(w|3*k;@9}xRKj+i$@4U`Q&hcN|_v^l| z>-l(I*R@}i0vGx5jkl2%p~oQi8ZeePkS6lJWE!2T)Iu6SV3FN{<$wK+glHXm^)TCK zwq9vvZL5(o&8R5hwoJ%$CNUi?!)^>PZEo%;P(rUx_RMkFPP*+ti-mUGrDnlg=k#-( z>r!}k$MSZ^$a{^xVQ^^%a;2D7yzzMnA&4=gieOl_!dYN>y=CS03wUAh691^pySI@M zX$S1Yi}C`Btt;2ppA#QG728{3CpgNmv>)-SZI+{1{3dSbr^rLuOj&Ym5erhg3QK>U zZ%&`RO}*Ye4XSZnT{Z^?Oc)rGO46!y)ABk?i&xsU1`?QQhd*t3n()hi)sOsV*VY99 z>#R&d&AnEi&k(|)X-J~8LZ3CO2hIEwdzbs=KbNm|Nj2@#Egwn*Ii4uxI88(wfy85A zVc1U{V^vfA#z4=hZ}f%rd~%O{kF7>iQ(9z;j*bHc#P*jFY)fKHaBZ`r7Zgt_6&;@j zttQy1tdW?P+IjmQ+w&(!SeDO-fko-&njNO?cI6T*Klu7Og z+#a>8{YyMV*s;~X^f}@k-Vqbi^+so(UhS?DzaAS++Zfk!@UUI*@mdNVru6u%ad3@D zKFGf2fJHBUg;2$(=qT0moYPx*iFemp#-oiFxyI;TUFjoSH(rs*i=$VphvfB}53EeV zvve<3hjW2o;d3%cism+1#+a{ywBF7)=QBcuEOcHEv+P(=$o%ToE}>Qa99AGwWLeHa za*Inbz$EK%Wq<5bH&Cw{;HE~e7QQE5R4 zlbdA~#Svp0pzI-J#{YJ_{j((fpP#Pt3JbRk?E@$XLmO-32^KZA(zB<877G&Hb!m25 zg)(}C6sqZc_uSqLrzLU?l1msiAa(Ysdysxyyi-Y8x%&+)rh888y~3|#-6lo%fglL9 z$xe@B3GNvG{-Baai8~4bqbDC`VR1Nyl;yapdnI4OykzCEwCE1xtP1d(A;&I?G6l zzG41=ipWi54lOwoAQuv?X+Q{Uj+eS|iHj*PZ7wgxZQVUi9f6`a9tUuEbOH&Ptvcdi z@JLR#ZMP7+bjCJnwNNBDon1f3-~HXZNk3Xhg*xNtASVcPub)23#vt=|3C~W&x1yJ8 zfvgoV@XO+52f27Tol5+?0w~P0W6F)9xXNd|CeQS5>$!Kl-Ilt3OsFWQsO#o_FO(70 zlLm8_Zwd_KK9B9snG+77F2%#tC}5p8I+$T5W|h7e9?_uga4Pu44X+zve4($xT<_2jUX)B>K$p?r7AnC!Ejh|9KUos^0d}#P<#7- z!>a$b%Nh$p?^*4w@|w#w;T%bHLJ?Yx9B%>;u1 zEUKd|UvzwIWH60WIv6UJt4vtfk_6RZQqk_e@7-3~tW(-O2F(?6jV7Lv$9Hq0YCY8h z9SbBF7D~4`;s?pC84GdwYK%oGEI~suHD{nGHQC38@)h*XgyBYMkUr-m9HsQazq1b&O|wg7egm94mO z$x|IMvtsW}d)_uT`5whk@=tIUA!QwF4v(!JYEr|3hE!_!zo`E4h>^ifKCn}aw!^74 zQGiCO^B>{xFf>u0dRm#`K0BRMWqU=TS4f><0L%_xK70Hbd(1q)&%#v(x=?-{J9Z!# z+1&6;W~BFm^`e^3@|HbIjNdG0azsG1x+mZNAoS9@r5Hj1eBs~129rNwL()Gnw3DVEy9LLa zK~Y;CM!NzZpi2J~hq&88I3lvZ6}9KNl)==O=^`&zHTs-C5HmH%vadO{(1*CMvTnh^ zGA!K*F|BcRZyzn#lmMjW(nfUH%{np|Y|FlBCfGXegfhOcLUC9KRA{3&-o`=EaTl}#Eb26`4m~Bno^uaRXk#Zco z0P+-`E(M!I=vHcfZv61-&zX4V*a-a z#&uW06;XH1XE*x~UR68&QE;_%{VCVH)VyJmmK)3Fpwi+-rq+*Tl&JiEP-L>oSiz6x#xs>QbX4O1@bfZ#5*f;ZD)|g3Df^~0)D(slnWdt z4*CZ@jpMW>o5VAV|MnW(k#AYVe9tbC>NM>dB$-v3MqBjKN88Z~m`OwZwx{goH`z94 z%bM|sP|W~DNVGgT6uUG7Bi1TA-V}-o^cbc~aNEbUcPy>C+`1<7xVvtt9nKZsB40@} z9?^-N7(sm1PteEi(6co_c%Z#bc6~krMw6~?T zzU+@)r&7P~T9ptg^~O>!*k2C2z|8SjUbdR&cx9}j5(7&}ZKb;wNLdc&DfNoWnMdXhd?(Fe#Z$)Op?#CAn~b?V|2 z9@A|HYh-B|F4SFNf_2>RR%UKi;s}~9;t>mIA@z?HQJH-#^#W~1Z74bU(+s`;JRz@Y zy1(NEr(B;p9JD?o)_a*oZfGc$i2r zn<3EGSl$&;KWG3D*=N036;j_OPBhK?+e;Dn!?b&upyEnXjAZJ)+_Rc0nEUccmnF@` z&lO5IXAaaIm!?xPjejt5*%fHUTXr$?w(bupJ07$ww?D8)DK40_aZz&P>Z;HG$ZFfKVDcE@tv-J9Rg4V=;7<6#BJ3t^ zBh~bEM=mYAAaz&Yja?RE$&Og~xvHnM4GV=WianTwf~KY#)d2tS_nZHpWYiE?=K`$T z99Y~BrfzfLq|cE|Ginz%SSTHZ`b>gsc?1->CQ#Rq7&1dj$u|ZW|qR# z;2QVAENY65;TA<4V7vlc&sn#;K3_~47SnOdxqhLuMz^%DAe_>LVI)vt&0?ET5DE^Xa2-K ztiM&M-hZ`*aCC=6M6dKm4Yfc7z=3W*)+XJ0+ih3PwaZ`M_V$;Tn$q0j9r3cl`~@E& zLs7gVf22Qg!f0Y_+LnI44Q~ufzzrA`EwYv4_t%ftFIau`#H!P<4`?Ig$^Ua zDw(p8+Tx|RT0C0z5Zy_>g|m+#qjRi?#(#;!zw|EpN1xFLk2m=9d;Nk?6(#{`)LC zVEKFZeGi^Vi^X*K#LLul<9b)Q3-+p1luYE^ai|RV3Z(((?*Y;%QcAGryRsY?~z2%>qGuy{cuvIuf)vcr(td>g82^Ffjfp5FrREAd2%23S_pXLFYm&4u+OR0H z&4IFY9vcwLHj?&EwsduNL>npAM7HqkC`&bC_h`P38we}E)%9Bo^mA2i7C3&;6#1lK zGqjoxtU@b7UY|ZS_0x-bwH>8%Mmz3?E*G`da&)-7dblt%>m%s)mxcU?Y<Qy0$CNUQePy_SErk6k3q^-Srf6bvAp9&0`si@Atl>kgkGI2CpY#DCn$0(Ty~jm zp5>@s0pWVAr!_8Vxs3KKpx^ZOA>lP__-_9%KgmBmn14PO#C_Vd%LA_XE0Pm}pG;$u z#>O<4DP!9hTWZb&17$^zwAR?H4i$APn{Fi=R(8Gcb10yD`o%~-<=zpEGu;1yWe(BU?;(FL#|hWrC8!Ig zFuI5|P&7@)if)UcS49)T2U}luhNm#_RgF!h;4&Y^gjHb@zCw>vb%T)g@i@c2IR2_y z5wi;nu^h;6zd%7PYd0F47F$YM9CFN5n7FdPAfdylAL~l7P5i9_@gU+KBIEcbE+epqi{LX`iq*fwP8*01felt zbqh>Xg`9Fhl9;OH|E@4$jpuwU8mPYeI1-qHXcTpDTK~2+eBPFl^G_GAqjCwbd6lZT z;``D?sobSL2#+*Y&Kz3vnH4n+gWMOib^97kV^hR`5QRuVp_Wiluldj3$M90jpbbDx zis<9=3E_loMDzinPDGr`QTaMkSnTbX78clV;x=yiZ>=OFgM-dJTOsTheWVF1>Qbt) zMt0^Ggk+Ua@79r4*3Ocqqtr{dt1u&4%(}7e`wW>mCOQ|Q$D6e3EUAErYaM?yK0?ul z3{lxGkwgDqma=~=RF4qVE%jK%yT&EB_9w>lOquT5b<>QL?q((|WMppi6rJF8x)-B5 z@8XhCYcH{5i}+*=e&EJxoK@8&#iEY%fo?rl z!KOI7T;Q*SkZoO$<7A%@zVSrb+nkrzEDeZ^5o6JM!dbK!Ap_;A_{Rx*OFnH?z(N!B zs?5DUBM_&KVkMbg0Mz|LLgs^f3w}{J9HxgC^%PK21~eRt?zyMVD09g}gXYgp4O&tD zX;1tqN*Bo!x0kfEk2iEr`jJ3n5 z(g2TCy7M6-DpUEI7EVnICw|~r87Ie(LgNTm2f zLv;%r-lczTTG{teMD!Hl4_~Mk)r~(Ru(RceW^c6J?<^sCR?!+AeQdC9+s zZIs8m;@9J}T$6yNCPII?*(nU&f`?gN^dreHX?6c9wfSG#Eti5Qq0J2>(Zsz`$A@pH zp~a{z7BYVnq|SxTjQ_(y0Y}P;KC|32JwfrK+(CCWD|(GnJ}Q{$(rpyqV&e+&a?iUo`Z2 zm}QBd$a|)U3f7D{GK{gDpmbCbV?d}V*6gfHf9t4uR- zfW`@;fEmLu#5}07 z>5$NOVfD7H*J#DML}&IIG4sUrR$mEa0uiNpQu10r`Ev84`f$*nhI?y$m@+p42Odd0 z^7y|ukGto>ro)h<&#c$vAC1$CL0IB$zn3!G;-F;deD9*GVMm^WzqEK(#D+fwbCg|} z!;s)D=ji7Hn*tym78yv}5-uv+BSMQ}=JDM%NRs8My*JyEPQX)~9h!41M4r8 zPGQ(QUam2KG*N3N);NwQmd1M$X>%>?m7hdf0*{!e>`tiK05%2i;CK%5;7wpz{lRIK z-Eb+fujK#3#XtxeWMVmWLH*SY^_@2|fywK6X&{1;C20%Krd7DiTNp8Yz}I07(0dG( zr9$*d)UbsqH6v}4rtBOOJboJEDlsLtP1FQRGy#1@QTS zGW2_tPAYUa33#ujp3*Vx$9iWuDymNAsjwv1ikgQkH&&DoJ6aWp1RI`R){Sxr^tS*=6nlM|GwRlnK(?>ts^ZyRB%l1Ijfl_$7;8kP9 zNX}<8kWHR1cUB<3Uy;oo>~iV9Pc7@(giL!G?6Oq zgjN6b4i)!m)BLd8v^BKNBV#_VT|OWms9#U~YmPd^HQm<$U4D36y4!0@d^Sdy9z-2v z8>-Aac;C+KDm)m|_mFGQFNUUUXwUad)N{Z8y^aZPs`GRFQZ%t~ka%&#k7&1w0MP(C zXmK=mbbv_xAGiu zi=`3;cI4e(UrDcp7&o$@@zj6zydhY2w*?Z~hol13^P0DyLQ3%#Cd+5EVme$ozk}ZL zuS9B!*|Ejbeq!OBjg6{+*UE3$szB4%-mi$Ft^dJ6b2tb6<(1c1ahN4)OnOZJEx7+T zQUN2!8iCgR?kX;iP!SC@lQd*Zu!(d_gE1i+RLeN8MvJ-kSoWJ?l@gcsYiuTjo%1Qh zW81Q)b*UhzT9A`(F2zUIoqg}+$|wyXSpCl+)DyZ#?Uop9a}{|l%mVI$CK4|TJaMqt8;{d8t@17!N+{-DZ#j?SKXBz5uaK^);_SXM%{fXg=im& zTM;7kRuSc|F580ve~S`I^5wA~XyaS5BHPWbrU&fpxw-vJ>DPsWd>)SzpfF2xyi-@- zu5V|4cM^8L5}q4Jm;vp#9|jSj@H2xno?-6X5@w6CfljlAlo^xmZFC0Pi662*6L~lg zM7qOgDwZ|0-TLU{Cx)0f7b=}0LDP2TwuMrrBbXO$nd<-RR^H>@3CXm`!vHOI3BfXR z1n!F0Q^@@DT)D2J#z1i{2{ItZkIZ^B%`m$3gk~lHZM=xeOeORsgbmidL@tb=3@Ns8 z^>NJ*ai7n8xpndxFha+VYIyL_d&l@iuGRJQm@zRXUg{hX@C3Ts?Bf3qrMBFfVDy)j zJQ$PXxMA09IR44azQ#bW-lC5yC!q=PkeQ5n%W>DmMWDT0d{uvt^r&B3$r#*>6#Dtl3KRl7;|<1 z16kkS1A9ehD}mx^$UzuNu1IBQ!{!|j0W%i~5n9?pG0Ooxd%cLl1mt?)Y4*D7UEF1r z4~XamlL42CmT3&hjW?g^(N<~<5aC`HrYDg0Ii`CBUCzQGR%K!p#NKN6B-!a5quHS@ z6$YwkGKH%T=4W^DwsV);=ouSgp$*RTY>A@b`jhP;KMyVO4?T}aLnCO3tyn?a3i+C^ ztBbLR*Vv8J`dEPTBdg}(j}r0f6f6%!;`qi9D&ne-bh}5!?&>T5;-$((?v)PV|etitB3w|NsIW{}HCodBGArs8Aem>FUsH{Gu z`d?4m-K0?R=XpfO(2yhco153h)fXgR1dVJ+z2Ld3l*(YfKSZm#7}MgYdXKq{Sr&G4 ziUe-%ivHsBT~=qiH)y&*y(5Z9IYi_&z3)gO=`$0z=)wyBjqlIsv@e)~A5nCxC~_YG zfU4y3&7Qy#%4fw7OF&N@7q1kE=Ys^9?A_mF-FgsOcqNn|fHY*WvWxZ5LL9~J8@Ok@ zOCd3L>o3Lk)9x=kdU8AJ^41(9f6YSCfmJN{A`b$P%jg@d{JavQd`&7DE*^HJ* z^9Du#E-`qm_4KFB=+egz!yF@){NFVb21~RvYcK;>zx!>0*{_cq5p&2mXDOZFs}O9? zSy@#Nz>P${J9WKh!Oz6 zuBB$31QGT_KN=csV=#wohUn{hFN7^qDSK~>OLl?WC0*GuPmIf9CGuC4J=vwNU`58m z5)`H1QQ;rPn_WoxSli@+8do&k_eMz`}js)5f@+i7;5QgN#U(IAK+0u>wV_r zO&DWpxb>n$X~mOyKo}^ZDA9nA=`WtA?2g#*Y;_injF#!r7NaWFb>H4_7%m&oO(0mL zM}nGnT=JQ`lO2S>o{YB1KYm+HluF|lCuwVWFZ|y^FjQ}N3}~yfeSRY}FX-QAhW>ik z1tC|&A!xi;Kl-{-nF@aD@8ZZ_=i+Ej`DIM^8s7hn^+8ysxF4rjalAaz%|n zq*~2MmFMaMaye^#-cj`qk^L_!T+FM9#2ZOT!jcG0-MG*a#%}Ykx$#W!6()dq$YGmC zO0Xx9wKc`$W{)AB53pb8f8@PQGYYU6diFu02QxBui zq3isRxX0uoY-X^b9{e*zbt!v`(gbijx1ZiUL$W zPph;dx&?h~Zd`2hEivwPE;pR6`_-6;i7P4G1wlD*U}^NO zjz>bRWdKySu4a5Wxdjo-m^2E(MKI1mF9Xl;uwix2IChzu=LRnk8L3s@%&~^Q+{}0P z9Lg#>E>7V~v60kluN$|^ixOBuDOn0Gl9Kj_Z(~=&5A@^MB)ST9GC@OC6D7>G&lOJ= z=QhR9n{i2yfgN}9e?LLD*zA#?L+5uULZBgjS6^_s6k}dljvKT+|6Y-Kt&QR*h;5VM zj{AaI3UKjo&%Q^zkn&?Ut8#>d63rP>Jx8qa7vNC!CD%h*`N|k|Ph6A;bYYJV<}?^R zO~x4_(s~%fmD$sqixwr{NJ<5avW1O3%L!1`JwGeI)$>IA_0fYaw|E{tT1suRp$+2E z<-8+ZQo>;ScFSjsxV$H34~l%ZT3xF03;-BpGj{dY zo}51Jy!cRF&~_+&5&p2%daN**!FKffE4>-lj<|D+{Pw(}PNyezx~${kMK7js>t-7o z17BhHKv$F5E|1cA9XF(M08z(U)wRZ&mop8;HIWHD+T4?;NmL6j1&u7;lf+{eAv*iMbe+qqxut^?x2l@ixAZ;)j{FukX4w*4CE4JIZ;fT73m3 zzkvdSCGSiu=MPhou{Tumve%5P17usiA4ieXenx7%s2PU%F3=8!GbwHc9JjF%~nKJh!QZ4OPW)G}XHW3aSG*axUT(=kVb|m@L|VnnLS%j%IWb%3Sx(p&mCv1U?ud?Y_@Auf{(M0${2V>#~U&^SA>?V>u*ImAs)zw`zN6imHYl-Ty$}Nfy z=I?#%cj(ZkawBKYqlcdaD;@7WA@WEDSL;U^5|y2;Wb15 z7wW>cUm}Q(_C6dGDIgHKFjL2og;COE!gVQ$u3SkrN5w?yVm=5CEs5v1*;S}=lHl&^ zv@EFU2DWv8n|<~AJ%i>* zV}Jf2XS7}JJ{#J#q?j2SRz@-0474qQ8~y+TJ)@nfR%<3c6hD2?`GXl+;(M2DL!sW} z>$aviWA>sbF7LK}0$0FZ%XZz^vGiJrj7f7}nvuT|bTwkKOtQR?kBdy-BZ{9poWSQJt2TLSL z)MCA0X$hpMc)nkk&2}9_~d#{7&`5E2CS)ab4fVzLy%y{S-K3D?{nH3 zJQV6vjiz{6c#%~?9~UY!o-pvHye0Bt(n-=g;MdSgJc{llY0lsR?|=t%e+igd zY61h_(Qqht_C6=Pu+T<6#x{rg)M@XJnN$`wR?1M7^rl-elbX+2YEvVC3q@~|b~3$c zpUTWHMdjsPCNENdu=!80(Zhz7W)rBt(qPa|uE9>_#vW`h_yCosH$p7jzM#$k$k_O) z9@#*oJ7OCZz|l$QLw$TtYS&c`~o%Fo^wqblf{T1>rdNz<`-ZO?u^j(x8IYr z>&jQ_1{39)nxO8zJIg5qR9 z2)lST;c@1)J?PU#-qA1IoYj1~HVc82R1xR3YnEuU!# zRA^jmNUE8>9Rv`ScmDQj%y!~4eUY2gF9V{jfaF8T)|IAwtD5dzdhGeqVzp6(;$V(a z|E&P=_FLLn)(hH!QGwCYT853Erj3?wX}_W6w>BMKwvCst9onnkvngHgS1}&Ul=1;8 zE7?eIXS(1Q&2s%)THP;GTcGpE=^E3Y^1{{cq@2;_$V9DVYR34dzC@{Gf-mf_LM^s~ zS!`&zUM}hvAgdqVvN~#`nmc8(-tAxJAK!`fyR?X<1P*;ejTJ>1h`V}=@()$4o2_^~ zv(3cMa}nCUU9jyCuT^z2F%4$cx;}5R`|n+Am`IJiF8sS;4bW~iTBWOIKqYIc{5VjZ zVNVl|DBT40s%gT__7C&neN}BCX?>@nJso<)g3xhT{c9y=b1VzMNJ>*(@h~Q|L0yAf z+1j|GlS~1Yyjf>0vMQ##)TMIV=^;rZk9-2=xvNgAnjTe57V@9VIu6OkCK;| zb4i#k_e0~?h-lkLyMw<1fJSSov_#m29zw|($zf##XG4a1hNdff8#JWDAu*uDRAj*< zev=8Glm4px`owe#cKw;j1!0zpgY-h@#qkI)z(4tRK!r=q-F2qzj|{psImS*d+Uznk z5m$&ABq~I{s^Z^;zq`fdOWZU|Yev+n1b)nKMsog@MW=!&XJ&OUk8b}a;%BlFMm$s2 zEnn$;K<{`Sx^sN89#5$u8ez;J@<;kGGY)h;|r*3$ zfoeO|!2I~BqSwh*RqHwCQkwFZBb+4Z%!?`_T{m2ICwM`*@wj18nG1AmGvSA0D!z0h zMHgned_UuAnqMubhMNVA&IO&6hz{@X7uI0z2baHqRntmm_|p3ynkU>*WE6TE&UM7p zrt#%RbU=H=Iw8GPVHrv(btrR);tC2M%#ssYSt)JYt(=_q9ap|YONhwYg_C%H@!ob^ zO*N?5u9@kT#?IbvifJWVGxl!h-vK+CK~0u83N6U9asqIIN-pvz*c%UiF;i|Ke-^n=SAJQt_(>n?UP z9Lr`041NzepTV`FG~= z=X)(RbPu%9cg}FIWO8! zd|D!x8*k$|KUAQ_(t^>d@IXwKK8M8+;2pdENIry=Q=Z1ctMKMWwM7Oa%+)NheWFla z^|A2PsE<7We>XDLz#j}B`ejp-%me{!)~keSK>lXWBw~t0EiVB>vYs~HpixXHtr87L zJfBC?ktksCQ%$G3%avVX+q|Z7l`F6aX$E!$=PVT->;vdN!Jw|gR1`Ax6PQ*Lj6YYL zvHj*$Vrj&n>ncp1zA#J@aO1bk*`lTiNpndY6h=}f{+krP2`f9(5ty$Y0P17i3-$ez z4(6b{Qy1l7#rW>` zYC0t}`&37#+`4=8##6iTJZ+{!FOx$bH}=_75mb#8!>^1{wlpHg5*D{WfvI;|kXF)% zU%xrQoQXun@E z18SU}J-JXAfR1T5Or;u_*r-PlS|69T^tEzgf39J7T(4o&jo081k!8e*Ta8QETnm*p zy$Q$Z(Ut?8h7Dcwt=OZ83H`M{3Mn;7T|48_I(|zxlhc;DKfp6OM&uehQXB}Uig>j< z7g+p$*Hth3?D_c|59vFc8;2u*&cv`(&#U*d?ql}LYR~&vo{7x_Shjy2C_L*JPcy4& z9$TL8aH^ReiUc&y4QF-@l~k70PW=Q0?(>9iT|tMdFa4gsunlZHnD;T(yZlKuB1cb- z)BGymduuwi!^3i8$jS6}7*xE`T$?)H?^%@MJx z`Vm4EQb@w$*R{9tYM&S&4`DwhvR{fW%(xOrg!{$p?#9N1iXF|a|Se|{+*{8T(uFMaLsaZQmOKo-}hRiftlZw4y)@M zB|J7vCpu!lz4bIT8?Eq6!LhZ<{2Ce%c1`dIZ2xLHmPPHsknJ@ZZ(6~S?nvOmcOo6# z?9+9*i|tx8miT;o2fE|g9^=Y3blnMFu9Gcq)Sq_8Y4exM-BA20+#H!D=b;F*$-CNu zUJ@$ySZ7K(lybt?QMlwZq?72RC>+^->4Frm1R7A zKJm*ZysXLb?YVO^O;~ZzKnYPiyuDkiMcr-NZM$aO*?V27mSS9L(y%|M@#56GL&fDP ze5`igXTIhDIG=XMI9w}r>Q3$wMjy*1OiPJCmFIOheN%B-E|^|tCEHsyBVvFGwPpoW zKW_}zI`VUR{%xWp0PGZZBL}!9m{NrN*lW%LR+BEO{XV9nJm^ssESoF0gYOVg!7eRz zj-5qi?Jw3H6&jZq*Y5DpTE90Z{1-~hx&=)90w;d<3$L074bTQ$TW~0C0h@c1#A|+T{oob9y@Cut&i@pq)Bp>V*!@iHSqP8z$Zm>^A6yisOK-gH+ zC@CW^#Pu~mRlHIc0FZCvd}c)0sH-yLYBE3v<--tsQOySkOJeEa9&sC-2EoGWb)INp z+VrlF`zNR+E4p-H;VD{-k|fNG{e$n%R8C0m4N`k*z!Lzit~4vG$O?Y1w{>M?lsHjQ zvNw;^RIHCeNmaz8vQk~Q;A?wM%$z>0DIql?a?gLLiLn>vI7BW;Js33Hc=3XpT`isD z`nl#dm0Gw)jjqG*x(aW!o93C$cmaY?u7;{B&JLWIFJ7ORiPRDoyy{EG=vK7{n1o(@ zRtvryy4&8R-&D5J9FN>sem0(M}tLz*7XPWp>mm1YqK)7 z=Vy31##c0fjcz&!PUzNlZJ^BI?Z#ylALQd!5xDuUV9beNZs6(sZ(0Rdmb~-GW$Q9r z@Z@)p1N7wF2OoaBN~hnaGE+0YGSy(?dID+D$NpyQ52~7O<;E$^+yJ_mawhpQi_3=g z-7QnLdhxR#R6Auw0o&ekuFt*KjO>tu1)B0Nm|{lz!S#JFjtz`4&eq-X*HrNGfSG5ZU?zI@wQ&^&j`ls$Cw5o zn2xb235|k>gEh0Z#*NNc8jv3kODKDN1yPIa0LQiN((HM?<^yo!wD*j&OaG~k$jM9H z_wszmmg5G?peYP5!gO7yb<>*A20qNo6sDDV-ntH0kfjAF_UOoOhr8x5;5UM2t!5if zT%Faf%5_uazERCZozRFh$Lr&$nf+u5ihb{^#)+RPDHr+5N)OCSme9LFP3X zyBC>~gXi{+IC&>2O9ELm_AnM#K&@jLO{)qh^Q5LxRZitmozl3keY(_ZFdfDlMeQHE zs(Gq&`)7DRh0=j$uI$Gxd-|v-Q`}wJnb-)S`*BPZN+G7Mqk1Y|E+uUbW4P5d?ouZ2 zi|KHWh<+Ap=IoeECnCB<91o&kWMe0y@zc%{Q}ooQlV>NAL$_(>6#jT}u{vg?rc=?U z;ypUaYJ$0;6&B^hRNixSj~{3sJK7$JmVf!3*9i5>3>%`E(qeU&8CrQqUed}+IDLz6 z>OW8n5xd)<$}^o5P$HsL#)`&*FDA!S4zn!sc03NPA5^owHcTQ zn{`oEP-VjH^Zm)lygj9(cX}le=9_3;v;CA_UH)ON%R+xQZ){T3)W*zxT;AuafV$I@ zZJ(L4^NUf#B!mb@`9)jU$y6@j_izh-^MG!0r*X%U#nknSl!yy+{%My;yApP!6}h3{ z>>c#!&Kg`90P?NaUTHkWdck=XgFbgi%DQU;J?{AeM9HVm^gg%%k6cJOqYG;pxs60 zuLx&q&7?WV0Vd`0{4Hty)xa}CSL)~Rx?66iZku)Av%NQsYD|ZLKS-QpEN9xp$oM=L5P|Jm7cq+WL1^;a@T2pH?mYs2L+Y{9j-yDV8`_nV@= zWKhsdd9om*EQJFdN30oIJh26|;P?6zazvLspDMU17IGtliLx^8d1YV^H#$gH^t=~| z_u7--0PCc%Ta8O{f+I=vG#rG(oxVrKeV>P|_Y=fbwF2?9S6A*1&m`;Z-kzT;)I4!E z6n4o)At@fc^o+6$R~C3F+6vkkl&bkn?O2G}xi33!wLX$p#6^+En~o+@z=M5}dAEs+ z_-}dY1t)7742~|CyNRZ8%1YZKBjPz_Lh^Mv2}Eds>LMwu2y5dZdqU(P@}>;J!v5hw zG}GPGI863DBwnE_$9LZ{b6FQ!$Z8mXdEvc;;WyHq*LFa|r@P4TKJST8+!4&)9Bg8R zBe*YW3QnEv>g6@_qdpH;>UTDdFm96cNp8|=b<4!C(w}HrjujW3=F%Vcb)Er&ohCc*+5Fw&oP8J zen+TYZS4XII)qxCi#rY*ACdNaEzV_WgPW}?w3VuZb(C<73M(H90{F8N>?MadIsA{= zu>tF{$`>USH6^l$|-w_ZCA%Q<^aTa(kk_$DF3*HIQdc1D= zUjcF(iFL>cQ5CuURAG}RMY>8iW45rmAl5Lv(*DUHDY)U<56`?9a3%67fJW8ec!Q6W zz;yPh02wQLWHdw+8%Dn#kh4o$Q~19IC`&F8t?isR|Z)G8Cx1>H?y5#a@TaY65(1m!gW=xKdXVCOm9^8HSuix#qr z<3d?++vvpSg|1IK|5N5tF|PzO>OIkq`?r42*SUsVyPJ=DLFQ_CgI9CjngZw^(&{YG za(8`StE^nfXSPW6VXlJvO`yQSSdn%u(0ghEbV3nNEL2*Te10+nEm3d$bRK|t^9>6{ zptF7Ej!nn2or-sL3WmmFU0B@;T954-wkx-E7Au`r3`}0xG-7gProIZ5ZtlpEIlNgr zQ)g_mJee$jWGG(QQi*_u1XrW?kJ|zPMrYnYs-s|@R}bws{!9gT)SCy6H$~B~IV)_J z!-jvrPDC9T`(m{|hycZVFt;5VJCmhfuh<*A%pR$aa`MFpdYcA+FdQlQuBWRo1?Kb& z%|&54e0vxAizjme-M2CCt-+D^XcRfm6Dd&am=^x6mTc>)itTM2LhxE0d!NK|A%gk)I ziC4;6Igdzk85D*1IfY9H|d++DIpS|B_=KcPB zvt}~+mt?M$wa)WA8f1;jn1LKMW(m3s%3F%{Jc+*ES3l!Ft~k>&?K;xxUq29bEL&60 zIHO+8^m~V9`io^NMIHr8GK2Xa)_}33cxnr&7F?x%(3(q|gR#Z)23_llYEc9+rf34> z0}3)VH@*F)|B0|!e~m{G+-lbLzTI+Bv6=KH!KO1;KT`*(I3{XFDogaD)1_|J*#N?d zO`rrY7R4aRvog4_WQ1pVC@?iz8H&Z{z*bE;OHC6ARS9?y?^Ng?%H7fNDt8~~&`jxm z$swf(0px4LzOf(q2Px3CYQT zEWGLbg>`(XZ&+(X>-j~%=LZgN-0I4y znu8h(WX!LiNUHSmxnG}JZYF5dc<;3CK<9Tll#(CC3J@*@{J8tGp?t}krH(#ft&BCL zi%j5f|I`Y0khd<|aQkP&^5W{ig@+LI?Np@^NAqq=LY2*<68+ttx6MhrWJ!Jc`{zm& zr~vy=auUu%3;!kA1){`usphIRl3m3H)qEy(w~08XDdv^F`Q4s;u_O|(j|bZL zEPma&u>AM3lFeSv#Q1IL+fHOOj1p5MQVJj|P%93T)~6&x#kMqBE=0!>RDNEw;A#4V z(Nylx282N}^fTI_*9vOa$@5I_23iZBVnv#-v(K~0vv>?hkkZ)!h@3VF?PHY*6)xuo z#Y3`+1BNud`l+-iZ7LR3Ez6V9(IPUqp3fTWAjQgF%W`h$-TK{>eA|eWmUyk%B19*= zrGx;X$)Thuj@6dpz8ob8s8iL;_)wynU(Be#myql>4)0B(8mqo=9RIey{Z+(ghv2XR z)dMg1945f_vpO|!FyU>$y;LSkz4~FfpRr)_m>>5LkBBDd1(R52e#HG)?5L=F^MS+R zdyXVUdv5>2nMv9F?<3VmsxRI@GXkAX6d>+@)D;rmXlFk2kJc;uk^al=)hi=^1%{SK z0|z^M2_#NE6_fm@LP3_H=Z}mkce$&-#X{I^{NcobP4WnML=33hpO$qj{`ZeXM z7i_ovQ#Woo#0Uz~{MHzJGK zgpKf94AC)ti1NAbFFA1qYdV%Ab)U3OmYea09m`X++@huBpq;(=> z>)OCQt6FP{FO8Ur-evV@gmIaceH;{eLfDhHwF0^9f9L5;jl=4|%zb9x!rOubVvv|p zuqDwIM@xOUgmaFyT+zcU6^|;;z33`jArGYgj*CKny76pUs18lO^S$B>!hUI@*_fh?-K;aU5@Sb&uS-M= znZ-PzR5XOwIj>lsX%g9Dxf6?bBBBpJBMJh=89H^J{UqrmxoqsW(x}gqa#JBEfpT@m zstu04t;y&%54MOJGJN?nuyEA;Ae=LnTo5A7W#hbtb1xlvucy>(^m1R?&zX#mxOSMR^`jb82%28fA9>aNrA zR)&Y*(l*4lL467jKpbJ1v@pvcv<-Ft7MJmjh%aC~5PNSq5;*o*Se)EsJq{9JJmvU9 zx^|}bn2|c{_lHpzjpm~dr|#vYa<^6CWIF^(A`Bh5h+9iCgF!w^Upd~jGP{in=OwjfPMsMOod&%a@Cq6?SI0)I8(s<~34sjHxw;A3wnyID6%o2(!sNnbsl zW^v}+35Fa`7WD2C*Ky_aB8%>%>C773-v#XzMVF)qw|ZbkClh|Z1tl7$vWIkNAE4tb z!%z};va(i`s#x_uKpyaH+LuQ2%!0slbUy6D9y%aq5K9t~z&sEd@DS~yR*{MB(;!4a z^a*Ry#+ioNF^Qcnc(}X$nqCa7ZuEs_BQEb@B0F0!GF1;(L&a{cG^QCV=BZ7ZqjHg2 zszutiO}WpGauhz<@P*tQ7Q@;R#AqRzyb-WgTok)h2xjwrb{c^6J*Xz#V4q5a%hmFQUwaa+gAh0aV-=m8UK+eq4d(t zi?&6e4S77=wg2^_>ha_muL zg+s}c3kET9Qe>QFdLJ9l#qIn>-0NcR#uhUCWw#sQnY*4^6iknmJOq+;xTcftUU2J^wy?^E-|`O=AhMCfmh z&@+QZ;<=>?ws$>>9dXChHSN|j%5EVG=KY3@1%!$+lU45Xc#Y^mQg&x?l{+Qu^+$HB)!C-3i2F5EFccEZp!3v zk68rV3L5ojxU}~XTTu+9U~GV9?Xzcr_Kyw+g8!|QUo4-0t3`W?9fBo2Q#hJG0?D5ydcSY+ z3=|GBWDTPxY^pjr6^(fLVE?OL0J+by)ZHF-R3L!qr}^#$M^p};^=LfZ&XpMXq)sin z;2(ZockHpLU_}G8ihV~yZQuYc%Vob`2h{I3i>eq@%JyUnxKOa3ua_|$aG>E&sm z$h6m9Kz6RQ2MV=9ebY+GBhdHgJK$V!x!0pFbLV~ysQ-R}*!MZu#BexScl)hl#g0MU z-`@XFV;3lSIJQ<>*uKZ^ z@v$F_jx%+v(aLv6SGmctoEes=Xfi;9Kw{Bd2wmExF_pMDo!RLS<_XZ|O@?HsH(@s8 zAy91yG<(+49uFb{L?35H$GRUoB`Wro>v2qzUY}aOO|E*qvwwVCY}lD_;3k8GBnb(G zB}9iMC!JVSjy6?U=Yd}hmp&VB{Xm}T%R zt={R{XymfqoO|nA3!i1q`41b^O0IeY@^EEZ6tWC-E=}UGQ1re=`E2j#aHVo2uBxhb zF6R)>qldqhdIIfB`G`n7n;tY5OkciEO`L)DqRa(vwt;`whX(C97$S5I*%!sbRVJ6G zD|}t$PoDv<463&dhYI4%#U}kQU%YFKT6%x@zAkyQM<}zheQW%Nv+j$_ZxbVcULs{zPx6=tZya&>b7n-8)vGq; zT5$7}pe~3uh#kW6_e!7hS(zW~bS#H_uk( zJH_ZJ`q;=-!-qua@^hsu%|kH|;`yh2`O?4Ni*?S4Yr|Zt+we_NPr9!cPP|R6S1EE% zYC`0P)mhxcC~z2=^1wngkDFa?9k>5(`vP_7x2XmBu@8#udGuyeghsRW0f2=1%^m69 zjE>7eJ@5z2cRtQx)@_hKR`1t6@_rgR~@@KcY z%_(jJNdB@kE{Chr>h$ne(h};|jKB9vTMO zRrJ^2IdM%5xRS-#(MFUg>`ir5fYI~vcuZ3Y7OB+75#?LPc&tF2Fp?_DwKFrbLjc_( zso5y{MdR-7^XFP4GSB95RPWpgeUW%9FKyosFwa8Fk&6D08~XqhgzcI;_~i{&&mRYV z{b^tuf@{s#c&>(1(cl2`RiI)f5{hq(e)dlHQ}Ph(*Vyi@s&5UmPs~_<>!F{HzerHu zWvl1@@l;XzlT&`UIMe3h`)0=7&nHv+N#^4BIAOCtk1sS*7SM=9zPF}ji5)8WcTijGBIBI|0bVy`gT^)q4v<|b)PxGXyyPqmZy^>J zYeWE6QGWk=ax!|8VC;bv3U3q{U&Vqg7xw9mS5+8uD5zb@5DCKPf-nRts(O_?`1GRo zAYf$sz42S)N08s58s#fX;^g|S6a`@gri8CV#>F~j&AVrj4XcOp!8Y3@xyqx04b1TB`n1ts5m6hkmo7#M;d82zVaGvMC zrJ@pB-~AOg>pi0E`Xk6~opow&XK6aT7zDLRI#0N|eg};<8MRAzdN^610=E3D(;P?; zUMW@Owtcea{rKD=M$)T5BK7ZeQoxG`f^({Y_WezckHmcmtez`ni z;%(kTyYxhYfmO(;F>(SIBy2?3asI@0uDh1m#x6X&k;>!Ui1gGZoX5ASH1u^Ztt+pK=(mnFZ(`k>dh ziQo8|wq^VwCA`=C{QbSlDt|s*yYd8;fpBWBDap!0t}rid=K&~gJa)*tbub6LL-_j# z;`5KoQAMTt8{u(YU)Vz@YyFU&nkTJI32|)n-s;8X?f`S|u+Azgcx8ad?QnZ@dIw4C zW(a%164%n3k%UlMx$GR z!e~}iN(Bwj3J7>@X{S*&XMjTZo^BSpbN7) zq8K{-f1DQRqaJ{kUTuTm*&@`l1nW@iAKJl(%mBP!wnX$zdC`5FE!oHlFkE!Q@F`I# zO*{UXZd%}uI;X!FI*p`+fBSMl1`RwszJI>X+Kh`&_*Lbt>;IsoB?x>;v$0hfdkiOv zvErax5mkD6CNpnca`07DY>V~cX>`q-`P%b3CC1O%X=CI55DBi;pT1UBc%YNnlaz^9}ZFH5VKX%?-q3PQj*wzq#T@1)&$}m ziX9$8oO!q zdNMvfpa3jPc8r-8ZW~Gc){m*nC>lPOCG53wU38Dh5VEYt89drqN@OKA#Rq|;w%Hdg zLsTHHH6kzBM%(;ebr*Hpe!yu0~&Hn z+(+8O+j^G3FT00c(s1pL~3saV#tJ$bifQy0qY>F^x_ zN;(Qu7694H7}*vvWuGihtX&_q0KoXBO#C*$hu~3QypYnCsWtDvJlffj?@%X|-y#C% zA2u$Zu+AE2LfwC!JBGThRToA{yRP3qMJ|bi?2Fu*?_q>|e3{WsF6TZky4k#c3wek2 zObNNlh9ZhjVXk+aYnXR;*HhZRo$9_~b!WSFKHm>j0Kv}izo&x|-*O!mKJ{8LIt03> z`6(kS2Z%B@LY1POMB%Ob=f&YzD@AT|at^oP#7*5Ju|BAS5miF-&f0|r8QkR3rbPMV zsh{^7F}v7Ld#5?aOg-tj%#Qy~IL|Ji1UMc)vwP+wC)8`od)kyU5WJlaIOaY;>QT+!4ZEALlONssN92qeI{wXc#!xw73x~OCL!TKZ!H=Yura%3)@b373% zJ`SSeg^gO^8Q+Ds~vS+nkxKlgt^*uQ!(b zO4Eea#aGC3k>$a6wwn_f<4(;(eK`oLe_sDWv6?uZ-R4TLy+r=pqFq1n*u>}83md<6 zS*N+@dII>_m7dZ9as~HB*BRd`i{`_}i&f>Jd&f?Pzk_#>6MD*a8HiAK^A8gapG24Q zCzUU3CJMV=%X+W-$Zuw5Y-1}$_j8RJ3rPK{tkQ!+mS{u#$6(dI1IENBZH^kHRBRz> zd4Y$U$s^d}hR9#@uF9vUUpCA#dUY(u+us(J`4uj%(j4hXpO)z4pL{Yrd$6)D;bQ<9sWpydaFw{+Y4@|$PFzbbY(>_JiTJ7jQ`p{yWs zb(SN1Y;Z&~_NuUwm&3*FpY=dq+0NM7DqY)ERP?5N$9b|4LkC@xIjN#AmhR_hQOW!E z3)1P@Rq^SnmM@_PYmCy1?Hz&BFoS$q*I2XnxV+S}qrXDvKvLhz zm%y`^eeY+&f@e3Z@LhGe-{6$4ekKxh*zXE@bOQ*iG@=i>bOL6*&WbXjsI-dC;dI?|Y&P35xC1KR$#q_u~1c8#^k>H_)7`Lh+#f=ppIb9?o?nI!Z;+ z6c6Mieah^1^a2a~A+Sl6mvM+{>FQ&RG379@WP(n2iA4?L>bU%pOEt;6P zo5{cFO!cOJ%wA|sJC3&;ukHRohVUMpObB0mK7Xi31hinQXrLWOPwRtw#ES?S+bsaH z(r}XNDZ&?jSnr1*Jl;N0#7#}xUCZd>&lb?rI*d8tFGC6m`zI<3W2Kxd^1rF%8+OJH z8+V9}@S$Z1pWIvopyBB|EDs91=?oNvpXk?v!j}AyHfF4bt5DR;85aPR3{sKBU-cp#3b8$(R)@wq>SH_}2dU<$=G%f@XW6{_EH#iUzGEEQV))VEc8*T25=MNdp<;LORP9!Gyel3O|k-JILwsMh$nRNVb_-QP1iEzZJat>7;KXY>H z6SN+&VL&--KF4pFCL>ku$l=nBQrO2j*En)8xNlmu@}>%RM^UA9cqb)flrF@YqaB!T zLVdO)hEi~;(J&5_%f8L1x{-7m2|^QySA<0jZ9D?e5=ZREP`pTF*M+`&xCxjo&Kqd5 zJL)OyzqO(XD%>f~qhZ*(Va@OXxdF?u-By|+D+qCGJ^Int+kWbpeKf8u;to;9zgWs( zO}MtW@l9;p^B&Aqr_~^}ZFhWM8vBlQyS5Knf215R;_u)Tr*l8vp8R8P63cA7&n>uSsKywcYAB{P%ZfwA|4Lg6$8!k>ZqR4*^M1v)?I+ zXH(mG@&UVbKH;xw)s^d`C3j9mgY24rpZq+#5fk@gSaX>y)y8Rd_1U(3tF&)s zdOv?Or+j)+o4q5mzvtsRRc=VGKGK}0dCJJl^NiUA*0S5ORH8Vhd8jIG!@VYauVbR4c4Rm-B4S^^o;>(u7O`Ed z%80x?3s@FX`dAtHel(D;`KbNq`265|S0D}j{>VQgvfRky0g%xY0if<%`hA2Y<+3cM zzuY>#nfQs&daUy#uhzq-?0R7*5vGVtFn_teun>7Ct~Li?!d4ypf}(J) zf%|jQQ|*o0KU9UW)zCNiPOzd6OQBfHB*KG1G@dMQQ%qoXEZ1IL95-It_qYOVb-}ms zHiy$rs(gxPZ!J;DmoRxXGr7UOgQBvYn`(A4Kgx*a6&Ds{p;O6ocdwN<$zf=MaIp6Wbb>Z%= z=KFay&V(-3ru2s=KdWcU%rsAbx=?@OGyR-|)_)r3}b!s@cxf){J;G=DC9 zsW-5P-_qInlthZqEBFo1Z{H-Lw^9ipreej1c2~w|7kHXcB|7gh)e@a#{rrwQO;q== zOnu+@^II9i?uU*;qRqKW5-s*f6<^yIfV3`f!~Ko8uNRXSY>!;13iU~nqzG**yjqWmdcg5KjpSLbzYGnGTUHrbN5jG+3{ZfH{4k|X1bweU+ezez?)3x&&_GR zP=uY#5??B`KG|slG*!qiElq&b#GNR7_c2d+lz;5M?^Kc@(9*M8*aNd;+a%*F2YYvl zdO5-_SSt1HDM!2F5PqZB6&3TWLs1-T z<*T%KM9@H|VIBzw`+zp7+#zU;u6lb6(NK{PxfRj@4?QU6g5^S0?i3z7o0q`VsB8hV zq6gf!@5B{~RYe1V0r0_iJw1;c`x{`%1BIU2iQ&p5!92>|EC&}6h-Kjw6(2|F+f8Da;xzhYSRdfAoH3gh! zCT8hiD$JI0x65IhL>c)ZaPeA+gv3Ao8Nl2#ob>FO0tujNubSbk&DK{W)?G~zmJCXI zbVW^XtsCA_PvpGuSelcY!kQvi8C^#r+KxgR?l^nq&Jn@fF{8U&Dx@8Z30MEWdnyI| zM^B~goOhi=$r;fcy_kEt88m-8Dq+(GmkW;K1a;jy^)`B#EY8&X0PCToA}#@z*0TwU zxMcPLS(Y<9O}RQEJ?yuFH|lB85J3o%l%rFz<0ixVBM_}8F+DL$+{%EJu=X{Sawzux zr4zB-)?%ge%NR=H0s&1ceK`j9qK*t|A~39tus=4VmTK}<-|-Nv!KJCssK<<#@mn*` z(bcc1`!cUoZk!eJKzZYRsK}pi@sm&MFObBj*_a3}nTx*o3V~cnXo&YFiyhl%07bnh zyo;@lyd+EoA{=PeQAZ$-r6mqZX&Fo{(YIiq@E-@aL}`+^=dNCQekBJHk>py5sEECg znH)iBN2_tT@nN5GobQrd!6u$-VJ=EFUx%N6D;>QM+sn3Q;3I;L5TOAmAe&p@ERk(} zxRQ$JFGrn|ud>R@oUlW;gM*Qdzb0OAk46mSuHXW($iP>a$swzxo*@W}UDn{hTL z`qmV3MGFUEg_?XYBv=^pW~rSs@-{Up@>HlueR3q&Kx4tV02G<&_6j-1D>6M4~z(Uh&V46eu0lU z?~G}ye!IS@B1KRBStgMe|FoI7*pl4`PVsR2{He6NYN-t`lRZeAJ+)RQL3aQ+813{3VK56RvOThni@W9 zXr6AQVCr#qScid81)D;hI@jI4&Y5eR_DZL>?#*Gkx1Yh|5a&-Sq^@G*#FT{hK>xqG zLJ2#DPPnB-*s?!<0dTx?vo9$%G9?s)>2C1uZBTzZH~TIjNKd1Yey@+qQ>=apiY5zM znT6c`_8G4lD5{+5X~AA;?Ic>fl(pSprx4%XP$aGkb@<>>=W!g;dKF1G@!_Q|)rZ_!6U-YN?D zZ_%D}#esGFo=$NYckaQxcCc1f#TF!<|tP@fP+DW{QU$%jJMW-kz*!BD-?&1W~s}D|Z1l zn(DjW0eQoOtcsCvGHGh~Hm7!m4qC&At$!$aT^MRN>-~{QUK+IdM`ObVyMnskStKBU z+r2^_N3I(}BJ%nnl#Lyvl)*KNi?DqQXCLc^D+c*&l9cD$MuU@ceG0ED(Z3lTM?NsG z9R-|8aJxcFm`I>?w9OrT$t+)2YWqeEXyh{3ox9FxFkYf!=X{JEb2tjtsQgTTiN7RA z6h_k%XHA8Ld;%t`y>R^msE7b0pI?y5nM}nhf^w>poR=`eGir+1 z`i3(;y;Dq@nHE5}Yw>mJBdXS43GE+t4?ez;A~I`uhC!t;l@2kbQuTBh%P3x44NE7ZPFAz;u=NMXOfQ zOgzM_2~& zi1K2&DI+;HK_(>Q#F$aW4GFX_O{x0ZC@59a>c5?Wdi%=)x~c21UB(Lb8b;V6EoeTj zhypc4+G_hs)Qf9U~Gw=A9QxNDwHqq$@!M&BUVSdJr{Ws;p zw?-)O1OAuiN3#?#W!ARkHN|%M-!&P&n3}}OExTKKLMHzXm!O=oBh$4DgX!PhP3u+5 z?X``J%7n_1uy1XVvIq}Y=>$O9j*O@aEMT?HYXPmnc5-}7IE zDVf_|BjqPcO$7c$%8sM~HII98$-^%)+j+0Ic$Ix)IcB^+t-^84A(68!^tT}xfFjup zMzniyw}6oxQ9VE753hIk20;rWh|A#PD+#3{6u!X}@!`i2u~a}a^FJJ4O8=AL%T_S?M}9#~ z1F6ez0OzhOkKi)XLnJh>nxjIb6=RrPJ)#CT5I&(Dl=ugiz;}vYT_iH!+#z_%zDTst z$8I20vyLF3^S|hmI`eg4uU@Imgy?-AiFk zMCbS7;p}JvpW4Vo6>sarmTie%(z?-?7JCC=I}G8#G9=fs4u`GIx$8B2V{ib9#ijuE z=Zqs-p9t2^RLWIJc;uuz0F_C+3Lh60c|m`?CMq!ve1(mfMTS$ES0ijxT~le9Y}6rl z@NRWx9AzCAu*1)=Z0e67>Mxa}t0J^VU2DhSRpRnVA3)Lv z^kPOONa4vPstg>(>gv=>*c3$^b0tIi{c@3G&BqFB-?VyIsGx<&2e13J!fdOQR7|KH zj1{Trgca*RAwJHd%v~k0DT6e@z|AIS1Ivq|(!y?AGcb_}XOf1U+fczu5$54GmPpTU zmrHr{HbT?L`|ITbxv_mM0g6LE1OrN3i&IKUsXep%BPaVkN(ZM+%eX8;w=Cy*~e5@D*C%7`441$fD~#FjQy5EU9C%D7TQ zdL#Uv6eaMR_^id{i-8KnfLdH(Tb$JB2RHL9A`Z-MObPxMy0Q?c`v!vK8*li_m+X< z1XvS$3~RNE(N6T|G*W>8h1(e!}oTceGT+#PSGstKQz`i;}^>al-?T*vhGU6qd zv8p3yV@B_zDLLhLYlN)&ZQj&+^pw@E6oP9=M4Q~Yw9gS@T|!Zm^kjShSxzQWrK_+P z=S68{M+nL$mI*qUCXxlQU~^>b0x)HqK9V3WWMqnhPE66+5TgD`_l--WhLxR1Pr@@6 z1-HrwjXM#_n<{oyvi+hyvuRa0NmxiniI$ZZm-hYt7N?i((>U`Hbus_h*bJP&Z?}xF zcNqKU;JDY9OOTAz%Pq;h&9OLeKVthU5h|Jo#?j9W)Tlk)4+?pBSfrUIN_bMk z(P(w?bnQM=gzX=moR_KE2eF6gX{7_qYAi$4!@PO~0vqW(f*$-=Iy9*xG@}KAp6E3V zixNtzT)fl?d*u^=1%zD9#pR=!f3(X;;{Vnz!$X5W!ST_xfG4bKm#7aq0LjpVdAaT^ z26N&1@Gs@ES}3IRtMOmMq~q@qVN&0!L$ZecrXB;EpYM-M)_cuiNP4YQzC#!)C}&b) zxLcmD|Gg{6%7^}W|CLb<2KlWb0?Z(^)gC2w$1-eLHG{3KkN^d0-QwG;;gZL^I|LXm z5K#_P1llkZ)BloG^m6neY+2I_@W=Qb%?2u9yCV4x{2*}3WYmzoS0mLn?Iz=Ml>Kwy z|MH>MgM`EiU;*%}Szj+i$kF16kWVDNr5k zsNlV|K0vkYo$7W^Kq<#3?NzQvFTU?{yrpKeWl%b&`dH#w&1QxZ+~^_R)vP9$>)&p? ze-#BN@BoP9CZym%yD;#|bl75KJz?gYB0vz}kXg_}&^F?eX$27nyM3D0 z1`!oBB6(Y@pw{E~wqJgPM<}OKZw$`}lb|o94#8-b6Zg17Rd*SjP1*sHAFretot!Ru z+Fz;vD5NfiIwJY(t=(~vrY1N>EJBgKXp|082hi;=as75g=F&ZJ0y*oP%DB;czIhKn zikR4UxFFxfXcx8~`NL^!|Hts~Uz?qSn`po_)dxV^Ll~BElL|PHMs+~qbxwjHOu9O_ z(CGVfMV7j%BfvP?ZWH_MUtR#rPY8p?91_HmOjR{LyNwf#cZzoWrE9#=HeTzq2hq)7 z*GvdPrdruEO%QF3l`Lzw+D{y3&iU`Lh{eNLS6l&iv&hURK-)`_n43e$cUQIqmUmS$ z(uavhbLDmh1Vfj%d5f}6J0;gGo|PjR~s zWS#rtsj2TWm>(8Ju5wsk0Gr=u&CE|@1g?@%`q%OH&!P6`ow5gMz!e6#%IRM}+n2)C ze;WHF(PfgZLM+wp2P_A0N;3G-75a500db-teH+oM9~sw%My)dH-`@iCdUcW>K1GH` zf=Xz^(+bZA&7ou;`&*-OCRKStM%TM)iT%_r&-re9{!hO9gXqAV|oL6iSw~xsBx4qU(zrtnn zm_JQb)!>@F{(QrUXT{h1N-M&8M*hABR_@B@@X|*=Ae@m1J~A#kb@`%4gpUXbi_7kp zCUUD^BV>N2*7#-MJNxkF!Ex)x>0;=Td}^?c8qHzFoYVW(X@9EVJoSa27mJaKr`-wR z0jlrjcJXNt?%|~u)bK=6@O^?=#Lbx7y3m%sm(esljj#P;r;y!RBC@<@J}$N)wDU@y zn?boUf?}u>64x3XBJX#T&ozx!cvo#l1+wtyNjg?t#^9TsuUsy;OrE4dn6rLWm+ufP zZns`pbxr-~FpBB;-z#(?^0#53Vr^?!_puk`oy|eEy!f-DUQ^(ksn4bMIBL}I!jkCX~39t`p+Qn!~rG@Ifr6e)hj2T@3eloHtD z;QwPY@+g+m~_eDnis%gO(h^;O%P)tAg#_+w|&v+I~nqmw_Z17ICrB9pC*FCW%>bQ00%)qh{fn<70vlmK# z!oHf&JE8pgVZ_k_{Xk&%P=jb%SBVB?e!G1XNhVcS+~b?DWfh_`fdB8X>J)u=ifmX z*cPMt1j2_inD(^7f`ov$uX4U)ZK)m#Kkr|`<^@9H!^+3nxwgE1UMn}T``2bN9B;=V zdmc!bgn6y34*UdcN2;d%Io}^sQT?Z<+NdWtsz@+A!}(>t8PWW*dB%z1<|=Zdt)K{U z_}e#hy%G-(V#)X388KX0z^=vX$yits$;$Q^K-_GzWY%t#jCR%SsXyVbmsj8B(+!dq zqm|$I^jSY-n`d95i*tjfATL?Ob4_!5)5#5&=2@HZC+kbe$sJNE!~W#83*9TyF4J>M zaUr_IP~2FlptRf}TaKs>x8@FL0ItrT%v3a4WRlPrrgvv~H>BrYn7Cq8f`!gu=WI!C zj7T>AK+bc!J{|m7-!0Y#KN;8c7S`zdiU6ry*cWI0=oV=bb>jb83!3c46>m<)u@C0M zumCl(=aV?1@$;K&+n&KfM11Vz5K$tIR#{GUt4lgrPrSg>vtx&3?Q3ONhLqsg4^d^2^7ge zNTmud6LU%m8h~s&4X7EQ79kTg-8CblT6aRC{hj?3WS6nLw}1zFO*ptFbHzl^H{E7C zqd*v@IIL;;g+>C1`szb;zdZR9^`*QQ6jBpNBdqe|k^0KMUY-voN&Z>0>x9>NwPy=) z&~)maw~zJzTBtTmN3Ra+TwCbr92w&2K%DjKYjq3z16M}wuU_aanrCv8;G=rpz^~%rnJ?6e;!Z z!z>{u8BAUS(dn$3o&4cA3D+ZuPPr*A+o+ozX5RCDa$OF!00CFXn7PA%@RoT~vtyQFl$$1waG62TD<#CIq? zy@`M29~!zdJvB8Y1~QWPPmkA#9n(g-IXviTBI57s{Zd0({`T)U(;zz5;pBz=VBy!v zDcKBz`7mbwLu;A|=&;%{8u|u^`wFW4&fTi#z9GB$9$&Yo^ZgLr%_|^nCK$-zJ?4~k zZ=st4$oq<6a8Eb@`=?Hrf8oSM(iio!aW}O6$@@C(t{H>NNErxEa(KOa&3;8Gd$9mo zJ0=ibb9l@cGsz@s6w)tf>^Uo3yFIIK+t;V0t#o3IXEwiPu`=sa81piP?WWJm*K$aj zXi?*)%AbaXw^DWpKIs03Gq@2Vk5cS#b#-+=z5b>(<*3;7_wTHt@4K5}$#1BtkX|Dr z+Uc_xyh&$o^+RuVc{jJT%^pp+0S|UNPf?BA2ow$!zBHi*4aC4`YU4@ zt6ljy)NxMgdyp($rS6p6AnWCmfKQ5&qRJ!g=)_V(`ushkQNhJtG zeid!^45WUW-muelcY9WK1bT)gZd3xh{)clI(-AsJ0E=7%4D{vqCf^@l6|UE{tjPWE z$gkZ6q|N59AAd!DX|#m?75UX7oRc)%a@G8d7ACu?9-7}BYvi;WaM(FI0CVzL864ri zvvpTN%sQih(zOWHEn}|4r1V13Dd|t>7xIBwIdv(q{G^u(k2Sf@E?z5>*sX}Juu$I? zl{TTG;2EX&0X+g_;!SRliF!WJs~KB$TVawsCzSBum%bmCR{s8BhN$lgE@gQOkAXhr zFJG52hJOWrg>GASCnY7)ti}fsw*0!gM5GX%Tu?&vf3M30YT+rW5Qo50cSV$H^~Nd8*bYmH+PMU?6f6k`c)7 z^(XqPyI5N{$g6B-Vo$8EKSek`r>BEEli)o% z#GrS6Y6~xT0qI|L_j2w3mHsu;_kXB+&!{H1uk9B$2%=If;`^ zRel%~xPpm9ujdl$A{|@3%_vT9#?X5-h@UIBCrW~)v^A4-SsX%V6OKUt7LMX%S5I1g7~`P{tX?cr z>HxLX$qix$JG7Qpm@nr$j|O-hBz3s>gt!Ke>v^r0kPboheF>T5aPz}y?E6~%*9_O4 zO8pf5MgBFmv}&i;b?B6iekXdY9uXZEXGla7oCS{|tKmh3fwpkijD zI`ech>$Ec(30bjkqbV7Xp|(#g@{MzpH+(yGu1iThK82wiW8L6FPS%{?Lv3Z ztrI6)Dzuy#k2UExvl_jW5O4AB0DGra^D`VlW}=~oTI0v^DZn85M;q#s$1EZwdly*{ z6zqwj30Speh}!i#;%P?Mt$5UmG%A2zff4@Zmq3{~)xKZ7bNu~-PYeOePpqHiKZXBQ zE(IGF*Xjy2GJuqDp~R|F>mq}G%DK&@q!X9NryK$G9b!_}+aNv#Rnd4a*UPGYYl$s~ zH4ybHQwkBJZA-sL)faG)ggeD4b|({XTJNImZNK4BrDzp4MD`HAU?Nb*6C+$n#u%ywx&%|?heNDyS-Fnq^KFSbjf@*^Fbd;jO@n9 z+CJO3P)}KBqwK-#BOSnNVm*8 zf6emfwFTmjPH}xQp;%&*5j<8Oy#5;0=)~1ubpD^Qc=HOs_!v+46$LG?Dy-Yw=`{Zi z?cG&zo4i?F10C+MR!wP1-kX}fnyD{+4MHba;bj?9_9DV!su|>idQMOJ+SeF+yU@S` z_%Qn$?6z(Nm>e%QWo=gQW#1v`NETr!)Ym1`$tlOhQfmRPbJy^R$0Ai*q;=6lS3N z`DDLF65y$j37W@5y!8|a9*&LCV7;oTFR$Q49%NLu^ce%qe|&uH0_HxaRjCl+NN#>D=i5s% zY3gpyNtnapZ6YKIz-bf{Zt}FQaGFf_Ft%VUS!M&ZHw3&U5b!HF zH7Q6aJ&$RdzzUjllY-)Q4v5vtIP+fyehx<~6mj2_*8%6a>z_~;A19Q?!AF@&^d+@= zB7Q4mVPYlw8yJEo6kFhS!cY%#4d%q+AY}>(BYCpwh`AdcrZPraW4^?Fnw=WfRbe zb~2VKS`+W!va4vUPOB?Eg!97^JS{%uR$I#Sk{8a(33iDTucwKcEiIXEW9I43&?ln~ zXQWN?j=d=5Zjb=p~J1dP`aU-p-+r+dHb{ky-VGF8z-Et*g zaG1$STcUBxA2TYJMmQDg=2Bim_vdHamDSq!|5gOp2T*Jm8aJM+9P65VguGMYU$6-y2x|4K&Z*mH2vNq zPleR0$a5?iugS@hSKxsb@pNn74JC#PIYAg89B1r&G_BI3^(L)!)FDFghr>1ORR@7W zvP_iL#ZrFxk`yMV0AiMagqLxY#*-<6CFs7XmaDa#r35|uZ2+inX`ux3tlftq#JQ(2 zUm?tk+xZ>OK24aq*6_s$+52VtQNb?f*yDvo?`7>}s1MNBr}dr0DL;2B(z5%NHYlr`$g+Fk5f0ZL_f(ZEl@9d%agOEQGw(({jn} z&6%H>=|-z;b91(s{0`xB9C1}i*lUg4$|sF_4VN&oVsmmGIy$$1Ye@Pwg4gHqRu+Z0 zx{!>0wobg;md6l6F4rRCH&2~e;kJEREfRBy|DT`bSHdBOYCk(<7Zetj3t8zN57W2@ z@Ve{|C+&`&TexNBlV$A69aK%8dz(AO@1#y?<&%2~GrUT(pLkgp(u93kcg$)vVCL}l zymoLB7;`b9f$i94oCD9F6O&J&K6(yEo2OF)`rASrC&X@4GwyyHYsQK7db}SGuu-GySGV`-3sdn2h&kp--geh?LpR1g+ z&c~LcOe;ylcMDI3xVsPsj_rJHz7#?fSAmYsZN0aNO`$f zrW%j)BwgX%R_aMA<~igOArF{t_FyJKaKi6L{HuF}^MhEiq6%~iJc911f;a``kS|FA zzC@Fpm@8`F1%##v$ITGp6|;W&8GbW-$=rIQ__cUGO4wAwF#L}2H}7+u8f{~VJc@f# zyhy9m+*)@(M@2bg0DJmUURsCuxmPg#IX^yaNBQ<~R!_4*4DdvdcK8ZOVgh}jRnKjX zb-Db<*|S^&11DZhh3ptEwr%PETQtw&$gex_D4bq_WRymM%N^g@iWd{={?W+r*w%}N zR(H_YXK4qFY%aO-n2; zZs$jKTUJISU@-uoY9>1s{ZOSx1{bnQ1eo9r998<>5JKShwd|jwZ3VUK9t%dhU$9F` zFD_9Y0Roj}zMXnU?M|Xjw1V^pFN7~4ud&4+XciOk_po9Z4MC|U29&Q~L@;gV9WDmo zUDEUif37Y87^-#0i(PnkxS>3?xz@bLmHkf_Qc||k!th4LYBjQA`gwcspCj>}RYhqE zs=@xOT2P8`YN+@FDeKYoPF~F?9RQP!RDn5d%Fp4_i93FT*0HOy#U&SUsgGa}SDcz$ zm~Sd(V}`L)=97e_#z;xcW+{`qpZJR^ z`d0EPi^pB0g{y`kb;G+s#*V9WwK*9)I{=bh;y8=rj^DZWS;BDp7Uqpk837WbCo^4Do~m=jqBV)z^8()6QjX{uXP6~x~AWFX&x zcD+zvB*7cAZtOKVgw?6w#cqM6A%3yIG)cPY*jQY__^c85>K;@aVjHICPAbRfl3u(QC?C(XMf+0nZnwg zTMcNUzkxp|M#~%d6{BOUq+aN3Fv0bH-FG>*?$-NEni!A7)WuHi&te5T3{VV@=H%tOP1+We<+;#mPmH5{>&!Qxp|^&?pnd8|VK=Lh*>8Yx zrw)GfVeGMea^k01XRu@hlPI=-t26K=mM1u3FE!Bq#$+)%&Dz&-*u?DlV6LzX`eFjK zWN?R#>h$uznpNd^es1ZZ$|JKz8vV2@$(FnAWE-1vio^GbFqFT-Z3-J*Oh8iz4sjLQ zl8TXj$@5wFuy)pagIM#T5%Dy#l)b}+wU)RpsM}@h9fE*vgg#Ej>kivoEdRBrVVJ?$ z!MMUa?vVAP=Ud(c7*qd6YjTnkTew#(d{^PeHjpyXI7(Nm@G*$-Ufv3Y`*?XT6z`f- zhRj&@gkn)F1nSD|r&$M(`EHucx<`4zPG&#xQY5B}8+PG^6mr?b>m+XgUZ&VL04;5m zrwF+g;7Fbdzd!`wWSV#fR)`0gPmCFEJ&G$*C zV|+bEVqBYq7FK8&O4PgVW*TAexw6~k$F&GugkYMp_4RO8coPVkKroeMHoa^qCS=F0 zJ6MHpU$mI4)!03exg)mj!LxcE9u@k3RXW|!W0vhKbWdMzkB3K%PIHX)IX+Iwlo~p+ z{f>0*pxESL6*I8xB&YJz^kS|O+b>C^TL(OOw%TD@f_@1OXN8ny%z{AtQU`uT%8Mpu z5!D^0YrHscWh%dL$>Mn>qzq%LzSgtY<=V6I$PA8aEF+9 zFO7x~sW8)Y_6|%`(pZQ6C!^?w72CA;=Vb0V<2Sd@4tHw*u`=}zbdO)naiAdPpT=|e zr{G48#qQ4m`%nk_UCKIilTFVq7s^!;(1M@62RCSU!cXBms)waTo!*&V8ySVsCumL@ z2%tqFWm+)#nP~Iap>m7~{x#7t#pT9|ak+?k5Sw5;xmkK|rMZh=JO>7lm7c_gaEjUV z^y2XsQJ705hdKW-P)#w%lp9(Z@5Ti>J_8I z{m&tEgrp8P5al>%<@#7~6penjf(C);ZLCuct`q?S=@2*gw8cZ zVeb-t&bLgl2N5Y$#%C%u*Tf4QZADi@e3cHn8EXecqc!@~E^S!!Bcm#=A52{lx;p*& zUv_thW^h(yW9iISTH>%xfFT_yTk5@L+TN3U&hB=|=9W0M$R+65G=Pyyolj zI`VSzajJ2w3(Io>8Kt*hnz?7T!joKnyfm_GX|$$dX}qCfJG1m(;yIvSShYyC?A7#i zIie?LXU}%Pdvn;Dgqga1p*N-C3t|UIiT89%F^+|qu5voWqLaSAs;FjW<<{#1r<$?z z+O^+6h-u<;u;6XEhPjnGx9 zLq%L!SXFr(sLSLX^l~J5>vEE$bJE+n_4ZS|UBWeN;AEq9fN zFc-)q%GE&z6JaF|SdADB)r_!Htqr%=-{4d;C5c41SC}7Xu0e@Kux7v;YE0Qf2YC8C zGD4J(3Hi8G2%-ae@AFD4s` zAo*ws!@fkM5W4`~jI!0-=Ix8$usy1YMgfEIS1@71&9Hd2z#auS8Y?IJR*#g`VT^(z z^6kkNHqgeGe7hAQS9GG+fiQdn;)$CjeN@}1B`y$b25T>3~y_a zV-hks+gK;7SVhK;izvITQG6#~s-+MrK&=h4e79%gVd;;35uz|jgQJi!Ks8fy)lZ=U z6+TDXt}EndZ$?T3-xc2O;^Ps)%Of09IP;E=Lgv1n{iqSo^MCRD0pElxd;3QRR1_=v zxxy#WjOC^gWv4piFn{Y&U6Xnz@x&b9k!q}w5+dg8 zoO{4=1;wkjervNv=p%S)ZiRNh)3keu4Y$o}?ezxgTNzcSrr4YsUHOtD2qY{IlS4q< zF9kfYQ9X;D27#cNwH9!@7^-ox;{Ce`j~DD}gHXF&<(>n}%G?98D*8t1YZP@0I`HE=xsaLIq1^n)L{ zSq)wPlWhvwo*u*J5_~TgZawN@oPB{cmtiz2x}wb^bPhpOrD5+~wQKa%n4%y^S_(R2 zeaK1)%6z$<>hQuJO)_guu3VF6Zs_Rlv{jOcJ{p-Qd}(>yt#skCjx$fM#yAv2XrvxH z^bNW*ck8IMEK>M7PVwct;3#VQNimaWHx_>6U$kxjz4HyJcT|9-0-IV26b=cAq@R&cdIuR8oz({Gn`Js4u9(`9BsYSbHYX=&sHu;H-R+H9EC=j?AgK{@CR@?1{#QK%itJ z0r3-MA5YS^bIlAkzU?BJK5IpH$F~GD2cQo;4IAo9N2?Z>Uq8kT>>p8WefVz=$yir=p zhu=FLYrU35{dTjMD7V`&w0o`CEbvLn3BlFmug#|Ei^L~tS7Msqwce}@S)<|_U5jo8 zIJV%S+t?WOpxRc|@8J(yZw8>$KeMH7IFwS5s&y!jCu%!{3OEY=xRk|1iF161Yrseg zMobSa4E5)IACk%VJxS@5*#%ES@A_;gW3WBflS_Sf=%-&Qx1tCo-Ia$Ka&x2hr19U2 z<&Yae_EWJ!OBR{FwbtB* z6gLtsJq?Aq(otXd&@2=|h)py}@4cu|e)myfF70Fv^aXYs$E+sEUO>6F9c1>8I z4@&6PE`Xk8hI81HKDB=D{r7+=A;TO%-MurRBKI>LtX)EV2ts_=z{1SC_)wd2q z7N*FhZY7M8n8%!4)8gs_Ow>pU*`RI}=jJp|4BlYJWMctz`COo7CDfL~w(x>`wR=?g zvY%s43s0R;{s{NF(SCB!Tqg7Dt<$ii8A?+N2BJ@NlvJcsG3u4%#D*;XzLS7!Z9tbf zXVdEo=klJbMs}qeY@ad;T)oM2)d=aQghXhcE@fDaD)gk^CLjJ zH^)nF%y)4EHhHm!jQJb#@=Vw$i34K$W1P4Ot+sbevsONiS>I{T$Krl><0=b2Yy!R( zG9zpmhO7wue77;;hz~%X4_124R=o3)o9#jMJv1(T;|&z<*xY`^C=;a_u+}b>QMp!% zerX^I7bH_b&G(eRTEaL$EWnX;cgn;>T%)}A&PQYACgC!f9|uz|h#5tQDsWpV9Z*tA zk_@4q5rL;uI4Q{=EF;bceQ28AE9SKUP6P8@Z)g-uM+;%?oIOx9 zLSOJsuVRrT7$s*YiExsCN2q*T&zA>nuRHTr=J206(uLy)RUnVyKTNUJ7junt!Nwkr zgjY#g=)-ZykV(cRN!rMS&e5ToRIBc(gWqJwkxvji6yhK*g+UA-L|kX4j{LkHjdZ7LA_CwD@Kx$hh>5lx&$tbP!)28v)UX%ksBO6)fFTn z5PrrH?f}uP7IalUgC-Lzvv2Av1T70Da)A-+Lzb z?dM-<{nG%Yycvw1l8PGrK7i+%Rrv9GDsyFQ`M2xe3L@HEOkAH*I9DD{*-f(D{Z{bg zk~Fa`%MTUt`1WvTN&1cAye6-gU|Klmkc|0EhYUTgGyTF0$#q(_QbF8MKMC51ZNaGT z(T_B2Dzt+3h;}&w?ZOhOyC-}3Aq2}q7qgXxhxHPRs0S_1;d`Nv<(4^(F1@pXtLOD) zP=3ex*5jkl0AP3}xEqVbtA~xG-a?|+?L=j*Ft5f#32$3pZmoR=!KCZJR4Q6d2I=>L z_2)1V5n);WX+dB$*&R22OL^tUpDuC2jbCHmSLrVh-%DRv74Jx8o4>fDTYXC0&Q`WWQ7SXOboE%gf_vk70j3fz^rjBCY`+TCLP3z z36T-L6(ntZ$L^q*+<`+Kv7(@zR3?{hkkQ5F*f8XVUk@l!)BVO;57wBz=sAy?jyqph zKi;z$bKmb#^XE_3m-9t)$$6kM@@9Rvi9{r8&R%u)Adr(2i#`DwhjdT=EV~7KY5#{^ zu5`V@7*D#bAoOqBd$pkF*Z*oUaA$Zec4z0u^!C7sR26N&OEo=j10$B;!wrZ!lhSVV zvguW%On93} zzmk6IxgO5Ed8fb9t7YPwg6n~(6^u4r6_szePq z8`UW#Pu+|L?^De$I9^$)o#NnPC~x!g$CGY+dqg*Xl?1kQ^?yVnIhIJ66tKO zHMv(@m`kX0=!fv8aB~r$5k#uRTP`wg-l?%AXZ4J}pJNjzM#1xP_}s}8YbEPfWF$+B z(VloaaoKBKuWGwn`2h-%nrjC@cRWPI#1CM@v;#^n3hQdoT2QDue)5ZON^j51LD?m%h$ZAFr(U1tp{%Giv_IdstrgYp0|m<8ydjWCPr$DHkYt6y_H|B=I+$ z&f$1{&ShV}jYAPgAg519CeI7M;WZ)@7mA2vgPZcVE-o)ok6th{HV^O`lyDxS%@Gtp z@Q}6AlSuj?k)v|b9ke24N?J)k7kp&^I?Z6`IC*Z{Me=z@X2Nr?Cyc|>F^C_pEHTfcB|3f0T~qzcmZ4%tbB|FyWmR>;?TJ4(^USgCH<{A34p5a+s|JEh1g9PF^m#n#M)LOk zrbZWQ2KKX~-bFwUB>;5;|G=|pYt1K^_AOnh9qP`6%-WCz2{H9;QBVnjOnt&Z#~J0+ ztd;cvrv25dm@@t#0rjeL?lOM}&9VfsLuGBaz=&nMfI} zUd_M5z0ZVYdZVX~M-BK;7|K6lhjIOWE`f*j4p){Zs_pD4t7qu{_R_y4dnygAUM)A?mvLx`$wRj#+ z1;y}DO?QbexBk_iiRM+Mu3S$X{NDNl^3JcDfVZCY(rnLth~>7GQchH=@^p&&YiEYRd;dDj74f<;w1J-I{TMGT;Bor^Q|4Pe5`NvgUq% zi>c=)i`|n@)p^XS4Q=d{!Hl)2{H6U5hFtBirqaY<@cX4W(++Fq+!4F5q(e8Id@DYke}G532*Zvo@vgdPPr@V(g!g0QvokNcD>xR;tVxB`ga}Q^# z?QNW75*p1t!`Txe=Q5ngCo$VX)6tQF4aiN_#Y=l9iXn3@Qy+hP;06sq(y`!~6IWat*wn#>dWf^^M zj~o5YN~<*E&`1XDd6Uv}$c<8t|AH>Dj z=J%cw{+^khQg>Z7Vl7_63N1!IB479ZeT*qoTlShq(@ji_{_idG_fMoyZY80fPI}@e z-^TTQv0w7HtR_#C`e_F7cto6MCd+8jNhip|-;Lx12E}x?$?u$S(%IhHHQayhQL{M5 zIF;zrE4%X!8Rr2ahN(Wkpn(mr%sBha2I{m`8waqqwX*VTHxdO8 zN-TNZRWQ?DzJP~g#G4c4PiN|%pgWf01~Th5uS2pk*`IM76?DW-%l?9X3?zJVIG)?I zh}nyiVeY;VO%HcAvNYP>{sR#A8E(X)O2k3woEQGzJVC<0@&w|~ ziWc4KOH-p_q{nqLqk2te-T{`RrLm^h0>)MtTG_%UCYwGaf~2-sme6ZlAMEdJa^;%W zuAD^q^HY8w7m(2bWh~^p|A44~I%oCy!njR!=lBh(E)*=9*2W!$ul`XJR|vRcd2h$Q zAhH+omxtr45zMJBDAkV#Mu)k=EnfK})NofyAH9;c7!$h|*I^W2E?Bs{?M>(42O4_F z9c#<^|3B~t3#fyo!by{sb9=xjNK*m2jE{4I;=|yLv>tux#>AZ-yYsT$ji!S_RnuDQ zVp;cJV;mCDr_Z(0z@Y@B_*C>-Da_PjqPLyrERY40lv$Pi`B(X7oc&9@6%`ih+QGt7 ziq2~vt_-}(cMkce=6(0c5eN_v(T&S^K9;Q?ZHn)TbpMF~^VjV!g0vQDHsmB(bA2L8{bEcTq#cf=dikfZscsH$o;Vj9tM?%1G zVMpp(nc{e0ef3^LH>76si$?hJPmJ*s#Lui<|~xtDe4+gFK9{%;oIv$$bq zyCR2$IIP%JUP*ER)VHPpL?ske_DZVGBqGgX-_5$=jp!PE))M;qzO%FVyZn3#W`2qi z>15_hha1d19uvox@8DkL&`3L-;dIgag_a^=HIzed#59pc=)qD8F294?4eU?+GH+u@ zO-qAY&L>n6r@#pEM1f!|5ufg2@S&e7fs15PdsQl06odFIQ(lDA-;|Q1L~&5W!{>Hg z3jAru_Ir6hIIIUpPt1*kU6>s4?)cU8KOq@<;0Ay{hP%DJylS0KQS{jHk)4IdC7SP1bKEN8;KG*RYQl5Q zA(47mnk175h)IOG9o3N3Uf6UdtUT$J@H>6uwVf_fN+!_y7BvrDz>!2XEoY1k0c zr?$YT>_@fR8KK8w;RUFra>Jnl!=SPrGsCis@M^ney&Mv1CWqPoX?(m3yxtK+)Tn>| z06LMt!@u)wdGq9j_52O_&h+eB`Zwv}!i<6p6f^Gu3%z@HC3I!EdD3L=uZoRT#?#6{ z-WVP`BKgcW{> ztZwkfrI49yGU2jJJ*sx7Yn{_!e)f=)BPZUl4>LEDHbosDGhHY4)1rlRE5rv*T96UiPjuOsy(z!6!zEbe-jl;5gM56X z%W0W>6pMW(a;R!Hb2MUpTEpEl&HGdA1$SZ?O?mB0f!v1n8+%t)_VLPnW(@wmT*yl6 zuz&rg{!%IlMG(~84{g+d=cLvLt>0rUZibg+Aikw8NsVtlDZhQTV~#7r1|ecteeBGW z^moDdK4=f`z~Uq+*&nF7n+w}5We&pM9I!6_?^yse4~@N-vtL$1)|zw2o1*ta8C9`H ze@Tz4KURL36QK-yj&zT+x4F|k9T*gyfe1$7w5ng6*TtO)Qh~m77+n%Vmvq167_0FB zH#sEQKiztR1_=tfVkGiEsLNXFf1xgg^;_sT!irnjSX>4`^;#s*K$ifbPx{E|>pL`@ zbMv%Jq6vG{HBON1YL)RZwY?P(M&sqK7B1Gw|I58%fNK+?uy&%_}Z-QBy~RRa+kc)u6KPS$VIZHd}{k#_4&?hZ40}sPXu&EZf#Q zKesovIv-w#7noY#vvCg|uNt~@NB7J1v||-MsFeJX2>A=W`1YSWv3g~<(wpTH&+dwH z?$_&SxA?wY6gZUhF{H2lwooc$+AcHUmT-2FlIhvKfp(%Byvz;i*kQYqFTnz(P8ay> zOfmBS8fB669+LO?c8C9WpZ%kyTz{j8>!*JS<6>#nSJNz;QtKsO9k{x4>=>skgzcWW z{FA?xe6!25HcNFuopk#fX4?@mv8>@o2dUjJx zK6HYH1*PK^EJBKRL&c{g*jdM&;LphZwk%^MvpC8AW6F~1Dvy8HCa$*dbU&VS0^}mP z75MAklQxWhDS~b#poUnN;>RYHuf-~6y&wrfx?$RH0qXSm zwl(=#xGa-?eVOn)f+;%pdJA|I9yMU~Rv@-!pnh$0Sjdh%roKBZ_mR6YlM7y?n5THm zK5+M14<*;M?@d}^!#kV3mA$tatOsrtxZN6^5-PtD4Z zl?HxntgC_25%e=}m}5j9`h17S;lTd)K!zV>c(Ve|-o*`#N6x7%<-t)Ntb|;j2Ug|Y z9<#8uk>g#Pw=eTJN2tBZp=KY~U8(oC@lMnA=G-x`Rs3;qps_5dRkmi>qTbKk zaGq*DEampVQ_n(Z0JS|+?nb(2xckNY5c1M+ZJEahTp;}R8_wa_;69{PUHVQ^`R=3Z zOVx{o8V!c+wLiNISV_zW7;wRZ5hKIetGyAMnUJOS{oS17Op76Tbuf|{=$ z>hH_utbwmJ&+1M_X+58^Mzn54WzDSi<6$e;r?d&{g|{k**P+^*A$VU}$+TaxWOvO~ zx5gaTQ+QZ?$TPBEG`>BJ8h(}5Mxx!tT4&y#KdJX@iQbk#@vsvM_2 zuM(>mh)+YIu`;!gtItbqHvNx~Qx!`zl1{}vff5m&ref=+(T9nzF6MrqOu<`QpQfjW zV~a={8u>E}BQ5_+>2@CWaU{)DaAgZHoC8O%yR|wb4zwp?IcIkj_`F*x2m(HH7o{S$ zPb=12R!DZ+-Q|pc&^_r4y;MDJ_Z#Hcz1F*tr&vQ4d*_83hdsEMyj#?AT~ziEO(fR7 z_-;HsJ81n3*8r#Y#F^^2C9UE8DU)l!{06nIc4c=RQ>KC1OFk~FSN=*!dT6<3voC9C zC>XknSh_pDNjR>!>qPcI&PMK?W97n^y=s=p!$B+eyobxZ3N|HI3gJ9P1-rcsVK=R; zs_G(2&aRp<%qA9Fz4rS&gUc&w_aIBX=A`?$-Jh7UzE@VcO&isoJ{~^xGZ{;C%x+=1 z3u~D<54Rm=t_+ix>yayG@-u_?=9ezQb~wLS`ulC6pFm2)gq+WPA7uM@nNQ^8_EKTZ zve$qutM7Xy;<%;W16y}YL`T&U%Wjo=oT7(=@4~agK2P;q4W|`_Sqv9jqmYB~!!v4( zS*pg{`+@uQLEHPgxeNfe^e}1vYlGLYSzTQnrFondo>7#HKoN^H_eGuRkX;N2?l9HPPOAFj$zbOR<*Bd(WW&qywdx_cOX0X}3*{TUwkWC60tRGihQPdlLszJ({OXpijV&@&?x0)iRm~ zobg|eit2~1{sPLLQCx?l?l_5o%BU~Ay0g*r>s~BE_Tn!JEoxCBze!i9BlVUT(1x?j z;?LtPC$Z+{USJzq6R$ub+dn5Q=z{n~cVS_7mTF-9{k-m}r;@)^7Nq^vxcw8zZx+{;JN%~_mP#Bg5{7)Llj zI&HiM#(h1JE0MeQ!wuCnyBnmsV#vZU{KLNelFtCj-L1x{^~#c%+;=K9 zAbVG4&EWSn!bkGXaz#D}yX$ZJ&FE9uo&7+3!zUa1$IUVw61Zk}p<(+?*r6pCa)AwN zJ8fuDJ<`Mw(JnvKFSg=A3YjS#R?f}*8MHLix3^pb>-Vm7FhLHLs(ZnhSop>$qQ`E+ zZExZmvtMPe{=rank!?RJj8&KmD|;|lKH}Z*Aefc5RyCb$HQa+ooktQeusSuLkLSzw z{a$YR$WD;@hfdEdM=!})IqmW>;J z$!YVyg~j9lhTyqs_kS{pZWk!VXcewURc^YM89Gks!HG+ zpKZ@nOOot%(MZ55x=y}V)IDnWA9yb{Q{8Ci)^+D`^U1cRewG3t(}>0_zN0a~TqSJZ z@8zEdNXwx`W1ihQ$~nnMmEq#+zPoL~Rb4+QwdR*47)j*8P>tQABK4uex{=r-Ca=GZz797t+tXw&2*;`?g%{t^q@i_C0L_-8DADC@3@ez+ z>~Q#3ohm5r;<3mdh5KLonP251tqoZdRtpDXOO6NTZj!pj%#lq8X5lLp1y>$~32bmm zH|T8bj)*EeDwjJX&-N0V!EE(GMH;e_a?2$4<6FNx0QE&;7aB~F7MtaO`o*Cz{0i(# zE_BIo7+$-f#YhTe0z6H7kWu?qgU~_S)n<~(kGHVgE7PFqKF#1 z=Z3@IhOicm^A_^xE4vjz3(BAhoGr69Xr_4)j&S!GZip<~odS7#MsD|53YzS!_{^5+8(>8woiPN@o#P_|_ptY(B3nMMu zkX0@Jma*v)XaUj)N4eGoAa@$I-2Mh7trl=vv7A;!(WC$Hje(v2H8RIV?4?uiI7U|Q z+~4@d)8bDxQVWY7!(_WOw1KeF%WFSeip0Zu{U!JAdgZ05hh9x*&x!1Mod9bB7~6!B zhYp9kLBdj<>U16bZ6rR`W!Ehoz-h=bPm{Q@w~hTiP%3jof({gChZrD459b=Q6IGyf z3D+>I1X488@R}b-Ii0-KMQxfO$XjWmec%a^B%u#o5|QA;6O*SSGy{~vYUKi!94OOQ zy;)m72G@}`b-ZDVG@iu7>MxrwmdCll8cKyuAt?WDPg^=`X@}(FnO5F=qt?11nE{W^ z979?4jQxy-EKTn-LBr)@t_8K*u{KL<<6*&dygM|`f-?2sD%|86$O_wBE-POzuNh9h z4yq7?7c|C>%x8ChTKcvaRAI3XizEZ-55~tLCZC? zQlVBvE70kSf2u;ct~dH~Z1r`6x3r#DQIT0#gyHApbyG;9yS+K96lg0ru2js=tg@653` zeGU5mp6}H9e)YOH2WAWMy)4}qc~-OgG$qtZ4|rEK)Hrt*V)*}u4JBQ~cD}7>qnDWY zvIlSY#5rKh)Tq9G>`qal*tI;xt3t&@=`Q%*jRkuUNWxAV<);8MgRet5(ic_O2HvM?AU-NXiRk7wl1T*o^$}S|3DZvJ zZ6X>2TIuThh~rUqBjw&!d6s?!WhfStBO={+R@*t>c4f8~nF`@nU$+`( zgZMdul8e)a?|Q>0_ZLE7OMLNH30~zL%mHsjXEMF-_!R|r{^2Ua8rjs@XxicZbEm_C z;|o%8$jD65=95*EV$d31;D;vY|98haokamwox8K0-t&Lu;!M+MVK|sc7*+qmz~Gcd zr4@bOU6tV+6r=1?=F{?d>?@7^g3bQ8KQl%ns(v;6DD&=!T{cJG4D79GK>NL`m%D;? z@&KNo%n-lf8~Yc6H#1QTV%tE)dKatQoy+^|?KBBaoIAFVtxJ8)_99RmA$*x%AYcq0VNjZRNhi{zB<@2WGuBey4US zIc<1RWB=2RX1>@^Hs^lppfxGv@2+(n+f#aF71s1_=9W)n2!wmDzP!Af%gFGh-Sqfg zU+b`8#QqQyLPA+15p{r&vavLd=*s~$EM@;s@3YHA0M1QC`%|z1F-k4( z&Z0|Rbc07qCWn&=hJw%E)5yLik6GGH&{`o zX0>2($7ZNxiW$UM-20M&WKIpW?=d!Ug?yCnC11*aj@j|7)(AFgtO6-LZUP6avv_!X z(>Q^|ZhLQM;S?urawC2)Iq_?<@ht(rL;{$`2MbVkOsIEUleUkn>u6IhzG z3%O}Ht-3NhS_A!6Lk{p$`h@gwc)J2@1rxWjdmlM4T>mqZWeoQwMbga|G}g+4{2|=+ zZ*3pe{jBA!=0t`#d#M~$Mt3xz>Eo|~Xrxcvm1|EnllAHfP&3Bu9Bm&g)HnrcG>>V; z{GZnAyP!s)4%nA>w$P6~F@z9~`!jFPVW}Zmcj-shC-1pFQk4K}f7R)^e?lKD)9*Vs z*?K89?JP2rFtL1d*;9%Kd1_x>@IAWgr8f+6$AEgK zl&ob!*G%n8gMF*F8aa5G-;WX(M#{z1|Ha!||26%-{lg&2P+}<3F%T3Lq(;XEqF@j< zAl)E2ItBxgl9+&kv`DFR=LkWDFc{6~7~O->8{B(e_kDfd*L8n?|31!tV2{_%^N430 zhwhI2$71ZmL@3Q&M>aSzw?_92_gxo$nkG=@>V=>pJ#!@P9>PLeONC(Ib`D?LqSo3w>$5Vb z5QXG_G`EZ`FO98L`kaS*Gm3jNsA>)tDdFTx$uHp++YALt!EkV=uX}(0smTRO9=W9Z zj$N0gRG_BOVd{l$&O8ykOj7>&oT~%C_isNT3Tu5|7Xomr_AB(_b~hc8Z4N!9Aaaa1Sg+tswj!Fn+AjCa7E`?*O1P(`VH{iVqZof>4!E zHikj$x+qh70Y`w$!;notU{2nEg7)8hlKXJI8CBD~w*wUP^Y%b zkfDOJ28uDMM{dip1TmFa8K8u9NIDzXqwoqYh2TvG(+OYyhBfc=@0)e*FHet61D-4G zZ48tQ`R5lj8Lm4dn4E6UOuRYF(5E1&05j01*O!|1?@``Qv*EjQysk=v%4P%hCl5T& zS+^-_ndkI>hLvy|z@*zGHIGo&D|e7*E~TkLm)T-1 z)-3__?1pO}d!`))5w{5|e+lNg+Tm@>z`JLyyZ`sq{IqHxEwfxYsL~RBDBZT{{^@1` zrOv-JNY&{7=8Twv=af1>COV{2B744SrXJ}1qDsr@ntC?FIpW+~t`h3E2UtTjFV`%S zS8A#%)^GNmu0e;+iDQUgtw-b4>j&EV@r#2KCFk4wKYIodB@YZK`>iIhRW)P;VnVEO zwto{HxCYz*+ta=Ic~GbLtIRjPxd5?>L#pPI^yid_u)j|kGg2f#>dvHGujZw6)^#Nc zTWGXBD&sTa(hUk~C9Y8%`xHIV2|XpqEGlegP>8!R{~*0JN_9&PLB7-V?NHc9yppX> zSMnY?O@{VU_GqL|WWW1%HWroHFYD5redAtL!`AfFw8tV~!qXHtmOEB z;?Y0CkS`_87f*^6(G-)nqVG>P+GnRc9O0L-lhjXLwgYu#>2c!oo96Aawrri<-^W%k z^sRrOg!lx=(DGwPABFem3SgK{ph7l_UZAYx3(fgR>Qs`IKK&)(j=9~|9kmSw2hvLC z{GY_}C!V)&;<6HHxTs&+y+`YQ7c!&IrMeV)jRu)UOVJy`-EJb7!rQ(>bX_A8{NBg# z*|MO&t+Ia&2A7%dMrk?arg;2RXE+{Peg714_Lr4OGjn`_l_r1N5+La8X!V&xZ)lX? zDb?HErU4Dzo}tDen5wd3*PAmu2Ppgr@Skdtn3Opx=7YH-lQwd=q%({y1=sC__3zf% zQ|1fRq#u^9Qf}z_KUd&|Z#@3x{%#^%E&FZ8JF$je2i&Gv-q8ieQHsP(oV%4!D= z204^#CO*V6t2aWm;qZx}g&XDQOX(eH#Dj}3LG zSF(3E6=`WSASd1HD#G-ekl$ z^QAHBXh_r9e)}i^II4HEsB#edW1G9UIitaA+bLBxW72E#SthBm{uRxRZVd}IHQ-96 zZsXacZHWWIbw7XIdcfiJ>4D!2L2&%Ax$p4C>Gt9Omsu%;v*m&ZD}QZ~Twd=pJsJ$U z@P4CD^xgGmNAmBksihM@$zPjqN2r1=Ca+x!<%pK2dC-1tWxM2ZA>zh2J84egILR=( z@Aq$TslnLe0+Q0V@)`=>^&Uh&u>HD{TafBcY&x8D545dYzV~bU=W;-1{_Ag) zGyC%eHJ90W-?_fB44wjAXq%pnH~$F@0p&Jq)hQLlK&zLV=iN=Zg_FzfjMBk0VS$$? zt3}@Aq*@KRP7+b=eWWGk3FXHE;9?}9=6J-v7beaAR3MqIOv$SnDMrjB&OZttMS z=^e3xnc4ylOz{kh7-o*%BY5~%&N?5T(bibJ6Cc4~Z&tbTE;_uNOFXf5DY&qj1)uss zrICZU)7dvx!oi}@_^Lb3Tpgqiw;^AghzpE)%CJWzgml4+*gC;&J@9ns!K;3hEss{KkO1;AJVNLH)ve*AEl^i_A({Y0MP@xt!c0iyc%OGbmp-N780mssf z8^0qMn4a16m2(3m>J@7SM(Wb3NE+bjGdtgzj^Aw%f?Pn^4Ia@|PDJzmku@+rUR)n& zgBr%0?0i=l{J4pKw_rBOn|wuo$tcFnllxc@sFGNGGN79+IVr_^Fi$hs`5Ul1m0Xv- zCkO=QuNR}hQ=D?`Z`r*AP0Fpz$1Sn8xu}D&JI0=i!}jF*EFvh-bGy>9;I@-=zL7z! zW?k+!;LBG%3Cw606sr!Fk``{^1*8xb;VVf5ky*Y~sjeDm0RhacAeG_T@p9 zx27Om6CQ0Zzge+eyqYwh-IpCgI_nLMt$SXe`k&KcV;npUJx+6WrjP)XDj6OAaVa>S<^5t&s&=t};F>vdQJ%rv`N-C+YCrxYc}$9D9e1$9Mu_qf+G658s--sV_#Cvq8#KW&Vi9?Xb9VKf(*3GLO(V0v|YzYC0zcrkYJH4U_%NDK6f>uHc8~pIV%3+ zxcb*6NYFRcQ1Ma3Oq!Z>E$KxL^4{GiUEe-m({moPZGv#G4cuNf%`=ydKu*fDKP5#o zgvr{FY@6reBYeaT3Xe?*#vV75c4etQA6;#gb^A8Qn7aET+fKeBVCA=(cqq^97ea2; z-M%*d@f3_VmU{ zG?#8oMDd3c>R3TOnJX@sU$CMJ|DM#Bmuj({Gw0;xc|oimT1E4y-B2OhBV0&_GfjBj z=opipnS6k=M3Q)CimLtI!qXikQkJV$DQvfn+zk9>{n<(8!ZzI?@jj2r6FiTJdbR7^ zdt#yOW{L@UO)uXT07^){m5sM z+&`MSB=NVN=kC*Ndx7@w`n{hn-1nWT`k}J2MLRE=F-Bhn$Zj8m*nH;IojvR<{T>>#0|f6uOb?ZGp1_4o1`fK^T3 zxLEUHaH_Qa&k+$}SvP!CnQDh7leoR!@{5UUAcj}n>oU9bS7pM$`oskdN6pf)n&YqZ z>*H_gq&5viXxhv`^R1NGS?_D9N6Mp1-wCAMaf+s`UFcxTlG=-^CT)4JlxbU)l}$2d zKVY>KQq(GnN*5XWtXWcM{|-$zP4{GY8F@qu4EC;{PBC2xwR(cr5Bz*waK|JfW|G5< zewstzc6?(^FeRByCy1eDQ|-^E-GjpMr|tZcddHeM`qrEqwLApVx48FQQ5W^z8k%Iq zyU+RF5;(BQz2nR6-rSwvnkWZf%8F#M$0)_E6h#m>#U2&K4hg z7sf_O9-D*Q)2@z+J3+R~9e(`620FKVa(_;8NGH+y2puTY@{2uV@ItE1Q&~&SaXQj* zRyc60Ey(Dgj-J`>b`?RG--qs8!V2h^?sSBrGu=z4>JI=j&{A_|^$Rptqd+Vkoh@oI zE+$<@N`kh;1Sxzf;$FBgMDa)LHd35p0W(gklp)O{&TH83c=ckwB8=8L#D>|``4er= zg;wAFY5XI)=L7{v4ozIMO)Taj4fbHm!3yuU`$YS$GLVAD|CExKAXxqp;A zLmoY!ohFXy28eH>u(5=~8TG-=_nEBEwod-&E&>!n3fdRL^QP}PiA#2|S_Ta7|0LJ5 z!Nl zvZ590t__a^;fR&pvE!GwHi*hZL9&M?L%)#Fw&5DXD^IH&0++|Qv8Fa%0i6F-< z*vV#J2pUk0W7g%VAoNELL&2xtA@e(-ydO-;(F1RIApKVnZ!gI64l`Z8!B-bKRN({v zt-fa9fVFuCnjJdMyOA);&Jk8U9#mDD@JAgP6WB6qI3VsYBWh*VVqt5vI4Yr@LdmrE2+t-g!0<&HH`Z=xepU z?APikb8{}vD{6V~Zv9+d76W+Vpg^)x3ROt>uK zByU}puAg;F;5iiH-G)5`$A6HosKM>bBxpKJP)6O=o-V9f}SGf^l-`pu0V zJ6ho%Ino3IvnnEXq@&JUFQ)E~mxURvvnL0}%{BK@^SW&~Y7BF;@G5JIy4;p!_Sw{5 z2ksZhC_|o4Qwpvmp~~?#St3@s}dmm`QT5B^>$P|GVNk z;yx9`!Fv$s*tI6=qb-t z(r9MKenlqPv7Y3fl!R148?=@LWa06h@Z>1;L%zL}(fp()Vy=DbGaj&djX622R-IV2 z+Au?QuE!rJ4TLdr&p+83vB#ngo?sEvdwb|?g?LzA9lp3>ARI!>B2AgS)9+QljJ?iK zXjXPeGImQqwZ$rVmZBDrMKfQayLto3A5KGTA z5f_*SbfA>(E`~Z$MtvYw#*fR^hW2c~l}i+$Y4c+X*)9NZ;Oi<=W4da}hth{P2>Y@7 z(4k<-J)q3l=zLHBI7BAQZDc3_sI27XaVmx_cW)s z|I*`YgwP7cw$;JRGp+iyUj+kenxLL#mXvWvo;{QPizSNYugA^9dB6J*YX8W;3?M|A zYGod^qxXjAAB#Nce8%I2%UZ3)$42FQx>jmj2$f1$pWbDc@A*l+mEM9E>w~{K!f^;; zg5ynsv|IdJ3QOj1WFv!SrzE5t_gNe}vd|fjApHz-T&uclgW3LOG}Om~X#|&ITAC~j zfqe$6cdqJ1+w;I@XGrgR%GOk7u||FB9cZ9_7-Z9%*veo6-5TRDSQp2Md6U$}zr3Ci z>9tkA2)5r~_#GXaLucoryTWrYCp8Kw3r z#vZ&`ezUjqyAWu*5#ivtF zWn#UO7}>wix(gSg;XPB>4bi{N-KjgIOV!I3h1756Ck;Fvtv|c_9{V$pE^k+|p^>}( ze?GH7J^sT=QUwTq2H?{|oY*4R^sHTCZ!Uk3mwd3tg^q=9am1jXTv+_L9VvX4)H%{%6sO^YLScY({Z=%qWj7ze1Qvd#OcE( zIThlLpn{G9pQWT1lR)#mYW!*9fm^l^Uic*X%mz0 z_P6=mcGn0`n;L2KOMRST443GWRGmPLg6rRJBR@Ns_ zV)?C2yrSMrDOnNLej%Jk3YzLx|B-Cs}9KN58r|0?bnr@nNTO@Tm(gN+V0lC3s;$qE%NL`;XZ>@D}6 zh8bpC=X8Z zQHYOE*(EZz6EYRy^A2Y+i zGYBIt<2QWS(#_>gW0hC_J<0~GJ5RP#svHIo=h;vlxoIEE53yl#T-X)8keJihg_rhq zTw3_9^h{lq{h`|U25aXxF3scvk0nmq98W^}%vSrDa$BySOG`M-74Fv`#YAr_^Z|vQcz^ovpHA-J!SpE| z5r*QiACS-Y56l;^26x)8@XJQ>B`aEW#hC-byss#E%--DWS3hB+=d*}`5_Xs!!W0Re zE8-DIOp_q4m%Z_z9SwlZ#;rfCgS2z76w0>RKs&5Tb}=UwHvBJ(fy>XVEwr`f!Mac6 zQ)7ox8Dc-p@o=U}2$a#X(J*CDcg*)=h95>QUNr6GhY$G#;JYuZ&r}_oxxj54cWkqN ziak((R5UG|653tG7eaMytX3w$4%VFn`mE)1QAcI*a6axaJ(!p}(|9ZW6nbu(raP}o9IJ*eq!j8eYskme!1+&GXnFJZ`7g2$-%p2W1!W zaxU)fXiZz1NL2OCCodv%J6gQnuU%@nEjn#-MauDaWH68E(tPT&;;VG*+i>xX2qe_A z)#kT2r10i_tJ!FPi^I~T%N8(y22JBv(_5sQPSrzPvtw@@&t`Sc$7#K?IFxQTUg}uHI5=;8R zLc?uCt0>Xc636|)q-iEeaYKxccS{9;dt$Dl{*G~nfnEk^zI{rDKBXzpTguc3eD;<` z890-Z_~Ks>TH_a0&$=-5_^-zwsPWYvJD$7v(pjx1yYP#C6wnZ>Eeg&b0D$}?(qSV8 zt4*gz?_QTJ>c545wy9O#_LfHNemR%q^&DECcQt}=kyo*BMdLGs7}je)N)KTlO{nUT zHOxOeYxN)e=u{oWDs?|eK|JF_l7dVN>v8(I0v+0hp!LxW(}_w3quTpHdo+SD?3%5NLP2zKys zdf%gOaa)r%7T19W9TaYcwi8dKAo`voq7M}K+-R+wW%BHP>7{9Fa&SLG_g<7RuqkEs zbL8kW$hO?7DSLdq#7R}Dkq|d*Wlq>krDpr(4vTTYCj+(f>$m~wff43BR$a+up1SRs zzk)AC0V9q+9h=jX$uha8gB*?zZhT&72QP zm)bPX9s%t6(_shwn}U6N%lF~E1bg%R6K=O(^QWfEySO?GbI78TrnSuAxzG(pNY6`LI|^ zw7SFGhB3>wCz|&IQ*+_mreXU^CMU03gvyY9fSv8+IIS_t3yS$fa4_wpB+|_Y>a5Z5 zW9vUw;unMmhousJ*b-@9I=ijd9pElT3T*2>ORt{Sr*!yr8UTg1>_LEor)9O<*7FL1duSago=4(=gr@_)@JMX_W2Q{+8Bgze>5NtXDetC)nrB->X)4Pd zUa5=SgX>6$A}^7&ye#AW=C<6iw&wJA01a&IUG}x!S#CH2I_0G!_RH@F@o6UH}3&3ww+CmEUBx4VWP{rk7aP1oP)2sM+}p{ z%_Eb#rv72VdRo6{PPR@TddM#=fli92-bPl!Z&Zk??6S`W#RD%-$LS;2%f%}z`=Qn5 zLO6&x%PAYyA!qZWWAPoFWUwFFTziF@waw<%NVni2rtiZeE(qR>OJI5#2sCmoh-s zp)QyfdR>=q%sR<s7rAI- zV+(q6)0g_X?U(-M|5qg14!G#&WCe#a=+34QIInXW1bB{Y6MtE@nXMPJlwlkskIYtB zP_o1eI~MP_U6~23!*xL+5m#BC7muc>}lFoVtbBzU6d;T0Ob?$j&W|UqR0Ap{(4mI!V4q{u|Z)imN$te>79P zJDvNB3>svo5HsY}V8E8{x?CYr^QKc=H3e6$98NS`6lsudZfk2>-Nl|~66>%5Vifu?Q}q_#Ki%u zqRa0p?o^@Vt#KTnWb!qyl~0^W$r%9757W4PE`?Y4&5BQ>vV}?mr7T*lpVD-`8mR9S zFRlwMwt{hVk;5O-eOKril)2wW1CWJ>ri|UuEZDb&O`ip7RNLK$dr|a@1bE%2?fvR!+80X+;Q~U8(xe{D7bmHTI(6g7U=29(I!I zNlE^`^Ly4igc0l+~lWfT-v z(xd)ws1m))d``7!Y2Fy!(NlVKxYCj8{PETi$wLobS;wq7a6j;Z@9iC|Z?F3x%mr>a z?sqIZrgWGz6ps(Uz4@F0jZH1 zi81_|rdPn#9Zd5<4-$Srtxl{oN)WDv!+?q7n~6RaA49{5ttkT42jP=xa|AVUnqk=; zh6v7?@j2jcXtj(EpNgzm>(HrM7}JFksL$wb$Hmjyi$01kknX-ag#y=p(g=d7ZHSH! z4CQ{0Ux;i&p7k9k1)G6KzK6)=#oTpRIJqLrBHN)hJ1&pG@yi94Cf8h+v9;i&VQ7?c zB$&ftTUoIzrCW>-VvU>caRh_}x3sKBaisQXetXRR{S_2QDf{#G6j+R6Bx_dTACK>> zix>a*ECAmgPtwo%k*`NguF3!({wA8}&!+9Rjg*32ra+{nopam$WGW*4Zgb%J>dxTm z3E9zay~p1>b^5el>ikxj%9% z|6^ia-AUq&%Q$Q_{m5_aYTxlTS{!svQoI)=2*mP+;#p0Lj{D#XOb3KaStdljBp&TBP_!sxb8WR| z`e5~5oMgnJX!W#i^LY@m`#B0S)L=To@+g}E3%7h|RnYn@#Mm*}EMOHcTiE(s-nv6o zXVBn0RIM2^OZ>^hx$xHLo~Df`Ogf2Kp4JclQX*A%hZ(G}UX#&3OrZk5w5fOgSp{(_ z69f)i_zJ6Jssq0yAyt=^HdeVRlKT_v@qCLeV$F5<+Z_aU5(VR8Fp+HYbL z)>hy!c-V9}Rr?*TrzZt{(*u)3G|Q3hnN&)UPaI4V0vD4IH;YMP(QU4k%`_dGT5?yw z`LtiT{(!E`OFp8|T+O8DrZ@%gx-emNRXKABGFs*aiT~KGPE1$|Ljd zH<=oi78sf!(QTGdX-yaSIFH7m${%=Mn3hE4;fCv48fabMJ3H+fI^qxDyFbX>shm*E z+PY^%g^scV-A2w3a04(L`Q4qRi?LU0fP#En5WdRl%G0a|IF!;8OiXE|i5b)f`iWy!s$j1ZeYvZQLIFc>FbhXQe-#sZWn~$6p?=79X5%Y+iwx$N&undjzV6N z{O==zUAbB3@t;jM)x5CCe_Ss z_JrEtq&$VR&mNktJpR=~7KSe5%S@dXABVFP4vR!l_Si3B4tQqK;tuNnwiwEnL|POi zO*dn(vF$7~n(ToUAFYalMDVqVm=?@<39fd=XBz=_${7Hx?U1%Q0Pm8Qa%@Cl9ik>1_2c zF^clce%vHT+AZ;?Kxxs^zE>XDC%68juTBUlf{hQ*cZP&(+U)&`35;wD>I*Kx*J%i^ zswX>;N?!)7q0{=**5cYMfr$ou3R1R<0$uI(x5&&i#!j|8Hu!J0jET?p%F@y#GGgBe z@7q)fe1gmhB;4Ohy|hi+qT0!E#!6*i?3k)7XSHX`v%^f_rw>BD?HE7mGH3%wl(>|3 zR1hvjMZ*`WfX{oAIe&v7(UH7Oac$fz{t|_M4H=~V`j~NA^ieuu0EgN^Msjkqavh_D zVbJY1txl(x8Ihk6)M%ISd8c=dtyhO@KQnP{d*3MjRzC_mXTF`TSR7I2yGGt)A0{C? zYI**SrbQ(wY8F!gDd>|sVgxWR*tdDbRS((m?;lfnMa!6)>-p9+WfR`k#d-+r!Feer4n0wobg_ei9w`UEnE?EV#lsdCq1z&4^eolT*JuHEYCph@~p#sK1%R>XX}2eZMs$ zt#Vm(&s%P*!m3!0T)O#v*j5|YPF<#A_-F#iwGFm70eWVRV&u%YseCZ?) zNCy0U{>mz`C;y{TmSY~Hr2~ijenz2(S-y({!G?v z!$ssODqEdO!M1%LcOPLO|55}xPI-PI6@P-PD-@<`z6|bhU2g3q`kCVRoY^f$-*iT~ROB{V8xbdJ-t>Y@o zcdaF=k=kC|RZ}S-ai`-q8(&>nG}b6kwc~)EjhhZiZqm<|gDBG|EI49rq|XnlzWh<~ zRrQvm>-Aj>`EiAT)veZ)j^243-7d`qBye8!g!O5*X4bvy5CQye?iuomIyYX>7UwZP zY7`CnzM;D@uRmI=n5Yas=m5%|_SHxxw=#pcNp88E6lfjIuF%E;GC9X|pl(BHBU8<+ z?d)vSxJ#(3EAa`7I-yhIkD7kOpBKuJoxCQ3He3uhWzA_kHUkM z3Qr8!*F*a_oF-o_VM*WZ&Pm1|kHAx#Bti4v#^P_!4#VU!N-1g5kFf(CZ-Zc8iP!aa z`k%DM$JhKIQNQIc@}jWGyNG9?$nAq-ZDjl5dxY0Hd3CE{`^n+VxvDf8_~$TS)Ee{t z5InnWjYQer&NZIdm`Knr zbph7Q9qxC1!K~jL5C>NFJNO2{iE1vV8a8JgnW>JKCZc2cXe~^SG?5JHfsw$hnvAti z-C+ELUquIKUC~BVI9`KA=HdMpY&h)^hxd{~l7(MHz;`@4`x@-6*e;tWa7$#Q4gn!T zd*AfQwSJ61g%C8GX-BvwQ}9b&ox(q5VUzTVlHR5|sj8EcIEnswiB+(UHRFp}c5$}#-%k$(n z%H*-lX*4g#?HEM5#OhctBJ5@@p;_J4jS_x!w%``rFP5~>t4Yg*;MGMsj|=;yvwlMp zZ(zJq+-Cc>837nkP)Lq->c6e#+e@)#~kJk8(RO9f-j0XcC`LNUH zK)!QKv?7AUkvWFL6wg&L%Cbn69fh=0_|}412&_g(jm~z*GybA?+pXUWb&|H$9c@V2 zBQvML%RqCH4DD=N*<11@3u__|l3TvBM=>K9I+f>IOJyO-usE=#x{<}|VB@E((OWnz zPa68fU<7&Cr_AJXM{r$y$XS`3u2tX7w)HUf{zmOl9oCih(U>VJcR*`Fas6HLk>==f zs>ntKc6#-axR08$pmWrt>WxV5o6+swuZoYCWL6DFO;Sgct7V~?TcvGGPbZvfoM!!U zBf9z?oFAQl@(W6u6sHa=PIu9+OaXIQs|GM`#FeBw;Oy1v374nmCne`8TaEbM%HcPL zxgkuD+mNQcjbC+_R8sgT^{&U_7rnex=SGFCAokr(`fum?|Z{$bM^WY z2h*oBbzAjEnwCPFWv6_rrRu<^-8BY$kjrlat-`|P+AV3ClFrC8=PD|szBc>(}n_{1?Wybuz08_^sZui`qT=s?T^jOZKV`2^EEOpuZsQ`{lCCE z+xV=P0l%O2TseP1{l)fo)iWdGvWP)(6&KI6%f57<{-E>i--k!#EIYtI9M`8jrP1=1 zC^r{kyvMbBOP@%`bcF#ZIVh)koy~*?te>LJRK7AB0Dj*iRnj6Aq6zGUXl7y<;H!hm zU8!#!lH@;? z#aQ2qDQWyxpqKvo`{hENXF;}K>A*RSgzuJ8h$SZH8@R9>GH7urk6AI+OwJ*+(J zi(iP^JmqenJ`2a5tDiJ9pEp!G8(@);o@9r&vVAt8j)z0Z7C0nj5gWsGvM1ln}Bdd%kGNm0ut36oCGWCs^rDS__)B<50( z=y7I7u_ftcP1M$p$x7Pq*(DkYB+4u>sUic^EyROloOr&3LUd6#qGQ5z_4 z?`*A+!sG)rW3%neXT7GJYKR9l&4Nx<`y(hDU+F3L!>Ltu(8>AWCZP~zeg=y~vN|=8 z4_3Vq>kC9;K2#+=a6B_7s|pT~H%-v74VY!L&zZVd;1A^*yxaGm^GvTL+v;A3xNY{0 zP+-@~*;T*p8+pu2V)73n{9ozoSP)P+SknW{j4y=-vVGheL*ShqE~{X%e0GoN`=Z2K zKXzX(Eh2*-*CSrAUxd_%zK@k`G)}#^EEJH9kUX(sjHOm+8`Y=C2}ICxs7FpL-D0=g z!m3wFtz+)JI{h%}{pP?VdJsfkhx#12zZ^~P6E;+`jy0W>&(^`AhhiVU7n=G|Ur)Ef zA!GueEl|c8@x#^jZ$;%e(lve^=TrE=R7dzzejt8jX+=xaX;z@@=pnj=-NEBiQTJK!U}qJeOnc`{okwr=8RmoR8ybu$wdSqGawvw~y@khP9 z?C<86(1+o^FK3Q>z4Isp5`QgeHfZu2WZM_3yX}LX@IweNnvu>cp+36xB26MR$1d$U zN?V@~k1CIx8ScalYl_$3CU++_^Gxc;(zA&^63&FXjh_e6e3X7=dVVrGc)V=-X~bpL z<(Jj9K=r_(=bE{CO(fRU02up9Yc0LUu}RM($_L<@y*KV2up;k z;Yw@7)~A6vcq^L2>%(vgs?BNM(Na5aA|&SzSi~ zo4uAW9)5CJSdtMQeT&nW7f6u!AfZY1e1(u}klselLwZ zI^9dlHr>&J&2ic0rGG;fJ+w+r@7oOix_=Cnm}Rn0SK{bQJG2 zon3>{9+!NR40Sx5^43M{OhZ@Pghn2_ob^O^BY0wD6pHmJDio~$G>cwR&8icOpZq8D z{Nc%n=giikzB7ZQygWyh{GERcS;n!Z$6b1RuU(oXc^=y+Xi(TJA3yRsGL}{za)H-7 zSWJ4HYu4oE_|Si1K;PY+y&qKhLOSX%&fnHm=u>7DP_$gJFMTO6k+S$&I|orQ@YNXY z=c-UMTD;>7HJgINM}V$)OFamy2-3Ffh&2h~7#GMkWzI*PMMoO_!mn^E`+l_bx<|`N zi2TXW+^!i8-0K5DFfuqyh_&kcj--LI1WtWrs=$i^-sksDQW#$k+2`O}tpf6Le+ttZ zgR7*d7vdpb7w_?d%el5SqFoqlN}us+rUmM&TYLd$w*g~rxqM^HZ)!c#NHL5k$0+Zp z!HJ#ioIrm|g%EXT=k`AU@a}jG2kBQxIFT?%VZF+EE{Rv<#EelTtgUXVs7%35(_z*L zPVD0@&0khOztn*xFQ3D<&L54oA0qZ3*_uy15HByLpp zyXf>uVohEMP@(pf-1V9%KSRak5Rg`ZUPV{e zvT3rRg$l{w+d^*iSFoI!+st4=cHp)m4yMdOmnl;cZJ+?zV0hQ4Kc509AJ>U?Q9jtQ z=xY;Hw{5o2TG)AUPcw`$fPs)qk@#tz$cRZ|Xg(=7#gwE~5z_l(1bK8dSF{T|?p8>G zpVMT8+uA(4EyuYbpE=8M@@s$mnNVu;xNwZ172Jo&_Axn2MY&x|%hsx6bUN*mrAva* z`j@NCy}G6th67-t&p-A&=>KWYdwEPJ&7FQ4x+`$K7L32M-a!(Br%it zSd{}t0&G#8`cjGoLr&%yG0bE75_>y%b*pP8)tDJ;sx%)VI1s1Fm&|tW?jcj#vI?x? zcGHA^o33-Go@My_*1$gsx`AJ>3kDJm6K`GTnHfZFBqS}S8WJ{?EH>HXy8KQjW-{Is z>i7RyOqyiXGm1M~ayh$I6tjJ2FkgJe&-bn8{N-aCeHF97wk8l3bGj4Co_9K5Mm;GR zhm>hvk~2wQf|7Pfyk>`P>rOR)OnRTzOu9L2C^Sj3^lWE^H%aii6D5y-d38L503kbO z^(&U_xi09C)o?sQ;EBkgLGm<@UP?{u_7!CF1s*JQEUiy%06@O^=4S5yP$5zrpM<*- zNwi-n&-+R#DEJn`GQ4*)4Nq}QdL~5iea~kGB+`@|w0h|!IQRz?0BUCNMsFO)R~V@nybr|e8Fg(73gmUS40ER*cn z_k9@*V=#W7uKRmGzpn57eV*6z&$Il){NrUlpYuH5%ke&r1ZZglN zuDHd23YEN~T(4=lVlZB$O>&h6YQ#)zVm=ERy)V&iF1X)cw*K>x%l=(ENncm%IQ@4& z7mnH;zxC~}!ojbx1E(R@;=w8xow#w%9+|7J9EAt2Jq1UH)PP zN2FUN#y*_te3P*(QSXdEi4_zSpDlQ54uxvhGU!0+YXP)Agnxbm}lP9iXv-&2AO9rYG2-dRISt zkNP;MRXuI6a@Wn~cxIRYTP%G;ViY8IOEYXGF2foq-p$Bezwez0`umL&SE z*1)z4)m+(T9ais=x&h>vg~By&+HF)`b!`V#`T6(u@!_%Tg8DQ8%i*R83E>dp+3~4w zmv>-~;0+pE62f!yHt@QVggIJss}R3RSS6yPTPad0RI05w z6$MNGMW^l&jI7iHWz?5KoMhMMMeHn{0>xdpq)xiK-c{creF@K>p~*>~1YjHwxda#C z60~zz9@J+)EF{N7{@*Gz!@pH%pDETI;rvL;aU=v4lsF?jHJ1(Ce)uk``-n&5M?!^N zfV0?@y=Y?Zk7t740#R3MO*^ij!fiy|THi|4FY`&p2nY!AN0Ydu+EEX79iLwDzxK$G z@OIhS7uN^3Ki0+Xy z%Vmv^8Dl9juW;4SWokjDSD(A$v|XIJ6K6LGy4k3PAeo1anuiYR5NBR_5nk3os%Y(0 zD`_{;e8T!m{RRIVs=)Z`>nc+Rr1xD?Lw5@E_5acF151`y4|MR0qTl~ZFm`_ChJ99& zey+N*uV)Rn$KF?L-XXkSZ zMKmB;AzNA1w@8JH$#(?u?=QXO76j^QqjOWD*u*b+9v`2CgIPb#Pz8KO!UP)mC4JpR z%R~p3hcN9gpFPPlP2SFb8*Ehwri@RA2Wku-aWd92D*gNX0lKl9e^JJ_`1Vg*HF*ae z1U%un8=Qf0eIvyyIE(4NEYSVve5K>ENtlwLrND!yrgmAWsgF{6mSK`#v`-g!bPIJj zMZfl+%|B0~3A?hMF{@`!2IURE*A^c4_1o|!7)1k@9aQ>2gN1N5mK(JcZpmGH+_G|C z*Zb~B8}`Fd4DWmev*|bhDHlBa2krOK*(Bp*vpuMzdL#|D`b(@A|lh0T*~RC_84xiQuf#2`Qv)eCON*bcAOg@ zKF67I>^b(kY=z@P7#??O4xV3hb}qXowLBmS4_#kx&UGWsMP$u}OK(vDnU@Y`n#MwM zjp9g?v!9Z06D7q|065zIqb#WBSxB6y~|>qpnk^pqLmdycM}oIb-&$ zvcoevrzc0}=6viRTab;EaNXqd4i##_0*j-nb#dr;c#{CK9n=qexgdvF@b3Q|0aU(4 zSTWMRMOeqgjdnwO|M7`&&-IWtS@Pv)1FeT zpV7%ir(FPeP6@|SwOw7FH<7;Vc2bh(h#o<}F`VsE?fLV6b1Q*OK|Y(L!?NtaJf&QZ zVWYj^Q+(B~7`!F6-hL{_(=NYP#DG+34o#yzJ}M$RUT?`RE856fjR@%w191=|btUCC zZL&%As19y?8NPq^!YHQx+E0RnJVNmlpZ{s7|4NK0Anp2pTQNk`w%OFS*OU?T{iIOO z6Elev4o*L@O4)DMatRpFh5DTX8hS#nL>2U#Jg_zF4HiTH2_&N|uW-5|&`l@Q5X^Ue zQBNE&q1sBR@aSizcV5P7P<3sz11Bl^st50|4q5;(lp^w&B%mH8FVAvFx8zJ<1oMB| z6Lo7Lq+s2rvS0|lU0*M=3)#Hm(g*XY{1&!@(KB)wQo8A`#5b*V5r793}_v*s#KW^dr_j+uE$) z!R6<@zWUM)DX#yfj~ERS3dz4{Zu{6u+Z__szBcxzAoj?AOe28n;`Q`jw8 z+S&|G zW%?P1C(X`~*BNF2i#8gM)tq%G`5b8sk%u1_{u68A?hx9O*eixtNxAX%rdYPe>t`$% z?+DcJF;B%Sgv)3a^e$nOm4Z2SES;^X52h#|kej4X^mMM2(=F(11Hxnw+;mN+Ghw#s@eb`D_CmoJ(RwGHoK>RbNkVy25YJL9qzqeQtD|bp6)2fvu zxo2Qf$JB?t+0bLgK6R>RH%_B9jx}lS$XwN9(%Y4iPGxaU=_fH7DNBAm%m1bT{`#Hq z@NAL?3&f!)_~ldvOZ(Y}phsJ0(&iS};K}k5bEeY&p%j@pXVgdEIr-VidLo|xI>;&l zB8Mt!3CDx`#?9m-PAG1+v{%-lSf(E1a)l%h7VZI8+IQUhcT<=YO%E*GWb(hQ{Os z0d6O~Tuy5tW_1f!zA%Tj&wi3}5_n*LTurlq^)wiqQ5}AQ92w;otkl23HIJ6m;}v-$ z|Dhw3DPzH0NeQT(g-xphcj8v$hrEN7%F8QO{$*G5F9(NlUZY?Dgvw~~g9alC5{2&w z%z2X6uD(y}jqCodvBf+e_faz&!tZ4{&t9m>X8uE1{k==kxJVz38w@V8&*hGZWIU&} zEUP|W!4RDEF_=?q!j-jemNX+}369*(+cvt}m&y6k_N;Oee}JL8H@aG!RvlpO~qw36l{j~s{AB*v51?@dvoq}lTW`{~4BN^%ha7nS|>)XaVo z^}<*TEYwd#8Y3sFIhSE9p&4y)ye7uFp%db$R`C!7Qr+iUYRp;!($1}HOoXP=F(75V zYH0U%+dq@md8e>1+KlNb=Q<{^`(eQEliAIW%)jPv+V8NFC)dZ1#7OwsxFENfPH~D6 zKUO1>Jp#)4XPu=%mHy#q`U(^0iq}ltI(gGdOYf;C zzv2Y(TC}tW0X!{Wjw=OO3~)gM7oT^$;QY5dB8DTfQbtn&)c)mtm*+W|N!?eax2@WK zPQMBH@W|S@%Gh?1pgPRwD{z18Tpx#ZP}itdVc*NIPA9sSy3zA*GhvFhqwDjJ?}oc@ z*sd}O|3Ffl{H84jSrmf4H`gu3iO|SnCodPrrZ|DaS>+OdSuQ>l5f$)(3wz`=P>9vNMbnQyUB zmMyfhf5(!=0jboOpV{;O*tj~`H34`ed_L1B|Gw`Cx$y4hDMJLa|24+KgrX6^vs-1Z zu>&aSYM8u-fBa*26f%1_22Gt2c5o3GS19^0W#ej|lW6Ix=&<7bJWgZl|IiT-uB{IM zGJ>c2LG7%(%1)l`95#;{Q>=t7doPv12*S)3d&~}NA!!PV?fDa>ClqbMGa>wNYe8t> zKuHF%{Qnq{wX$*E`R0xB)Tj=Ej<@qms=M)6&A`U%V8c1w}10h!nmEz+=yP5edk zp&&2xlxSXS!{m?>i{$;eNrACud1#8BhZ0ET;8Y{L<&C_}Qbri^0Sh}1^b|#8vys*M z31n6U+1IR~WbZ5uF;*%TtHs{EeCqk&T|ic5@Ok%T2Pz2YiD!!LWQA8?02QFk|!lfFyp!7R4F`kcqX3&9(72k8KbIR6-$NhHyj+MLPXR-L;OrR%v zP$cbpjMe{`yn`oxwK%kgkT(^H&q!7vsh|aPQsMt1(9;Ao1K(N5fL40>^D-s~!G!2i zw=dv(uz0|#W{)&#&c^W`>Pb`Jfn!P%l(hS22^1LD$R6f`s%nT(m}@7Eqm$aQm{$^zgA#J^Eu=me zH<~Tw!^mufn+y6fp=C=s;bFA#Mbml>qQfcZ^Bh=_z5zK>U8N<7ev>T|%jUvSI3Bxz zG7Bq)M9N_b?VGja5OA9?E);%3#6u#l78SA`PLh(6GYTafsq6z2h~4%4rjXl&5{={P z@Z&-(=^HaN`e07H&})Xju;f=oVv-f|3RWak7WrLr=_hu8N7Vy;9DE$ zkE{qFo8qn`k3GrzJyhtw`#V&$zjb+D`Y4ta{eZ*wD64}{ieBgZRm?W`{JGBkZ%>XB zZVfDw`1xOaW+a7w0M0}6O{^}YC{|70f)_&`%4=D%yH;5SI8$`F1G;aIo6D zFd5bjmRASEkE<)hn#yY`j3U^5o-}?HiWk*siZ?cwPA?`3B3#j zO8Ap*LFcl(z+3g-uZA4n+CS@fA~1YXufF7i*D-Y2*;Hv-#R22d{;f#K@E0_<;{tqo zS-akDE@tmnN`KoMfBQqdKH2_(sWV37mAp5GEh*J9oiAiGl)x03TvCfEOnzjmoGYqL z{;q|%vFofKp9@MxBrESNK_O05f>%KJg^VQY2@(H7`>@XkB1#Tu%=flyrJQOk*3IQ! zw2*a+9mi5mU+4PgA9q4HgkD53zBov^=oZH41m%cQ;fZWyfkkj+`_ZJX60Rr4CVUQV zv>vERYOQ5S)Xjb*S|)aPA@7!sNK^<1G(V~_GRozo)A2yX%obo=#$qjwkVj+cmkiWZ z>{bcz_7?gR&7NHzcGiqwmuBw$<6AZAO0o(+uS$&TGqRKY+kO|P1Fl$E_kmtd2DV)r zbIs#)mcC{7oK@&AVsv)!i=?;+AP$!~G(cG-=ll=kLo#8Oh6U1n9KOd=pZd#b{v7AE z{(qJit=KuvHy@=>aCQa+4b%B!zFMfM@4*j1U=iM&K-+a9cA(Cuu&-}`{=giVa*;(- zyUto>?ff<3LmJ_#)QgCX`n`(&*JnlYN%XZqdh}5AGbZ^FN50M8Ml{E3XmGa6#B%4t zPeO`@elUkH1pCyEV4v&{9^*E|7kFE*VJ%abyN)GdJfn=w2WY{c1PNFGa?F+ytmnm-CzeWkT$vJEiu~Y=e~ob ztQBvvYRdkB{Cqj7rf|95Eh$dWlH6P}=$kgp#=2?tE{0vcbH@CM-S&;n_VFGwuz)&c zOT#vZ<2i7*Tu39EFWRP_du;ZtaWWquUWVp}Yaq`X`gssw73QSxl=-+!d0yYs2QpcW z=5lL46VOC9lev3qO#|&4^JYu>ese+7!%3wfP;EY>@EsnGWWlRU{o-2ZH=3;q+-SjD zA_ceD6B-stro&{)wD zPXrh5r&V#|>1%3X_Q}*Hz`?Ld{wqD=0d&M?PhI4rCY9-D@qat!z?PT)bPW@t#867f zs5LNU%8+t%k5P27oH&AVj)~EbXY1ORg}1eN7y@MzYtEGcJy2Qm<2EKUVKp_mRMe9# z9SG|S5JrfspmH1f&2$QzzG#4)eF6G21|sZN2-q#fDNBIwiT5ciVEN`9Ri|VG+0>m1 zBU55#re9D1$m^q09%t|t6FB*d`D=y32$@aTnPZv8iCYFbzCzA7XiQ z(9hm}{kr%BrE_Rfwcew`r;p?O)my)f`Tym#SYMqN#I~et#IC1 z^5fRfXP(y;<`39visWeMH2T>ihn_xF!WeP=%w{9G)tnP9mhF~Am;h%mX&nMJDBeNf z{&tQ!oRt^}J&4d?z2s0JXx3+05BPZImufT%RIvk)jLsZ`@4Xjyey-I&Ji~q!Mme{R z!Eb?yA5|=v>}A;ng!%O}*+c)t1M24NE#@trc8<=qtLbS&B7VZY;#(~%I7d}E4brvF zp#D&WqaZ)eyPw%Y%=s(pRU4i3rL7>cBHwI5mQhQQLX-=e*T624&J%HflBm6(t_r2TP3Q#ZJs}%U%1@Z4pSCV_ALz-lTiNFD+5>CXQ$=6gv<&BtxajW3 zf{pxHQ>VJK&ap*+RKtLpwuFsJuvb>WEDjwM+y7sXIcfP6(${GIw($2|H}1s6zB$I) zmfL^S=9aaQ1j_!;`PfYF?iR_@)$WRxYDyoWm|L?NMRYf+03cJ2k*bXZ$3U2KGSZA9 zY&a`)}3RwnD0w!r7XZ)pbu!6i(DCY&a7UMKds5|{g zYAyY8aX?`|K%?||v_=vkfnR{EwK^lE0>mau6akHbBKaqe-~RXbTXX=hM=lbkaD$M@ zVCa;*go76+{z+kSZ1@G(EmLD^&B;}6Z=KUGTv^HaoP?1=0CsxXZk$}kqYKS{qB0@k z+*I-y@bEY~_ZEmP7rBkLthpv*n37>?e0|MW*cEqs=P(^f{}f<*{XClb@u?+2ZXI7I zAl)ZHfXkrVzazJ2!#Td%qDR)xKYdQEKm`@Opp~GcN1&KB5&=9Lz}E}^7NacydmQmU zypXTVGpCR#hQfso%E@GVB{wz~9!?)_N#CPv1rs9negdGeb-?ZA2gE&Fn{7=* zeL!;aq@+B0Ykb*btF^s85z)aS7{;G0ug+>@ztYrKM7+e}q|Y3~$s#N%yKDx)3R1TF z0w#NxbBnxhT8vXQBqy&m+lS_VpuFXMLj6?o$JOB86DQX>xUE9pBt9?}{LJXYn z(S(d0^~@iH%RQ+VRjQSZ&GhI=3J?~gw8s{!@TgIq+$E^g_Zv;;N8$A<8OBv?{<2wf zY)4dY3As=92QmO3n zY}&&8cuWMNME%s5!`F`BK>!QRwIN&Ds=iybrQ7nnfv|H9ONj8~cQc!bxCtEqJXI*0 zug`S|-D(wpu(*ffi)fbbP|+^FaX-Zq%yg$Yo^)BY37F(=pTMu>FW2zpAbH&B5a+Et zq=RX_{QCg2K*nS<|FZ@}wwLZ{u?6v--kUPUr|~QyH?a`+?sclTvF2nrTP05fhY(=$ zXt!c<<~yK4bF#T&-3k_J^#3bl>ocbl8znY^0FIJ)p&OY0(X6;|vcR)5Y8*qn#GFH5LO=lXhp7{8F*GM{xR`#qQ@*f3qq z9dms?Qd&>hX+sXk7b{o=U$(D5%Z*$Y+jWGT=pvK1>6#!sr>{o}M-1qPxuo2r#sDD| zY~)&6XQU@4q}y}mcAdfdpQm1Q6mrFXV(HGUyuBq(a<&!^7qWFrYI=+vOwozb4nQR89}IV8DlAm%j9q1j zA>uolKUVK>%_}e=Wm#*Yp%2QK>^fQnt^ZA#(wkr3V$R>A^8}vfVU#89En*BkTQUYZJ;PU21i|Qd~MGckZRrbF> z+F#keKPhm zr~};qNH98YEXQ_JaR8&_c9Q6$-hI}_8YfC1LvI~-xcc-iugb0y_)Fw-22_hkHrZkPC z*cwwRroVCYdgT+0PWi~rPjoG@C(Fh)>XbtG6}M!G4t@HCLJPb?MJ?TGM_Q+b)Gz*@ znShI}iH8WyaYHmMO3ejd*>0lt-n+d3GRWw4S@|zmY*P3_WIOF=D!2Rclh;l)a+CeL z3lYf#2H`Xr@HL^ZWx~XrFcaLyD>^22iA3#pydCqR-zP3sMaCdVGn0@7wtmDJx$e%O z?H|8EkpJ~cl2MkL4|jF+BjjbZU0SBl#&>oFVC$o2Es@NeZ#)olUTFk*`9*PF^&9#k zAjx3Beyl3RbZE&$9qaAPclR?1i6}}0E8>%zQdnwU27I^C#4)+O9n4@+JKBdhChX_Q zvD46A&+ChBRSCk9>f}~R34u@8@lk*%6mR(yGY2Sdcp#%sD+9D;RJ8U5PnTv3)X~k_8#10q|Q1%mZBJ7ayCMS44C`(yn)M zOyDZ!qhdhH0e};s5PYjS2(K+!B2-2{%1-9s8PNv7=hxL6W+x|lUMOFZd2V#Q#?JUl z|9>WxjxWq()OApY0KrDEH*9}3OU5avPhf={Y~`nltm zlWs(Q>icOBXmOAxaAde?HF;LLe86|Y0IELw)!4-iqer$~`>rEPSZ~=WHCG<5^5`dHOB7VQyfUf&nhg5 z2%c1hr0f^j>r8)Xdtl-I*8)Drk#HYb&G)P=UJ@LsSw`!#7?9bbFDW;@Ox;hyr*T7h zv@6tPrjco=pg_L^8HV7;0M-Sg#Agio>%3pN2Tm*>^H=7)fkjmb-e)zDpE9iTqf^LqKIyOPEw zTvucT-XwwF!uC4oEQ(0;?!vAu2UKbe-Adptb+GlGTS+2+9)crf`(p|D<`)4sqkx)^ zBg&I+s{$5ZB5X<=Br-BMn<{^Q}a)2?~X)=lW` zGaC2sql|77B+D@A+@QoyY)kY>XS8p`xjo_xIB=NP*t97zgo~&3=JwH<^Qc5gi~TTf z|5=H{X={kW`GEHcW-nqmL3b)^i$9PC)3v`D9?)@K#dOT0J_l`?Wpt4?M(-G+obk@M z>`dGjF}GZvFR$vUK|EhrZ)jokTwwOynm+UHT}`1&nb8Xy{oX!#7Q$$EmJ~R(4v~YH znZ%KA^5!9`Fs^zkf~JbqbhMlw{xnL{-(7Y_IlGN@ZMRODEgGu8R@oX@8^B6iaPVbH z_-zgUy`=iO?@Va0bI@n$LpytGV}aDwC2w;k#&()ll$Wr#Uz^ijynhJU4L4_##{&07 z`!otsHfHq8+1lX>3t{;@X2R9+SDzBR!jw7oK7^3E?oi-3;HKgNujVqe! z=QXe~mC=T2phRfy-%~X#n&_D`IE+6^yOeU`>#{(rlS+p1rBGPwa`V$+U(V6UkhhKMJHztH! zS*uIOF_noO%FO5tO8qoendwj+4%rQI>38L#E;yrlN6bFy>u0DSBr+3A$x4Ez0_B?-(y z{kzGQ!B5hxgf|8U86w8`!lv#vRiBEfTAR(1ns=vbUDf!-ZYaQ#4NNz-;+cj$t#0Suq;)b%s2GjTWSE|GDUumETY4zg8Hh8k{3Y?+~xJ~%qp{pPtsVqJElu9`8&^eRF*HXEnX^c*R=?Jb!ssx zh64q#C`z4+J4UljrP0chlRcly;O-RQ|F#x5_iT|*u)y??$5n`+Vr0* z$*_X0`y@^-m0!b+)ogCb-Wo$h;G(n42c3TNi^TI`w_7}VJ0*QGs*4=IQT+l_1Hx6D)1}Li;cm!J+tZmEJUmeBi zsJBMOJN;To+A-aFi}3^YJ$9PbL3(LL;8R9tt;kR(kZRTsG0Y6J<*{b{LvQNCAabDJ z*#6?++!4Q*hVRlF{6`Vv_R$XbBOHql%;4CXn&~i$`@A`l8E3Y`6%>_{p#+Ka&+)aM$=46)sU8}dFrk43sRiaCF6 zWAi{}#t#+7U#k{<{kWY&>9ceX`9t|_8}jx}&G9pZ?>F&9j_R^?r_ZJ_Nl2$e0*ghBgndMfmi zkbk`Pf3?vcdBgd2iBFioNs|c~S6jC1iH@5k10++nfH(-Z#nVaw*WK>6OBz%+*^Sai zv#Qy7$ZFHp7_@>fkAG`;35D9e<-ELcJ^;VLg93Adl~y();&UDb($+2JJc5h^woEV> zoTi-qkJcZ*7TsHoDe&=*^x_yrX@2cXDfz&oJMau*jyxdEx#vy)JXrQCjU8%pue&65 zD;$u9D$JPzfSeVu*GxKlumgL)r?$A#_;hB|{)$gq;rWSyA)SV)!B*}ysBKxe`r^wu zr{FUdlKf{Kuy-xYYki`9sUamx9SL2h3s|d6sXxB|h*`?;piiI9sm1I@qvNXGuj*(i zRxHLX<(a!)b(6upe~viz`~=EiE9JBfzH*sUO0ouP*9IP2Akd_8{X^!X1!S7akL!)O zjiIhH+zn%pf^|Q0Yg_ku?o;y!ee$vQc6)J@<-!aC6hhSJQJrc!pC*Lm9)h0nRcHZi zDA$i+XzLSs2a8$*;^BCD)keIWHm8>62w?VnidB@Ha zs8;!5+NQE_cwnzLo6Xm#3-l%l#9_9BHfFN&bmnkHRF8I)=~wzpu(tI~P`EFv3TAh~ zy}o#!ge zs;daE5n)V|Om~R~yNx^gIF{k}H>Cfkyd}$oawPRuE$zg-NH?KX`k4eBPMxduKUt^) zzp&u&d@weBuB(4?F=LGf9MUrRb+DxWj0yV{)pW4;r+XYDAHf5d!JoH@qO@f8f)Q-` z71o^i2<6F_4@6Z3(|-0Q>Z{e5U2L~0SZthivL|_tb}?!-e(-Vbu*bQt z{O@4EC_7Odc*u}|BI$848pZ5eG0Debqg@Y=7mExdEzvZRQ2@g0@S69)^j}%#AMMCI zhR)AiRVH($CQsq2d!1cQ7zCun2>FDDOKH3Hrpsas)_h#w^D>enYRn^~^f-wY(ex?^ zbno=KP3_~n@YY?4gqm3zaQ^`ApooBJ+DMd^h)=Ihy!6iGEv}?FtqsT%v$fhSP#C{(a_WVB`1+iGu^W z^2b2Xb{p7Ao-rxD?!lIVmj(Tpc+Q<8VXMQc(9_6}x zwnEhxzxv}I!X0sQbLv1-x6G)FIDv`rz0;^4dGM}UCVg_#=k&(3E!@j(Yw=8=uD6N1 zf-70N){KqyZB9z>^Z3*Zj*uTqEZXjIC5fpneK_^ak&idL8$)CcUOT2&FZ8|a?WL&Rm4(C|qoqoOL?`|VXZ^`Rb^eU(0MLQDB{GNH zwC@fBo<8!x9kOC{Vfwd!R4-R$xYu3weQcHG1t?Z$=wMu`*^dV8ZXq~6x6}J1# zAj|mxIB{;>4!8SCeO0w6b8+eOSI%@i-PzWsr1X&?PG7i9oWDPQz*riy>n3L5|GhD8 z#&y~byV+JoeAVVf+%ug$Z+gx>h@0N&y`m1@Gi?maia1@ZRL z$x;3K67T;UT)FAsBJv`fm;mbxB_@DTdDWio&vpkIJ~oB69Cxw!2GjN-c4D+^`eCS$ z$ObantHODDv7}V`BX@yXMLHXZlB)40IxF0TY>kuMQay`S+7EkI`Fh;*hMs=GC5A1U z6!OUVbp`z*FUKHD=3?9+htzX+GEbT5;DBJPZ+f@{YImYZd16rVL$C7WgF1)fsw;Gl zS1&mebx{^838SLsZkyl}kstUO7UaX1^wL9a>g=7Fm}mbiTT4zc~VSD>uBHf-S$c7viX2zoK?PDBYS> zojmB(j}Y^N%N}=ecufwb^5y};_w*xV^DAF%P>&X_z@lF*NJ|Q}GKrQkgsIlOFx465 zNp-#G#fI;0XL#pw&w^EPbFzoQ$R*i8<&B-{E#CXz*-7!4p%W?!pITo{#DIxBXEm|% z70$hu<({2~jd#r<1!;qWcYP}!JLDNw@HoJ+b#&G~YxWpd^+a#?+VH864n3{qiEm^r zL#Pjf{*KpVWy8J>sOT`lgSE`graJ%bIDxple_s51$Fh#n-b0@{Ph9F+qH?fD{7x1Y z$ko2=Uuv30MsU@jks^b_xQXGt5+O=)=?+bKVt<$TmwW8Q3bzz`%T>GDu_>Z)Vg{ya zTeQ4=QZCipf#JpL^(Jspi7chJn)}d`w%Fb3STgjor=Qer@x=6s+F?)3?#9a;(SevB zqU&5&1!tBri&2n%Pfa41>3c)$9fOGo2kt2`lz}tV{vgy2Z9O@&%!lD-^$YVH{np>|qC;ro4mF;&xsbshGnDr;lKSeD%>89sN+DWK)kJMi*6arE zzOVw#-t^h};(E7L`XwlJN{nUzh{mx}TAA*#_wE$!be!KgA`{lJaL8QPVO2g&y(Sgd z8?3#U$$F0Tr7I7YuIJSwt>jF7h_M>niZ|Vt9Z57V#<<@%NE=?Bc6^1+TvFvQxvy>n z;|TOAAT!xl6k9X|3~;^;s_LSH3f;PqxnsgLo57p^JkzKZ&AL zpsFblEesZ4JNU_iC=tzBG~{r1?|VKhhC5W+S{lqxu*+jaR$lWzk;G`(l^A6)?78Nf z>pJQ5!h!{(Ko-}pbX629c-W*Xm@G6gvlg5K&3?GwTYlq{u@FQLQOsfxpi)U|mC6OW zE;!r7#v3Gu;J0Y<%|s9j-mr9`sj#c*pv{MU%Vpapwo~jsu`fJ47{I#@d#eI{cY3>- z_eU<-@fMnlNAFdA=&lhe-5f}!4G!V#)ArPHjjOQ{nWAtC<`LbBR z`I;+inJ37b6L258KkXQPp#+sjhYMm#Et}pg%kv5bQd>c)uTY!z2XXkbbgmsc!`x7b z&bUaV;)>Ov<1*d2S`A3saLoUoD|snHS!eE3IXYzbdLolupcOU5sywLG9@Glg{m4Fyh@p|NXRe zPna2Af9;mj?0t4i>sqDNrKRvM{f!?>sRrXR>YWaJ_J^xo=RFQH{I6{P@zvsAr&vON zd||ABB72MRz(c=WqOyMnpDKhM^{@K?-VQ_z4&h%RXPtajN5}GruD3p0Pdb;dxhu?c zft{6O(7okZg^e5Gn!e(jexdu?)yo+7gsQm@s=0i=#c)-Q4bzRy63SbYKXFWLvj{fj zj>27m`uC|V>Rg`xFlXO&pC$BJc0m&N%S;Edp#jEuU;V0y=6Q#mBfU!^ZKB%up`p~U zR2BZFiKcH4{N+@ix8Y&FaFs=4TEiKoCmY{YdkI2s$Z{MOfpA?9Li&v%DRYP`i8C-G`W1j&?hk+6%Ku zWTS|r3Gqtkzem&nK$aO!-mqHD>vb-G)RN<&<^k2hpG5Sjlx4I^<$$O;k;hQ~;_w|m zngES}K`DfZiB2_L9A)W09_?F*F0&Q{E@YHSw-r;Y#?F%RPV`Gu_9C+*EO#5_zRkU( zi#2}F(>F{M;>{{}IFm1}$gqw^cbQsR3GKEiQLOwf7(Msx)!c3BhY8XF|G@)>Q{7P~ z3#M95+q%`GjO`P&rI++2|84|>nizUOGsvexpD@>bL6+lt*N{JV{BhpD&>3rPxYFj% z;nD$Wtu~-vnoun2X?hN*vT`Uf{K^oJld@;doMmPp)HJSCnUnLY3c)p)-wx zlU^J~)wIGA!wToZ6){^`5WiuRSE8u77|(-1Yk(HBh>b9+9B<1^P|5uXcTDId2W&>; zF9>GyTHl6`rwb77ZKry5OQ=w6y&z39_i|jAp(7zBXIaSR2abi5XX3N>c({eN2koVb z=~B6cLbPzZ92C-Gd!ySg)O})~aU@K0cc2&N(>NZuzs_iMDYz0-{2y@tz=`V4qlh$# zz1`ztNS1x1-peuERKZhew-zt#bY)L*76y)VC*zFdQu&mH-dsuGh)3!wFSvqU#ly;k zLXcI%UUB>$J$`q*a*Pl~R9#qNyy?^0T$Py8fuRrIOlm72wlLo<^=;^?@%Ithxprd8 zcUy3r3n-Y}MU&)6JqYVRHFJGa#3f;fg@8?_09QT$Lyd(4=u6+oyaycoFW)AT<03k- z-^@5_vV{Hk+;~t#LxeB8l&-8}ec{^@1nnDvCt*c@?QjXh@nbiF=KOI{)VdIO~W`{3^+PssvsZ&^sn9Dnmw45^f`OlFQ zu#7*Bc7EPpkotb;c6pxxsnT-axo18&0`Z~rR)IqY;iMf zC>PS!>p!-CY=+!y5Rme5JNY0;`V$l9&6)am!E@#r>%E@>z`%v(#xPT#MVPZ!n!a#E zwu>dZL!}j6t8Ylwtc)J3=^s$$+RJV684mHQ`6$8GiSWS2DM9()PJw{}F~AQu+fi=e z{lCX;{|0}csX>C?O54yz3^4$xJ4b-rHl%duGdzaSko1x(JM&{?EqxjO+>Nv&PlWrS z-RWGoR;d_wJ^pA;yF}3;V&nJT&)_nCZR}%}Rx4WT{)}~=xT?7M%{5P<0&CM-+wh3J?qyZifB7A_r{4HA^Sd|F1dR$ z?34bi%iRhCjAlb`B$Q%aNAvv=LQ9xlAZIg{@eTwbGC0qCE-XEUv8MSMESrn9 z!DrhQx*XM&Mbj$O23va_+M@lkeUXy~sl;ZbqD4R9$^kdAy081ab2f=s)AY%4TAoJ6 zTrMhYz`fzMn^A3`I{wHZ{RjPu(9|!eZMTUU(F`z-U9D_51ODhO?udosGwzEO3552|kc zsavh$n2Q?bT2mRzPj-&sf`;GB_^4Jm?f!xg(KM6dwom0=P0BE6%`gf4zJcPv-E29j zUo|gd8}NqHYc|iP!?z{00fZF~%pW|hyyD)QnD&Ui^zorgFIZY@WJ9ctWhz#xmddNh zQN!@nbDI!H&PvP?u}xf8z#O~axwGSA=E%rHtxx|`YmfsQ|0C*a^Vk=Ce<5H<^CrMM z`esLsMKRA!m4@Bj%l2dErds|oW|MT+c1)S?N8H9kv7R4^!@E-x zG|Pu-#1~)>UGKm$x)c=Q(MmD|0B*(xsVR0MzO6%;s2;kv5&9yI>5A>I?V0!=`xRg=5!)>yBvvDyVPv2xiHi!{Ua|u zvLoX&+E@cUGsSV? zx`)CCx9$pmHT5i-@XfXPx=%G5T}))l7!DHGJHV4a>Q~a{Ji3}bk%2*Nd?#xnCmxZ} zKAK1Dy^Ax|^@(06CipR2I)kUQJ2akd;P5!60FNJU_#OY_Tmr>FG0>~8?TpnOE{^oC zRWt9eD6a^tsI6`%^l!uVSaDMbGO17ah&EW-r9#xac7I_zh(AYMD6aRfwh-?hGa()= z(E-am=1ot*6As?r0*Ou}pU5d}VwQ0(F9;t%2AKp5dnD__1Kh%pj-<`nD;OisE zoAR0GC!CC&IVUCtTQ4GO&|rCw`kAHFZwaPX_;LoUEd`fJi7J`4UO1D3u8 zi}up2F~kP!J?S)4>JgTRh8}$#B@6cf_wO?Y<(u;L3DeMQ%%wfM|9&2UhkVVw&hy9< z!8SlXa=59SAmzQ0pjVtuybez+cP3$Wif)IIAzmyI=T3G9be(@y@{t|6Bt)$(92Kaw zvTA5(iE>p9H8tq#j>P%;zr;iN?n9JKs7>dR+&_)Ui!GfkL;L!`z}~~&&H-h-_ZxVl zVIePaXT>~T>0|`BN5u+0x-+MPjNL0NY1JvvtEOYZt{{>8ln#P1&t79(9bDxPX=xLm zC7bPf(v7CEyZY~q{@Si;@2FGOq0#-oBhi&AK+;^s9R_7E2qP4*#}tZ%3pVY@ze6_ zW-um+=tVW?#ic8)$F%EH|4;-UD$3M^Ymi{BzkT(X&s4Jz`kBY|(Kc8m^EJ$1r3x;M z&i<5?53olb=7Exq;$|yc7P`8h8<*%>_Kn>{2MK$@l{wrUaE6uV8vO!?z%t@KA9LZa zp6;{}5>UX!m6#krE&QIh)uhm*E;Qrn5DF4nHob#KW)wu!5717O2V$+l##gXRtgQv*V{6D{4LcSu+LO9Oqo~L|l@ySDy%hbysaJo((fw1QQ-`6=1*Q`NtBkA2QxjMp~ zSpO~L?cmtzuK#Zuzd!r>fTnLAeN_NmT3JcHItUAjp6^NRxjU58JJ<6kzn~2;b^j0o zzB8lENbSAX=45rqA331=_Qnp68QM8X!!Fdxz?`EIrPIjDoaD>{mrZH}dSMn~_ zO_QNGcuqk0SHCKca#~uou8{LCuHczm>U_8uc49s=CGHs7O8tbB_fFlF$z!#9swysN z&1Q_&n%(tfGfd!0rcvRxhR-3U2dP@E_{Iw2ss7+;W;6{^yfI`M;Gtt8k zv~3>D6vbx%;SBBgl-#qsi|bAkKdf{$v&po{fSue&URHNAe@FPS_@^Bnp>9qpM<%?x zL^MqJu1J-DzRmA)R~{9b+-S#i&)mOLLt=55?|gos zQu=Rh?LAxP;uyTzq%{bFm-5H6PWI}~Ui-ynqGQzmwO2JFmONStL)e01{FcrObSrPz z*TfiOwq8IIgTnAr;%mv+=5m#Y8pm^L&bJRiaC>EdBhMEspe?(YGe$3r%TUo zb>OO!9hTnW%Oo}89yQKXEzA5KKTgy281;~Ggiiu)s;>4-jaPoWwu;k1q{aLCO?8ta z(8^6dNP@bN4yku-l*qRL&z@Hd%;qtdUOoBvhh6nQ>>40#6|4fq1gT6$ksA7PIMl1@ z$nMsWxud&uip37{a~?h79=7+|vmVyC>Ck1RS{|WxAi=5J4ewk*g|gzesd3Je7TO+_ z>h_*%T)^7F4J!EuL#=Gy=9`hR;g!cT8a-Hq){YO!Bh*~sqwuVZEPwp{G1qP7Hw=?^ z9xt&mW@~c_+hvoD8EFlZbp2sHL`3Of)iVO>#==X}cTF)8e&gq}6Rd%q#7!)3Msni# zgD(q3E40q2J%PN0n#XzR;isJ`SE~nn7Nql(iKvK3wE?SW-=9yIW6P1gSEO@*~l3H`F>aG9N(dbtr&g|59lhnwccHilq?-7w`l;IWq0?k z1KN8*_M$bK?#b+z_P)|*z2g4oatm!-wU#?+6A&DcU~D1NhW(BB_A{EPVz@D`Rd{yV z;pY2Jkj@8qpiQ{jhI?%UwjUM!QT%pn(m4SDcV;#d;WB89$Oyyb$NF=l=sJ62qQ`?keht$%idmj z!7I-4b^zKFI{UD4V~&OD(yQUWbhW4ORTT9GsQ*N7*b{k zSDI0W=3U#XA;@jowzvw}#knqvzU;q3tQ}Uhn^hd_+;+5a=Ld4Z z(!niFYYARO}wc{%Y~rsCnr&(r{t5UD@PfdWN?z z66a!##NeIRmCE>m(Dp3YzE0U6lPAm^lXP8dWr;;9cW56r*gS!BkF8L9G4Y((6nc01 zjiMY2In^o>ZHe5Cw>&@4*^)JpVQPQW%67Os>9I zoQAgW0S3`P#*fiYHYOOD*^>~ZhG#rV4SIRIwS{ss9@)5d_P0XR4~6r;O?UHYtIO|V z?l0_DM0?aM9~~{z>Yr#UGO)Q}k}Vk4qE4@dJlQ*~^=HPRz%X{;p+Fh(ZOaibJe_Dl zIu{t1G&%hgkG=<7wub;Oaq-g@Cqty|{6f<%FMj+lj>2ACsuXr|i%qn@-R!~63HzXPtoAUpMQ(8Hj4oc2hh?}VjxCOT` z-grMoa5G!hIr_ku;JLDOLvakU0=VP68H#zkk!H41>mo#52ei(W${uNE-JCnTZ2os{ zy2HktWPOzq){?e~eF;ykGXOh_(b(}B)3Ec|qjV7Uz}xv2!t+B#vbL=`4DC zvzVp+Dh4S*VQrhZADHQ{{hzC~O>e**o$2=WZc6TM15}VT-amb?ll*`AqLsYwnn-)~ zWaDbtu-2iQB?Qfw$xtJiuG`Fn0696jA>Yo19Z(T3f7tlAh4N$0nlU8rw!Q^I>g4ojYC8-*?a#0CT z_?%M%8K$49tm+C1iiVJ#ODRiqI)2hS`tL7VgppH|_%$a18KA7ZvXX%rPR@R;r_@#r z{jNdD&Z}-@uI4d2B9AAJO~(Vs)PR(AK=L~w9rSfe;)OE!7m(sZpJ2DR_}JWY?INbv zOZsX=L^!kMs&;_dln7o~)ozNBYna(O=&?A*bP%9u+O_zS6S4Y#;OHpYmADAk{;hBA z0yBR@IAm=AS7iF+TaV_eT*0QMm-si(SxQhm!!Ha;fGRZ1Hfl48v{N15@8&a;r7h9+ zbbVpHd-w?kB?HYSMle$5h&+EFEWhQH*Duyo;Dp69S{NFaxIc`-B$TJm1nlg+t&163 zE_R!${~psV?phlrPavemkUdlZ^_qe$-382N=#Q*I;!P8(*Wk@7 z(&tqFSK)kG8)T^Dln!hNW`+<7l`6t%i3U)EI_Lq=&i7xHzU~dbeR*4jowT7jrbR<= zvD#a;)IsvuNp#^#96c?|%d0d#Rr$WsReiU_zm>)w@K-ZKfruFQyji+pFua8TT$ijV zC>PXhJEG3t!q`2>1fqF0UGS*eem{Sj37W`Y=#}0qM71zsyo~2Ibcf4CrNctuWk>pv zKxnRv=HgKl8tsSO?asJj&OP0@4?kJrWrsMG95^DvenzL|*kP|pM+)2S5#^|LiXEAUhiF>4$8DRQKkJ;mn1Wlfp5lKWVHVQ`Y|l^EDGfKF76!@>o6ctlj+mW=*uLE1ay1lfNvD03=*616 z)PJQJTjS8dTVmnSW#(P;@6*PA=yC}BGFX`in04lPseSqb$wXx1-GnAt&y1L(b*bwc zvHx3EXA|*Vd7}gSj>mJi-QUL!IOv#~ZZvB%J*ih3QeBwa?A?Ho^0QvsIajee*H%WX ze5>9XF0%2KZk-n0(eNncGc#1aq+cH)l4{tF=MShpA^ZlW9+X-+g*e)OD!qEiC4W0~LTDd|{UgK`p^ZkT9F*}}I zJ$LziMW5JqOs!&r-3?79;Tb3vn&s7mLJLFV`8RE!CGwY+zC)q-&XPJ74S0?OIC%~IcX{j}Fc!&;TG?OTV{SDjV6 zc07!BZfecDea-PNj}$~QU6XxYG6io!THMV5lWQlUGbkB?Dmk;;lqz?wLf@;*v`Lct z3`+j8@NrV0M;^R_R4B057D6J{+(q2DwvXul)6)W4jz|;xZUms^(Pfw)ZYFd~fnmmf zcQbbu;G!t&Xj|SU#Fw<{H?i(HLkqbM?9+Y8^=b^T+Q@?sK`=&C1EUS>UxD*_7nAS^ zoLEBJ%rt~Tbr4}JXGN<82)7#;Ge<<9{9ALWPi}aHU)4HhdEU*RpJ01pL-=%{CB5-| zpHqqDHgu@^@oN2I2lRf4w;QqiMd}G%uU8%{aEU>=BK$ESH#g^&*-=!xk$SbOUiUtk zF>O*ppzYD_t^jfgqKwZ!t*3xk{)c9H?={)CK!$p}a!8R+0g?(PqRmYx>OA~dRA=i5i^R%=;m>0o zSdAr|kwG6KR>R%R@ey)ynCVv*d$Km)O-j##^IaNt0-4{B%U+yNFeooapJ_JiSLZN6#50 z`!bSM>ukN{4~*!HmL6EVn>Q$561u9*v~XA^YnYKEAdM0*^vedbZl?tZcEQHd)ek| zG9CItlxAkC+!e1ML7XGtv_h;Qt1OC)_H;Q7W;yi-?}4b$E<)Ls`zt=;o3gmmdndgw zI@q&)DUojB3(k-9IRWdP4tjIB!Gn**kI>pq6VDs<50dEXi|zxAqKa$Mrd@&DSL#%B z;jIg^?XwGZpqP;Pt&prK)UTcqfn3IG8UOoZP?Y2=QwSV`F_^~B7iA&P#_^Rd5DXR_ zba=>DlXufZkRh;&?8G;Zp|?``1cplo99UH!5Zd|%vMOlx+V*1NJ(du$>%S8J7;mzkS&U z{3b4aT@BBXug=orr8Yhu(dn!|do_caA_+a3{u(lPoDBG{k_PxrwMMTXeoJ%-v)g;H z)%hS-N|SAP($||8CLaG)`dkIp59CfO#ahE1}${r*{OA6Ml^vN%mCo zmiJw>)OMou=+&@$!7a$Z?UlM+M*6V@S0f5rZS0QD9z}16+#OZX`Lfw1`C0a{B*%jh zRSwQou3@q-pMxTn7&2P5*Td(q^Y-ad*9L^IX4Y%^SkdO1$K|;)eNh|}U2n_HocCGt zf*UmS#^y0URi^X1;P<{eNK1}$RR=(3Ol_tbhRI7_KxaqKA{)iDmmRijLk2<0dcrUS zfB$eguyk>YjH(9aRM&Gq&2Cjg%iB~HBkTKnm%Yx!QE`Hs6(M+*XTn`VWDVAS1~8&MK2M%U{oYL_DY`PY#OTDOfij;$U3UFpXRV4}bY)@X6 zW$L&&+n}h);=Xd4NAfOgQMrdja!q8tUVHE#U5j?We8NDD@RpRb z>Rrb>2R;rGE#0@~c^1g;Y95PnJgxFfK0tr&p5qsGUbmQDHeVn6r~|>=VcI>|i06`| zg*Nrv=q(EafDiS3lpyRo^!69#T-E#K@7*7oJOv1e6TuT0XwZo#1|%|b-?ZxPWaG?n zjsJBV|JQN+U&rx(9moH59RL6BIOZSi2M|+BMJpvI0tn(<>Ulr@yFfg>)H>LqQg5>i z2pwG;&@CD_bguG2g`RS;U0bcF{xho}jWljNcF5TNWk=x{KRrf`oJ;`5jS`laQ?Rn# zB0XP@W$LKCr@A^8p}&Hm0SLCb>pY;x1$~pgjE1Fbz2Q&uD;*O@Us=o>?b#5D?PPa} zifo_n``pZm)<(eS=>~wlx66bmsy#!HzTIvUWh0nz{Giea5SNMWC#+xFkNNNIQ-bLrmZ~ zbJbLc!=hVYgLkQB4q0GV^6P3&+>FN{o0aaQw;NYcoNjQ`(i=F{0T5M=La5`AeiF!u;F(tPer^PS{^f1gzUNU$01>zZ z$dh^2W%un%v&V$ZM9xUl8l9mCAdZ(*`EllZE*JSFZm>l8o*Br~({3T@HQATS{>(B{ zcUZSm`REsmj{^)?x>B0wgaocC_c&Sq!udUo&C*WQQ+t|D^oj`UWRNE^yw>q}AARdH z+f0=o^z2Kkjop=XQqzk3eq{0xXP1k*hn~}mWE*qVU}D2(@UwDf(3(KW=-W(e(JsH` z*1pECG}e&kOY*m@#7NH<*NvzO!O)==uz(4oE`kR}hT*@Gbry3-lfg5kOBNar10f{A zXz1$39#SuJdC3-gW-f)!a_lqFUztL`R^lH7K7Xa|LY{cQ#WLZMy<};YnY(>Fb~!Y- z?(J-0JH0;y4Uo@;0e=25u9b38d|sK#t*{TLBRcV|V;KVbO`w-$@fBh3;mwc(H+H(Q z%a>Uu(zZsLs{x>CRMO?cYY=16y+W3M)|c~KLDtg5i)JHeI3?0JTjHfh6+J%zpw#7^ zIfv&!?&rO^d-=I?&6r_UoqyC6|ErGaG!cle%y;m3Yp-hB#QrMc!GQJ8&CVcX(oaPE zn!BF0Uj1%MMpj{n{!ZmM2mN{TQpf8(IiI7`6O+TAra8L16|w3@;>gK~PI%Gh=5Z76 z7NWSwfVr!PyS?C@MQjk?Q9b!3G`OhaN)i1%T3{ZKLSL9lGQT zOs7jpKj_ZbT*9q=BlztuLpzln5sZR;r=g?_>e7L(`gm_jV?h5Zn3}tO_vL+8N3v~- z0*Y)`nYx=xQoYnX)Ic#)I>6})dK>xWS5I{sh~@m3)wn5iMWa`AW$p~$RF7-G3vOf* z+b@9KjosNus&lq#-G2d`r>~%Q>n7QRUrT^Kt2C+DRL;}xD^QAQp@Dlw{5Vus|Tq7-q==2f_LuB8tw%%SSLH&)KW@fkFGugUu z15(|*k|j~W7d{GqrOMeYexN^G#vPO6wmr2yLcN*rf7y)8Z2eH`Z}=}q)C~x(F7rNP z%LaE@tWfU+g$)ID)EwvIESe6hXe!0Kodiam;QJB`tSKUZnSo4`n_K;1{dnXr*TzNqMy zYocn3MlbaqELjwnElUokzzSAwyafR9$gU=q{}seZAUF-UUAk=RnC*JbnteHRi0?4w zkIAc2jeWs}z}HR6@o-f%PbMpW>fDBsoG{%`to$di^lm)2Tsy}ku%fugdb7k%6J8*q zB?i}*oc$|5xFJ81xi<8hg!j9Oj_HE}>Nk^WK$#x`t=n+KLQ#{mpdz`l0Oq%5wSY!{ z^(qg5kr8V?R|J#EOQ(+%Mn)y~yhlMBdnBnuY|4SJ2ePyKb{NSr3M#7&%|Y(AD9nP0 z_DKv9=D=T12E-cXqCaLA0s^sPDz}jFaHCV`T2l;4v_-}%u=17CYwJJoyg5!jEpP|N zip)aj;ac)z#a%G1{gbbE>84tlCM(0j%|wpEWtJh-Eyh zc_8gLgU2nq%BxFxY7OdH7LS}p`mF73+eTIzr6MAA-@9D*H`DGZ+W96VUDKJ~lsJ4y zZ9|b{<~h(m)U|`DF@Zv#(vD5eEw^!gjqQ{<5M}C$vqTvqrH1#k=HU%5o18V4YdT(> zICf@D6n=B*^9^<+fc#on`80SQ6f0sQxgXkMKhuucyT7e}YeTbDu&fKU+_5)DE2>h( z$81wy7lb1$w!{JX?p#6&MyQU00cPJOVrOH?&F($oYul% zFtSD1@{Mn8#f-l_`>puRKeXjH^_G+#$fp}%h;Gfh=Q-?25zTH9woH)6kY(RJFM5@} z076=B*keLQf}^mxuQgf*u&F3nmG`|ANDop)&a4?treNfv`p90YDJ;h*^Ma zy{n6&D9E;WL8w?W8Dw^|u&6=*ZPL79UwUVF?WRK%^V{3t%H&0=vcBh)^{cwHJg@LW z)IilD;5tzT!wMyOh^}o_x>EG_bw^jnfv&}5lkOPzF4+SnLzXJM>-9S3()~vEIr^P_ zwjf&!P|flwDZltMQOm9h{9AG2d~2_wxoKU7Zn45vAZixyLs#pX_GwIncE<1gYIh8zuSa-^Eh#3R`ml3tqA!}D7N(1qdLp=_gkAz_KQMKedm<+2{b^;2 z8dUG=1JMbn%MBn~+>Gz^`TY;&jjZ2sea2|@B{7|(d9x+obwiJMseci{^{`L$1D2qV z>Qu|SM*N)T(6loYx^~(zXfqwu9x-Cb06SaKn_FE9o?$n|SQ#KC9t@)6|ij(4NU~ z*1fB;{>a6w>NtNI-H<~ExJp`Dfp+K~+a#$PBBJCp5LnmM@hM|IoPKl`rVn0&XzjkZewt?eBG|O6fwL?`PK92spA6;9jj_qN&|9^<)8-w4|%*zcwn{LzMj#i1cM%Z zE6opzKUHF9L&5{)E{YJeFxCCIBk=J6?D&qT4OaK`$cd(UE4^L^v?qG!CeEe<`fuM@ zqawAE$?2O;!q(ROl1 zvSlBqXp{c!Dz+}By{0F!cQuBbaI8IJZR#@j90I_?>yBKcoROfcqycpa`?~y1t>*cu zdZE8lhFawYh@Ddg0>-Q3s&JCQ#;Cw=v|`rs9-j{6x#e?9w=f>5{SoArn6uA%yX}c< z&8&&AmJ>%MUD;_aVWUswjUil>+15|st6%?mPhX6vCZpk+gcFFQDzD221LF{;j;Sng zI*1}57ZCS}jue^>3x9rEpvof>2vg4i#H3(aFcNAwBL_FUUEW?fQ8raK&jjOUPYWCI z^lKA9J+aM`Gv=t$E~Sc0(XetU$+?RE{Y0m?tYM2|s%-qSjEL=$4H{#ZM=;0Dr~FW% z8wM)|dH$T86nR`t*pM&mJ+}bj36Z}F`;va@BS2X_ez9`R=uuB|@NZ7coY&8!ol@MT z(%AiNf~7AzoYI_=pF`G+YFJ;}oj^;xpD*_dU7n7BO(8?RluX{*arN8aBh1Sf`|acl z8>sWhjG-I$fGLj9|Ain9#KkWx>!>vC>xC*yzq>whq*wPO?`<4$Q8Cm>AY2*nEmM9o z)e28x7*MO|iC2i&h9X9cbZuL5;(hQ*vKMHKTfp47(@Wp}@F|E*9&_afC-x>IQ`yVvRTI*B=~ zo1^!;`M{64P~p9i8+<(CuJPQYKIzXHfDFwI{wEi6n{aI>U^)$5G+Rr4Py z*rTbcbKJ!SvA2jDZz)UqRu=zESW$LI2U|of)b#!+!JAuWZ2xDYBROd@bkht5ZKL}Y zJ?z4CjtyS>{{(cLel?HZn=6U1Y8OCL&096>LCFh^3Wq zwwsdeA~dDLE9pjX;TVgu(mfLIqkqAC0Cn&0uUqr`Zv6H=n(P)D?l{QsiVUB-ufm&A z-mBZvSTO%VXXOHiFawGVz7}@xS7SGjh9B_gVmfb@ZS4OClEYlX#XFVSZ(}}9)8o=) zDY*HDyIl=UBM(E0{4x~YEr$ug$4L%F;~NxR+|tAk8M(bzitXv5_}Bh+6{JD5_y8;H z6}{K=bgzic-E99UccN$k$-E(Wuk3B~^`;Rn4j;SiIzb zKmvzibXJYA&1)`jKy-RxFIngq{Z43Tg15kQk}-1Cej}fD(+%X{D)24c`ZC2t+KsG?K(08i&HkX@0rU9 z)P4wMkBo0*k1eY<$t1Q?*A_@cr&Ap>ueGRBn3LG>4OSA?2P29ckN)B;8@&D+vhnM_ zfkCO8;|BA%8h?}I+r|iLxc#&AQ^n~yAmepkFEuB+K3|4Ep?Yci9{^XhL@2rmEXX(; zU9DCi66gx`F5v*F#XS*9?luh7cI)C^04ib;bot_vTlqQC0Qx}Z=rK=Qnc25xXXQ#h&HGW{VM^JPmW>Lm!jmr7b2YFi7SwSoNzX3Rs53A8&LuT6U zmR-%C-ju`_H9jY%^Wrdnn32!PU#ma#tB^frzXUO(cNk{agGE_M{G%LI1lUM7 z2@v-2ldY}E45=9O%c@9donG7`DKjP?{r~!TcIyC6PSMcnW!>%%PX{wGK^P*K)8>Je zrDSi_eT9nxngMGv9XO2azMReP_qChVkl4UsV+7k7Ug&<0fJkyw3cFvCt)yR9ns|gtrjoM* z>i5uTpi&+b5D~Y}Q5R=v5))x?KB11*$Zd83lAfW8s1nS=kx);|Z;f+Gckf&w9bVP3 z!aDDVH)gl<_q1aPL zDZonTQek1ft3C;9*(oNC-UnN|xLK+0oaTvFPg-}pp7@L^_({S7w$<~?cz@3W*Nu}G zB`geXcD1k<*4^bZy>Dy+vU>q<65VY-kiSn;SkC>Lm?0lb86B$T z#@jlkRy&z-pXBGphI94Ak=j|7CFABI&58yKx)LMVV2!8g?JGm^K%Fip+%&~;W*)!O z%RF4md#A%(F|Ck(7}D7c7iUWDsNMo4ADwe6F+|HzENz$F9LXt@3_ifEU3KWfp4Tn? zVNIeaH94+c>ae-&>=WE(eR4Z)@{x)ar8qDvY=ja z5q%^x_Si*-nr{l0di(X4Or}dqR`mxDiFeZ!(|*BYe|}2r`i`t`*}h)g5?3>Ug>@8F z)FZC5$uV&7D3wP!EG+q2rv3PZ9;wXI;D~X7h|_=5u)H%gw!D*dfpY{TjwIuSFq6g;1{&ait^*Cy{-!sh zI}tN`YQ)=RFA)pv%rl6j4|2uwShDF%2J>kHua_JIu#k5S-i-~|m%qwG{nEZGdjq!) zO>r9V;c<<6>6P6`$1f;Pp**=vF03ul`PlGcfn_U+KZ1v(12um#VV!M{tRKKE@=9d0 z_sTc%0pgukZVIa6Ey#6kP@i>?B~kApm8FtL@f!nvMG01E+647Y^i@Ymrt`~y_dNfj zF?Zr06f!o06XBIaaa_{o=+KL7tn#5(epkAflPS3QI?Z1HUArELMEg;hyCW1AXH%oO zU{2BFnZtDTolB0J>3YuP=vhU6TSOsIVf!5}H~wKwlKb$ZPA-8OrXEk3v@|Y@9iCJK zp6ss1t6SLiCrFNQ8ObW|ca-UNuAD6jXQSECb=eK0z1E59&v-Ad&ZCF-o8mV}>IW7Z zbJs04=F*F4?+21+YT722xZjP+5ncN~jk!O1<^K6xd8F&0Xd~Xll3Bg}KC*9@$ffsl zb7`vEDxN59qI|*}a&u~Mu_gELkCxm_*kVhr$Arr0>j51tBjYt!|NL%_JmwCwi0b{( zl6yzgDSK%~sBL%=$-MI#-YM$gWZQMG?kl3pybj$oTLx3R`DEyhE%yS`rt)B?>YHbY z*A_`g@xTQ**fYYMos1wIYZ&S`PMjm=V#5Saa8-EW*T%7ZNeC~-B~&EDywvck^(nO- z2UD^Aomkz_Yb~#1J%pgSjZf4kW+ng<^3_l447GcCPVYxDaIuDZ-W?3S5eK~-A2W6N zj{yT$_pvc>_4jmbqD_`o@cAAfh>JlbT8#G-X+S^j-c+C;mjm?UDp=ZZP;4B5-eb|_ z*}1>!UC12gX1;;~rj^FNjw>IdL4Yne66{;rrK23LK@ z{U9OB#Y`AVHl}e?^@w!veJad@3XqV!bF$7S7qqPedmyE5jhWypqEXON6y{sCphnU5X7K@(~DuKWeXbvUhxX^7H`0Ab~}yO8DrLd9rXQ zp(7+C-k-6Bt^dC8@=0QdgDj!ZnejCB0X)bjupBMrQ!b03f8OlM52} zf@^Yw4XomEQv&duedEl{!VKxJ)|>+X2)Xkkudg>uZgHp&y8d$X->XY$Qx|izI5S@% zW9M7CdaQW4iqEBy*Fz););JJwk9*i6wtQ zFhU`Ax~k4>vsn1Vo*j{u5rbFugH{9k#Q~rdcW>b{I&FJWyQjgP5ibY*Z$K+97-+>s z9djs<@)615;l>@=4JV+2t~YY6-sKpKoAX@2=jG4Jh$6yau9~51QC;gDAPi@I+3h$>rX5TvXYea5%5*qec*)ji*aR;%2~? zY|@N@wP=G;gj-;Nw7w+1($(034EU0iYEA~mG*rgt94EAb;PW>1r!-4RzFrQjsaIVg z9&c#r(B!Gro@{`BoN`{#mG=bOx|!|N#@iCGJ@6K-wn#4{)>^w24XRL6tkcy5Sr@F- zjheC1I7Lp2p#Vj9X>L@LVpE7V8Yl7rtPn_WSOB44kq z%FZQ&gE9H~S?bTcF6{=oYVY0sr>j+qF%=l4FzT}rSO@Ub6#xU@UNa)t@d(zhfg+1?Mm>* z<1Yp;L!yc>yDGX&n{X2)WL=lP(?!X z>u7)lnW0vtu6jh7$w7@?I(5>^%vtD>r&^xhQtYOdeeMM*CsAbp< z^=Tbhj#2hY5h|uNl^e!S1BsP+S(7wj%ynzU4UIgYqIOeS90Miop$|8b0QQmlgMFm# zP}N$jsNIblN&a_5Z5%!K;5DhJ9gQ=RL|u-)Mc?-YECCbB;e?#I3ZSC)QU#Q{&^Q~^ zHnFAK2&udCzwu=Mngz(UvojM2nK&0Dvp5vG6c_EgGz9!+1`GKUN6e!^hf0+vzq{H^ zmUkZ}e-ZIic9z+_vDd)laW%}V1Y^@_yM*mNiUy0Lfx6t&n4!2<9MdV44>O$)Hz{+l zjKc!#`-UVbp=Xzb-igCSa7^dg2d8qk>lztgA7MOV9h_1;rmS-@j=?jh1e&!lk6mYCH zHYSDVl`SHU3K#_|$7S5I(b_xMhRHK3YGt@k$54uTX1@=Q=pG1^$tLy~KCa}{WuOK( z4LEgX7ZhSKs%zEj(mJ9f*A4KKj^&jZB&~X1uVn;h-QLUQuOcmB!)Gb1#+^0xm65E_ z24q=n{Wku68n4%)fdPVbiU2RNfh1b=B2heEdn!)U4segu4Ix#<|1rJ&YdXh2SWN=F zNKo|r?2gGf+v;dsBc`fD+zRub&F=N-9r5Pn)$+o7X#Gg`?`DmrwQ3f@{c&<+iMGvS zYA(e4`{8fx3GE{KAxqAB19qS^ptfE}cONS^g&u|I3f_>O{KGumvgrD!_Pc$jON6(`AnmPm_tatr3YACG<%* ztFAXWg8=i$$y-g8Bh?$<5A<^hV&dP;_tPgb{_!F?io_LIC{P#s^Wv&astxn4nb&wl z>2vS9!0i5I&K`bxSY3Wnc4$!;{QN=kPa=4NpJy zv0*14}JLaXCSI`PYn03#0>F+X2~PUF^`0 zy4X2+a0^fuJO0CqY^)LuEgU*;+&UQrSy<24Suo^FMq5A=zxrCwf|0;9D{hO3RPKb{ zkPsGm$C==7m66ltW<^cmCwSVJmS#x7Xix85H{c?xZo57wM(`UuHCBvFOmv)k*i)qREaTq z{-`3nqtmrZ)W&YkJD?cW+rB(|P6H^0{lyz3*M{jtNi{A_$flQnYje^(5@e21+4e|A zYu*xKuL)89Ivc6jlazhE4U}HF*a4fIYQ*g~6f9Rx>XzfG((@tT<9Qh~4!W#csJz4H za&phujp1{g=ar(q`U-6lW~u~cPo8K;gPm1r*8)zZuFnO5;>>n5>b3QYPRI0pwp~zh zs@mfLn5TzkknK$PYotjVG8rpTeMQ&G`52yz2`Gx!jA>;WDF)cUT_`YzsEG1(_i?Pa zFLJqD;aEJkytDl0Lq6itGTyOXihd3><_mG}clWAFT>3=0eP}}H8k>nZS#sF8!?

TV+2v2L#53C?+B!j<~+;>$knHgtfk5Yjyr2S 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
GL!$~38x_2n^Q*-I*LdVi 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%0D`mZ> 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
-u!pB-s`I${z!+*&>FWd3h&Nb?*SP=qP@b4$NH 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

{3vM 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

pp*G`D|54rYEZf?Dc%;%4I&nv9>==Nl$6^@Xn6o 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 diff --git a/docs/static/screenshots/textinput-outline.affix.png b/docs/static/screenshots/textinput-outline.affix.png deleted file mode 100644 index 410ecbd5567df644a8b502cfa912413a0ee759b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 332637 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 btCQm(zY{Y) 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⋘Hq&71f3WVL#?vC$Cz2d(&kep6HH5;NUC9 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

6HTt-snXr8!4>A~G(p!@ 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

+k` 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 Z?vIksdyG;Dc3jjuG5)7^Ih*|mV>=ft-d-RK$ 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 =mZn^$(Cg)K^^-lMjGConO-DeTxOzf9v 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 z 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∋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 eqwFHgla-hRIw*y?d98 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

Dmsa8oe$xEjMbeONSZ+2e>6xJ84>PfQ z8ar4!NKq`#m;Db{h#N{QCTJ;S@#JHuz*go4hggQm_cuc2+qbAk$tVotnQ ztp-#nWD6khtWmz@fYFxvFu_!JN(-K8{-6C**jNI4Em>!xlp)>k*01HkK%bx z7#y-a--tbE^A9GYj`qCDpbM3OyQW)ew!g$nwPlo^_ZFJo7Avd5Z^T6&QyF?Rp7_v>i#hk}K%~iCF@6Pr<{$i|F+yjnLXvJ6Y3Cew%Buq`r zhJk5X$eyn$_rYsKt@1AR5ww}@MYJRz1#{~xjv>gJvu92y^i*`6BaCsrVfO|3;mHcQ z)v{Jrn{$B{fzHhiRaPWozHBrw4oe+5M@WdYuCR3l5eED zbKTa4wjJ;st>13*tq^_`q8B#f4%o@)pPxv1TYiM7lnR4MbAIqBXw&%B^joc-dF)TX z38gxu`A{-Fz1M{P3yT2tPbc|BOh`lL)Kz;cLp-{fyq-DLR#V>D;5=LF0XdsT5rP-y z0XEH*=2sq-nmm+1BKRZ*z>eRiOqfhr0C(NKd5DI##V&>B#V)Z=zF%VY_a+%wb`9>B zxGA~W_s5TqRa65ZYT3h8xY*s11we#kYEggLhYhbuLu3w?D^}W z(HrsLV?H{Ey|;kw051xuX6kwhid*Hy4gV+3cuX9tOpM;}**7(F(8ysgFY9MyD6B4? z26Xl9))d~qHzwKu%-zHyU1MM!Z{M)(&3PB!8hP^?<3`ALSw2YerC`hh4#U4%v{(7X zYe1r8O)PISDv5DOL8DTz4KkudB94qw$I1MVr5M3e1ks^-lARrxoj0wdg7+iX=a1aV zacqCyF5UL&#q)zEVJ3EFiF54!4fX1MdvB!k z-6_4y+Jj3ry^B#wq-s!r^GpE^6^&D9XLgiB-bTMXZ+~zT|38LJY)`RfWsRaO5rLj zj_Jzl&qE!bRa$EVx=FnipXJ}%Zas3M)`&r`$kCoT?)tnMX@(toKrZ?H^*6Q9M_?oric66$}N|STaDti{o9cpufE`$(|(<^^vC)ETE zV^B1c-ajUcOc^Eg@bK2(W8j+3vUi0&i^#Nd&&D_7)4sZ>*MsxS(67n1f= z)<4YKZssLjh>=y|Yaz$F!@_vZHqS2}_e4*{uNC%`c^@B81$ESP!`eFXQm6u$7?a!k zO5o&t&L>1Z15aY=>-?fJnIuAdiXUvJ(!@D3KHp8?tkt@6QIc!NGoFQFF%+Ec=&M;gpCuP6 zpD6t)30mkO3O}0T?+Tj!iI!!KncsWWi}2OJx4gU>Z*imTcunZ&jb+is6_#dwi4C=)eD8KkL>mBH z*Jxe-m^X)D_-eq{NAZVkb3oAX*4Q_StV~;QArlVD{k`nbnya#f(QMfgr^DZ3Oi?=I z$MC&jP;dG`((xvNax}Ylt69W1-VG7Gw>HAvuj;;-*%@hDI$4sfmb1JRnGv-o{Rfoc z4L1iR_~?I}dtsIF)Z;cf(5>jO_CVoHc}uN)i@)U%x^@;)P*WmfJ~D=DtCsgMUffM= zTP1SaOG^Gc_sT*??AWKYMq@wRj$8&cBQie=cK^z$Pa_rlNe#jH*HXCMN=3_KhV>M3HA_KX1-Gb-?|QVY3LqHMrT>dC4XBfg9#aInfV)E%qJhECnzmJ)7K}c4d5Kz)j%gYsWh8M6 zVp~5MXKTu6K~No>U{55BHu)_8f5AR3{)IfWHB|1H+}Ch__hyF{=_a$J9fm4V!v`xV z%cLVKQ9w|F1g@*+>fDs0o-Dm%?Kyy;rtoP#@>EPORBTVFl?#@s@Kqzg zwrs8?qeka;Vg2t$|G+EOF-H+!p+nyrYm zu_U;JBZ(QgVhdmJT~QyN(J`FU+MiPipKB_{!<*t9_-4~fp^Bs^1eOwH97|%4x4kRp zx9@$p(}M+vJSK`qwAZRW<63Cl7^wp|Zmy5_`@5O#_wV^|`v2!6(0d;~TmPp-AZ`7k zXV#;Q3u!A{D9?poS3Ty+cSUAC%6`{!Fb_TblvVv>O?6*%X=w7{_MzBn^e(%{GmqQs zjDZKW6!BYqlUBL<V2$v=V`#`4y#7yd-CzDb{`8MW z%$o+Wqyl$eIjyZP{&JW zQ|#72Rdx3SlV9x6bS0zjjBgRt9^ajsUVi2$IVfE+;ig6vrG`0b@WuI}4pu$Rfb6t{xYMF5RR;SS+ zxBaJQuK0ZCyMVe*)|#seIXc}Zpk}Grc!OzYD|k=QOx9hbwFeVomQk36tykWXsu;2w ze)gD{)btQ`xCHi{aDSNb{vCrnHj91c+hA2@g^r;=tb*%3=CpN&liE7)Y&iP_$RIzB zw4U`SAK&zU|IqfX)m4uP$a4R|hy2G0o76`D-sq>T8+X`t%U;}W56p*aR~{@=b)r3K z&!*1*U13pMq$@0cF?sxIkOw=t%pQ+R&xW_Ef3I#*F`h5+?`jU@@=7p_bD&5^2i{x} z^ad)}>)&)M5aU@7x;3s4H` ztnUojj}@`hMxpOl=OpNR=##rTURjG1M^PKY)(`A5+t2qDJwkBSWv7e~Uw_s^dMs;m z3fYhupO$0AP&DaNaz)E{)0oj)JQ<`VoCg;0&7D$HeCDT%JOIsZ%LnjssO%sc%4J?ne*3#GJ;Wq|s~ zKC6ZzUUeq%67|^kdrd~%r#VF;7eUU(x5g&vOBiiheO$~ z(d@yAN%q|2p%%=q*c^9ZQUBT-Zwrb!Y()XpsNQn?3q>F{`BQo<_-sKY z+xt^ZlRIiXF|tHZ?WeK6sHX=*zxxi z9hx-lud^Fh5gf#-?zvtH%^i)*7yA|*EDsJK#poE@P^HV#9PI#&N<3%i@oU?XFFYal zB+iyz@O=JWkTNlQQx8PkSW9HI!CNWrzO_~(7jL)u;*tv&x6F7|FOJ!T&Fgd;)T?|D zL*Gtr-jgc}^!UZswmWgOrLb|7JycuBKRRpj0${r<3lwC(F2=tn9@;L@_PpECh~A)hZKpY8qjq{O(T?>vUA<{`#jKj+H+ zeue1|G^?ifNYP%oR$&gnk{0PYK(69r`CzBjbYF)`d@pb0x(xam{zDw#9ue z4Ro&fgaj>SY%=~GfT%JyE56=wdkxtca|7k_a3b%Y52vQ;*?6NXs5sw+zr1HO28F7p z#U^^=WdXIYKKLK-93sFapW6#(T+Q9HX)}=;R4mPF`B8kfYN;3p&w4(aMv*wcJxtsT zp5=*F6npL2666UvGCJ`qj{Uu5F)IJz!gi(rek5^!J1wQ6{rK9gO{XDmo^RIW!6LSB zJ6_Q9dSlySm(22=TmgCo^b9358ZiVuR(5ryUDME_T7l#?e?!Oxq zxf9~dC=>I&|MT?}kXne4)>XZPL9T?>lba^S+#^V92enHoY5~NNd?0jb&HXl{FM_aj zehGtS&#KX0tqWG85R3KV04=~#`Lph>oaf1$4uhp*Jei66(A#^?%a56HOu9)js%~4XoAnd)!_XGcUk#?DziKj3#U6U-@| zywm!q-pE$Pa{Cs_^?}&-UVH9m%gyO`NPzh!(}F1YHKvSi??gp&p~3pzh566)%Rklk<$uG~Vs8@q58aBr-cE)8OA0ykap*&dq12GrIK z*Zg{!V|6j%C~(WHN5j%qz^Vn3+jh=^AOilcVkbR3_~1|NBpT7~V(p}?M^q`}fffQJ z5C3eHVmAneSPESr7y()v^@s@>jluL@ZrMrgdRhD z`mFE|6wCe-#MCzfd?ki5eTBSQ?#Tjdw7KAY1vTsrX5|@A9g2&E1uCZQfE5Z}9eT)F z#Lr3VwnQ9$Mq}+j>VaV@(y_Ax z+Oh@JLw3G@fGv=p*e1pRG66QHFK{Vyi!p<|70v5u20}ilnDF5-HF8Ya0@*1Aw!%3_ zMlSe=93QmxkJ0ijuv2c{RTzrLhE-2-inrdp3$!7C@%HlOT2=2~f8s}n9*;z$jl51d zb&_3JkYFgO`#Bya@oZ;2+7Ee|RoU0;>tTNlZJRawuTBuM>OEPv=VkzZ#*r_9WTv39 zY`SNc*Uv3q`n`}J$%Pn6hyg3)v>^el-5Ipt>bBZeKjJY#yOsHX^1Isq=nq6c&679n zT9HO9Z<@rm!jqDE&gZ|K+=3k-`hbnN=$nr*xGU!FpfA%#0XJqDw@KP7cTmpnNX9ZqOg^SaPjY?Z}I zdnmdocj(nUH}u9&OHj!cobAu>HPv5&oY+N>6MD=0+7thobf`^VBi1$Ku#Na>$a`qF z+To}}$9nmWO78Njy1Ai@()nuF4~JaV8Tzx_P8;J!wG$2MEViMczL3_km4&h^Q1^)d zfQS66zEKzUlH0HONOi{<7i1~C38Dvw{SLONsK~eW7JNu zBw029RV6eQiiZ*IrG_dsebJ?L79vftXCX$W`jk%BXl1Ta$}Z_NGv0jRL;I6??DJwS z&kUJj!;sy#W*Cu>on`RE*mT2vzbB9If@PO5rPtd8Ou;L2WavS zVUWswWX|DtOt5XttqXzW^;dOBl%Ns)(>8ppH==p=HUj!KsK%gn70BfzLTMe$o4AbD zD)f%~T`xE5|GLGx+`lPk7k;I;{>_1UQB~}+yOWXL1jmG+zjIwINKsaT!kOE z*y+P2pV>mzM?Kz^f)$d_X#8ubZ;wL{e|oKTHr_@N?jwAiEyeUUSEhvd4&5OfKpZK< z(zl`cnOTBZ+Fn5R4B^M|fYF_Nuegd4M8TBt^{Ge-Z+$m!xjACwYwet5W~s>IoN_VL zo*`ufzKf_1^LCq(&o;11x-a(}Y3 z^;EbbuU4wA*i+`|tK)Rnla|u5i<848HYH+Ru}j6ZJEdu6WA8r)Z^gagw&uIp{@m`4 z+)VkfLs$^ire6#-k)Bp=<<&#p9e?tn$XqUwa>4kGHd=N9s_y6TLP4}n84086f*~F5 zGBqQ8!eY7nk@)!hs0D*}r63y;h^>|>LkOgeH#!@Ftr+xX$v`8uWm%R9BJeg zK(;TeAMa1HrhRijCljxy0HNJp^Bxdw^NzUfXCpLI5!*%}+Dq{d|7PVEsw0JWcL(4} z4mUsuT(n2*;{Yt#(E%wTO>;p+nbK;-nqjQsCqFX#mJNEm*md|qvEDTw>jHsw5PLdJ zA!I`LasM7a&XiY5F#h4<&D^tz(fT&Pi}5lJ=2`&BtGF$o#pE7wOLexZS#h4<<5$aE znPfff$NPg*Dhu6uk0=st(=7hAo(qzyP=&8Lb_AIQ{Tme)8nix>k|>8T$rl# zuP@Ftw*#Y_L^YS1BO_<3sTvDqB2sjpe(pKAc><{O+^})3L$QIQY+Wn)2TM%_R`6=^cSW4LpJrlRbnXA9L z?wZH&@K4zCCT7Kzbwpe}B0a$9uaM&WBd4M9fN6pCKkL{mio}kk;(B18qFeCfoST1L zR{5v8S7NX9%Njfiv-nyK0~LBQV`sc{^;Pon58iZC|Nd=d-OykWNV0B~;qIAq{T*Bi zS#@J9jpuR62o&JC^-A@Lm4J?|jZ&t>_Y%bu6uEJ=Y^?SGJ38)l!PPFM=QD$E2WAg~ z>vR%_VkgLrGh!)U^6yE8B+Y22vRqA{QdqG?4u8qEmhpnM()YTqCwq5PCOR}v<`zc* zLQgW6Qf$_*2Vk8Y*W83EcXEywiF8XqgvWQKmH`PFs%3i^vRAHiT`#lrr|eMoUbIKp+<&9z%d#CH^K?hBlYv50l)?)sA4rGnAv}KAIBf2wIKLzj6Xy=yyk)9(Dc#?v zlCzbRBq$LNB5;`PXi7?W>!vN3c~X^?)4-)AEj8&w-Xjm`vPZLJV!>P2F6|)*Cn_=% zv)SM7mlc{##1Kr?4UcTx4x*2qX^MTVU#GFzI=8-on@)PVqm^t^OsT7=|8DkH?6vx9 z1J%$ro5vkTXnU`+-P>&NtAdK z1%G0RSt|8&l0dljJNdz5{r$f(m*x){GHd6*t`8~yd2;e+58S-aFcf+t__yp+Wf1F& z*2hjCKN=i$)_5H4Ta>ES4Mz@Qvx1+AoAvE@yjP^Qv~xWizvYm}EbiKxI0rwRHAXd- z4E+Fy>^){%KQ>`rx4=xSef2RdouJ#B3uu<7G_IGMH9tQrW>{lc6^rHvymeJyTb?Sp z8JI7X6BDeZY&4+uGRxxgx~MInwzDq0h2I_s_xEPd)&YS&i6VgA!22PzjytY_>DUS^~3s0HH6MZ5u70 zx$%=BDSVV6_ocwqEb?{`+4oZm@TPeZ!VXH1!}SV3c4mAemDuZq`^?+D@e-TNsjU79 zn5Fvxqdn=g1b%xf0M~qfkUj^DMQa?&ord`4A_e%J({EVIsq;aWZrn(ninB`k$O^LN z`@&3GuATZFXS2yQ<2MCnU+@u{I#EP*BY8ORn^TYKYsHTd&auNE@2m2fK6c98&NdR6 z{zmvS`8>C&pu950T@_J7x7ca(GuggSVzYncRhIQ!=T89qQLB^ngiA|z@uiHOQKq;m zp1Jw;weKknn{368m9yfv;J5fC&B`GCt4ifl&&NXsa7@L6-|F7?&b8Sr*l|5QuIp24 z87DtL!ggoPFQM+3|CaLgq$53$eq*mI(`$-Dt^t3(HU3tsy$DMAXz16WBt2jr@cIXY z+57YN%YJnzGC}fSw$a|I9bbAY)imQASg~bM5FEmvs1du)NcXH|wJ~tOfH} z;6;D`R*I|tLsz#3gv?W(8MmC`*@7NBp!lm`4}*A_iGB1sQ1-9*lMt(BxVhc&w5!9q zD^rx1#ojvOW3hnJhwOOjcXk6PS8NrI~XVM%#(UNI{{u9&GwITMzo1 zt>+L^tNV$z0Vl^8{h!!+A}dlH#&Fk;!9n+;28!>^h87;@oIa`gXX~zU4r>alU@+BC zqj5?fsW+^2P7j#{5aBde=i3dN9ZAoSR=eQhSWSbVf+%!&?1k;MWmDD4DyEk%}4HlfR zmJW8sg84uCag6Np^f@=W9X@0WZe+~)TXXkA^W`T&-Sq}c&WIr56?&d@w0awze>(LJ zhlx;xy8zsBflG3Lan3&}(RDs9&kBk&B6<&W=R+dNpzrU zM=2p1!tQomN_AM#skI8a>vVdPBQ@RDcr_b{{Y}oGD%3e(SzU~jgYSL8r}$s&e3z!j z?%wc!7iR!l>IiN}{eK14vpq*rJqM0&U*x(EV{?upy@_jNQC`fG%!*9wG1YC6GZyoM z*H|B%T?x`zvx%D9EdODM>v9qb)!nFDz?P~TUd3rUia(cVU}NZ8wcBG`eVLs7cM>u0 z)f6rq9if#O~UMg8_08~RmSQ)Q&j*lfkT#e?fa zYd?Y4U=FG#A2T@(ENKh##-P*f0>vpVwsQ}+u$DQcuZZcim$EzM<9?2iv?o&-_ztSn z0hUP{C#3yFb7xT~HW!1L8VU1Wc2HG8O`rdNfZO|55^R=Yaz^sYk;W}}w5`-6;o|0t zfs5YGyyhsbSH*q(H-l{=%vqJH($5LXh~CEZeJV(zNy2H!Da{dNf{q8S?Z=Lh>?a#0 z5it879}Eaw3zfs6fG<}pkEvYVemqu_CCQ;VO;1lhKT;P>S5Inx5=9KEkDK2ei#0hP zK_6iy8Gm^{x{I3@cctrD^8@REY64Y3Vg1(R?6s57!h{BP^h5Mn&7c#gk}>`71!e5l z%o3ye4P369{uztEyqWpVb;5}I6OK}3D^#lu16m^`{c}lrks_b8`R^)(m;B_k;^>EOCPwKAw>--rKVTB{p@AQHdkzc8 zut&Lb7zm#e=}O@3(xHRONn;~P82w+B&D>Ut`X{zNq5S~> z*5}=$G@TO8e3{rS4zDLFRmiiH_VlyHYX2H#U+w>NQU8t^|BQUmiQy&TrjxhOi|dbs zIe7{$z+q9uPQ6YmE>y0-J)j0UXL~Lpy7-gs}x5?EE`OnF*bG4k>VZMh%7FX#ziqOa+tap`XIgH$7yby;ar zjlt08#V{)ZR~SMX*Wo~QB!He)%W}~zK8o~r0`o-UOI z5y8nOUg4SlWzp35dSxW~+i}DlFVh}+BcBjC=&vJLWy|+F^hwVwBwQz>7Ch=jnjbxX zq3T{zKnigBq3%*TBzmpv*Ta+QE2%RbM$SxLt>)&V(Fyc=bf_R~ZIt_e1Hy8+>U?%~ zw%$?wknmS5d3p>++=;(6{N#>%ape=y=?qzmQA4;q9Uz`l)m%kLVO(d0N5#gkC!x&g z20{>`VT>Di&&FSAf|t28&ulNGyVpUC*w7N_x^RKRaXIBJiR?)1SigBA+DLc9zf)=_ zQ!<+z*NRV$j+}bC#v2W6O&fMswHSAIrF0s#CtxJt@=9`HjU9t&QxrU&7l7RGH zEH0PMuZdBr0YFkukIx4lt&Lxy`ZBbwC1VO9(UBu&FNq^dY3^0(i;?(bnsl_1#emc) zhEr?~-?QL4?72*BExKLfwCoqR_x!4u_d?d_M%wb?OOZ$2NgboBbvSLyD$}i$dQ4xb z(d#xyEp>>ib-Xd1I7Ys4>8w#Aowcgc!63VAs*f+FgE^65SsN)qBCoPnaL)2M0l+R~ zmzX(|!s)lbfVHIPe)cRg&z8`mJf7H`x=HzGU`{oAdhm-EUB>?%n*VE&MyGd$%q>#; zWaRoMy{c&+80m!8Q3l}h{CxG`cO%opqA^9cW8d+y>#j_L6zd%5=aDxln364P7vVIq z#-f*anx^Hv6vRt`i^!}+=-yo>4?8sV5X1r1O>{;0HN1}TyeV;^Ed!SU*06t&O{yJ- z(?ICDkb|FQLW1`uDqwRF9h&Rg*qxB3*nMZSh%GAGt7FVRxP1I-oafgd{{-jFDA=A`I)a963DO zSYF8fZ^WjX+wFKUJ|umui=*Eh(giR`&~amCm~s*R@K2rlPtN*hVT|cZlxoHegeWsp0mwIx5xk#mD_x&hNMJyg`C@VbO{w`12zCAge{H z@;tk(ej1q%oa;_$5{9Wt`N4KcIzh0qgBZy(gTlyui7UUDFh~6znK5KsM0tPiVaTaV%ln!0 z#@w!>HR4Afs7cyoAgqhw!#+lueRTEjNIUF7FTil*kc)FjNE-Gq{gMterni!H3ARj) zb^GNPRoS`k^lkdu_%)%u#L8L?K7UQek;SV5cNLoOl{}5{QF?6Or;+jA%i6LYCf~CE z{|p9t_bBNSX*Zm)>ab8XQy?f?4^zW8;1nTBS13<63W8eH4{%NXy&@v%G^^|5d(pUj zdLvxpv5N*Ah(1o}T4{(DSgF2(s+SgeRJL zbUqNM+bIDP!J`stA*Wu;bYH4_DyE!Dp(bg?0Rzag32ltT^rpY6$m;5K(-&F=9sKs! zXj)UJrjem_1k$VPmWlf2j`r_7njDDfEw^U{7L4B=5hHFb_1u;$AT zc8xo}H@CV-&*6ru-tqc0O_H&AITT4mp-?}&>hI7*s&DFUzhsN9{>m6cU7dgHmw)2O z1IHKjdwQlIb7JOQaR%aJ@7hT|^PlPkUSB36wpN|>-XDpGIhFA?NACU@kMk%vg~{)o ze>C~I?qY(6)A$p|+g~NV>O||m+T<_l(Fj znvJf>1Bk+TOU(*V7EYc+GP0d3=0vG(q>~z0IZ&pA0kNv-=b|2cd26!K+WaFOqXaww zIG1gV`XG>V^}Yav7&S!y*2-_RT2EM|^v?V*HOp=3{XqYpiE^9GIa$=#9^SX7z6^d8 z?mNUH$oQY*^Y7}6yj+wkGh81~zk-@mP`OBP{0(-%8HmMiD5C1!0&>y^@APCBSaSC> zy-*eXcvVE+z!5``*xbYWnfZ^=1`T`pEs3;^Xl>pV@xQ1n!#6s4o$5q^xzL)xi*dL? zNt%r8#mg?(D4h1?9qfQ#bHhC~f^v1(?q&;sZ2Xc_u5X?14Ax>vigOq;&GO>zb>#BJ zGv1`(KVZDi;>^g+Mi{~CYNQN8#!XSss#X}QrCLaCU$kzvloA||K{KETQ@v_EoT`pg z9zP$+ZL_?`G(S))mFOV0pFfP^nGab--7|}?ucH`hE`Qdz z2G+z598f(hf%|M3lN?QsYpX=*F5Rx7fo|UjkP*S*6Rx^}7`GW{SKf+8vWR?Al6Zo< z$0nFqnDz-Titoi<=tPE)MwWn5DwtIU;9?~(oe=N9j0p81jnMG5;PawWBi_ScF9DTi zC5>}rW`xmZ@^+}KfCk2Vi+E@Zw%3bWkQ(c%pYeTbE5~_XSWCByN5CcmQDQ@MLSNGXkQ8K;{dTDK zfPy%#L@E%hER%$00QY1AY_GY}q3N%lfxG&!9kQ=1<3be(l!oiJ%Z=`Jd4(eRUJ32< zBH7Y!PoyS-YM?d5&G8l2{RF2^c0+)D<-Jp5s9%xD=Cj|mR(KOagaGBddHpB|ngk$XKf zv*DY2lytN8cT-Mruwg~iB!x^KF2%MQL&|Nt6pX-|x1=aj@h83fB5<i~D~9j2`&FevY1N?FMIxHIbGyWKg83 zI0Rwo#yyXi0pIgT37;gb{txjBytexeXCjSv6hvRxbBfE{`Ta5}jxpnZ6c`NsVbz7N z8s$6Qg{Sm|Sr!d@c(76Dh>xt`Up~Gp{dl8)mg^f-&OaO)#ZuALxfk%lr%302fIBUl z$N!Q$_d~eS?FauQcUCy5XTm|IlAmh&ceNdZj*Lr!3spLLIQAyTosn-umW5BTsh{pu z7H*$lENQ6`B0gnJQ1)uH<2m?dj|*+@a(+tZJAj2>mk4lysLy>@u}btwA0|c}M>foX zn8r$7oW>Xs3SO#r*K81cXg=CZxCfRc`+!o&K?&zup`H z6t!N@tKR2Gu9#;~O9>ZYtmMhMiFw^A=GWGty^#{Z&XrK4 zn~OqxABM6S`hMQ@t>ODxK(6=~94QtNACdS?iQw|k8+RHsHtNu4kYYX};jadj*0Y*L zld4*+Dp$-h^W(UKPDc(IP+(fWOTjqj%Q~Ay=2LNHfC@Ur$+5?|1!yM`H#z(4B&0f4^D>7dq1>bK@BQ@e{Z||0^Xi#`Zc+C7%XrVg z@@~+2DTWGpc`c?#GMZWT)6vnol2i&qMmRs0Zl}`Wm#yKO!Y@LcK#UWegg=MM+u<=W zVo}1{31(2q|KXA&gAu({n|D$y4nt-;`BJ3xNzIgeLMT+-rt9#7L{%&BSozWv zdvEt6N7)2;gz*RpqaPMw(23!SNWTw3Jx~E1PKnlNp)m3ZXY-PF0XG=X49>CoVM8Le z_!wr5-I!sL-y&t|7LaNQ1ZvjI2})W;>(reKg*b;;#oTD>$v)D-oyBdBU%?^PVk>_M zT%J-32PelD!vD>L|LKYmN*upG;e5_+SYbwQCGAn}c6EVD93(&6r0FyHv~D8zrHxSv z$CXUEslNBX^+SAcFy;nl<4Ek-@tZW2^MZ*EChj!Gw$60$pPQE|kq?lqcMNOj%~U$w@_$)jIa~dqsp| z@ZmN>b-fo&^Cyq=x{x{XadO5S0(Xt87CE@*v@;NG(J41l4td>M>6LsubNSWWEcb;`NaMTuT{6|N`d%PCRxxT zS>WI+D(L*eLVX4K^FiMw--66#TDJ((Lc?Z=~FW;49Igv7^2FID17+9K*658)O@07oqg3AGhr9p7U zIVdAAfoC>K0q92uP7TGW>&bUQ}C+O(%-b}KRaIij>hTU z;6r;0Lp6QN)0%;C&-m!39Lh|seh@j;zcn?2D?7`C_>K3e)bd;_iX38R-W61N6}n6U z(?@KKPQ?L3QjuQ!W4!j48O$Z9R^(yydB8SMi}0PJ=e1f$;Lh=G)R&^*?#pLUL%(}n zH`j3OK%Pb-AsCN~K!cPaKGPd=u2Et6%WI^WFgCY!@f2u?plOBm_}g%pL1 z$OS~E*!A;%H#KLaNZ!}9pgh|Oilv-!Yui*vXmt(@1rsB|hK z$sHU~koFqi#hlG*T_P-Yzb^TbAC8O!QV3IwL!tKfWf0gTLu58DbJq5Guqa6D*iqoB zO7HQa^HM@+06|^}3UT)GzEH^Ad%G8>Gpupw9{^uNAyj!bCQpNHq!Xm+|M%vR+9}KP zEay}mJ(|>q-(8(7h~lZqCXzEA-UC3Vb<_1h1fqqiWBhG&P3$m$u(^y1iZW4%hyHah z+vdhLGLi-EDWasTq^!vxktjCe__KFqVvGFNxb{AHsbD`$&z7A;?Hm11D#oJd-kpV` zCpW^$*UEd~c<^hK5sGO0nch*3WPR;T0O$Sh%N+5VhPT;9w5S@!1S3694iYIh*Uhn( zeA_Skj@+lT25MvB1>Ma`$CR;ov&|GbOx)Sj%}E^JXv$LsB1Y@~V(UA@no7I1DMChR zHj1?1SWpo{uOWfK5fud;3rY)3KtXyZ1w=umsi+_=Aft>00z~N$5J^BvKop3SKoTGk z5Ry>R^JUKa&iUT&eCK+uTzmh_-g`aITI*i-T6eNw0FXPwKXd$OQ%d_cjThs@bRX81 zBn1o=Yy-pl%|9#?UX)4ZvIHKrn-7Ptw+D_f&iz+E=fAxk?Ydt7pQ3l}oaZ-H>d$(b zy~R7{i8P<@(LeMIX{4|FX_wtVNqt8@=iYQlbWDvzJ^=)4o_)YveEZV)A~r;CL!s=> z-Q3`J8_9J1tT=d;EC#i{ks+rXda8B$mtWmge_P8K-h7lrd8w*(>HVb3md#@3=rpC8 z&&9fC6+p^hLrbx^vh+N|Uwu;5)Gg#U_NYVlRs>J-{FZ}kvaoC>soO9`}W)FXrq0loCUq&?qxmagj$xxVphan z80Mq?k&|FssT!5fHTi`~y68l@ptHxh%B~m73Xr9<4qS<1Jzd>ztA4~=G5Sf31JfGK zHGFYrz~xMdrakDmt-s3cs(dAw&h@=E}8 zDTkq(ujDB^0IBZ+Dcg7`B3mzO)6bC&&12V|6a4NFQ-9t_<><@3w@M9jz8<#*8LZg3p$wj`wviuD2Yn= zrtmQG)^%^XMe}YZMhUGMKsG^+VZ&Qdf$P}J7t4GE;vA?A5L2-f(W_W)_1h#h;e$J{ z{LRVaxh~rqyfr#)a!Ns(-K9SO(lNIQlWNK6xZ@LDxJr_bSWwSf73|ITM(o0cfLp3> zJ=AZ007LZlNcTjP0)CR3KQzQnNHt3}zr>p5iVY@Aa7hZ8p61Cuzf7+WAKJfG=iRV- zz`4iIPW`P6t(6Z@ovMxaU9L5NVMUpEJz>(2`zHOt zIV(P!q{Ix@@1qRyC|fd*iN?hKMvFRGrV6%Oyi9YJ$L=Ceor3h{2R-$HuY;=!3>CjP z7NFV|7=yz5>q%Y?^HrA`A@+#d3Y8%}LOHTj5-^rG!5w7P59`qPN z)2D86rhpxB(}`d~^05!~$FHjX=W6<{T6@<_X40DKahLVMuynS?1Z7S71f$ug4r{cY z-x#n9|E3`pXtdKGyxD#?lxN<$xv>z)_{cC2Otrlr(Sq^GGSP)Zfl_o8lfuq=Rjk6} z827qspwA(c?Z6Yg?$#*oSruGdH}@2(Hh!msmgC4A!ImaP`I+i9NPku@HR5Ua&T4nv zILRuB$>PI&oq8PS`!L2JE!kmm>XB#K3qMh?d?<) z4k}MVG8I!LQEB-}>$z{H{kO7f*NU zK?ot~$J;sSA7qh(X#e-+a9zFyMw(V`%mXA)$EpSl`g2yh@sAYF7%{J+&?1$tIsQOf zB|i1zx+D8q!YlUE8SUWB%o^Cp`U`5E3CB1!iN_tnw;A4KPORwzg>H3h02UDYf_)nJ zCoCNBGtI4FihgIP2L50VG^MF9kfa_lHp$pKw5ud&@36V|ka-ZHC-f2aU107W*G9S8 zii;QTy8$qcfIsz-VDrg9!?HFDT10JptDuP3z$myFV>@C&=Bnc|Z_K-Z%@tX}x$@Dc z@>Z__ftW7&yc|Gy7tU6bT3iEk>*9ZQw{@0J=z7!npX*|?4F6))tw(BL(T?}u!aR{{ ziPIh5-p_f(bzIGHc55rOjeE1IA?8@P1?f@Mjx{wQb&V{YM2Y*J(=u$ zLsU;NS}AF1AMINF!lJ`{^a1O^ohNE{p8~3Q|CTv^E0{ ze~H9f4&gJ$x*Jd0=&EG1l6y8+zkqe2{qg_pFC@F-{%Z%%Up@bnEAWxlDsN~EZktWL zJnf`ow2m{t|A^yV`GMB@6M@XB**(_E(V08rnSUm{5MWWxWH}GHgRXWkN7g^C%OKa4 zDNCUGbR`k}>)QbndHxAp6T5HxJ4;TsS;{i@lX|7-a1xVkMko(()lcF{Oudw4Gx+^N zQXt3(nD@(GkUUn|V{J!r$*Jh8w+%bnOhA#F7MEPZIKfc$hz87s_p{OaXs0hsd{IY- zH*n^`z3sir>9nx5p7BpI^|VCz1DH`?|BbP8^0L-^L2;v6TLAysjBG3NLcsEz=hEx1 zmHq{aRj>#8eK7RK&p+%-Hx6Nv_torp_wNO6HZVr*9$L>&x$Lgbzla<)R#n4fxe$=U~lyBF&aNl&r_^*Xe0 z%%Y!5BjiviTzTYlO?B1PK}L`pkd~k8wJ197WA9$~;GFJiq3#Prbss6vIbO}UL*vv7 z|1a0`oto~>S$UNFULoC1*X;C;^vD>J1nYiGI}Q0}{&Q-+x?8pl(DbepbA*$MI?v?( zUgc7=T@{^^l&*|8_l`qLWcr!fhH;)Mh?e`?3S;{QxOaN;UfHSSFss9-n=wCre=_TIqhF7v_j_AAUqc{J@jGi0eCvvy5%Vkn!T~4v&`3d zNVj~xxz1z`Cw5M$di&ylIcOsUVxJ988R;;jQlh9t$G*jNngK|4j-zt1J$Vw$8p4f2 zP1sJHP+=VeSuRU^{fs50N@iBS13|7eL=!UYxPHjlq|$}zr%FKXJ{rk^``g3xl8+;S zBgRLJJ^m;^IEvZn7u`aeO?^OL1Lc?ApXVaeqfZSagNwYa5w$H89tpR+FkP4sE(pd+c8; zve{tx+0B)BqlD`PxGZ;Hp3dI{3a-h`44sGkD?;*z!z-V_R;3g9LTE9;P zQo3)5P)W2bfr0Hax{Qng3}?F$2bO;in(dVCe=xneAm@U>@u%_9eRM=@`$&m z6RhQK3^D>E%2eP|TfYvr6paZ6Tev%|N)!D|hEEP+HWtxfooG`wY+nx3Fi)6}t?0t~h9@olw@vX-*17tiyJ0NNl zNK=j-Qiz#lA05aI($}-#am|H%c+V5cXxl|X}Ggn}bc+aa% zVz#xPA6mi%r{qdboe?XH72qhP{oj20!I>_-%>zIe}hO884IPc#j-^ zn(K7W%sdTB_U@}L@C&Af5BI}iuP+AX=c3;=i;Yu0zyBtBh%jXwU*9h15cjk6e z4WoyXo3n;nUlfGP%Ww0SH5il&RjVN^&64Wj|Lr$yY`{~S*Lc_nzJy*3$} z=(wTSejBpwtID?%Uq%3bRstQgLLM41RS*z1(%R9U2A3`(Y-j;It}LxOgu_{n@14TbciV z6?mjwq%Ib9t~8PqQl1S&yet*=#7^9^n58(j_=KS67Ci`6WO>6y zEUZDc*aMqXmtT5!J5cEth@GMJ*~72ZEPMA%FMq@$%@NUA!`jDWIu%~bhweqUH5rTA z4uj7N0&H!(1P`8PS9e7uKGf$X&Ao&D>;bu%n#20mUNE_5hnxY0P+y}{91DH^Dsck4$K*6q4UU$?0#0BQQgyq(FhMg60vfUg2- z<*+&0O!gxL&-fG|yd4|PWKf7a_$L<#zaSu5!@SCQ2xj>$uy#o9v-)lA5rbM^g5x#? zkL)02+=(?(5mD|4pzZPRvZmk$Qscw|p`)lfHdtsfO(o{M(vU~g4bzxdeZT@8zSkMT zt#HyEKAl@|9C5XK(qlxAyq>qhL^8{W0|$l}an?6m{sz;?AXjl@6C7rBuFZ04MXAj_QZ4Qk#)!d9jkv$8y}gGc~7h zy+;N<)G?rx3?Iun(cJ?IwwgH<0TGdycB5#EK^N0*3%Cjs;NR`S^73wO>M3e*7~FKwV3U z7|Rp%-pyN<#Dt1uFyFsBmdP%_blIv$C)dzA{S>X`b)`WKBS9-hi74_!NCQ~&s! z`k25bl0FQ;=BZ`6p;eNZgQ0qW=6=S=X|#52pYq}Hd$v1SY5E=T;q~6(j6`E`>;U&( zftcnLODR5%np^o$@TK-)@E$J()N$!uL= zN1$@sd1Sw}UX?_(#g=r)Y|*{VCkz||Z%FNc{03%GUZl;O6u@f(MzhYK5uzae*k$x% zR0o|H9TOcep<@YWuAJ zsnjwPoN?d|=`Z&WSCSPjl&Fopm{5nJNNYVtsM!We z2k;%C$A3P}C5YQx+4?uD{k^>-4K|djTyHhb#A77`-|@AH_5j73m;AuOG~`|7VY_m_ zyYEI`7MJJs1jXfAea!4pmx4vC#_^iJJH8j$Ds^&`y&0D`?9&P+| zkKhO5PDZzcXwIipl+IXqtY+1$u#QP)X0B@FRsA7h)SPsU@|F8)HbE#(h&+724S3EK zY8TVMyjjF#KUMOY3X>1r!T@&xXm+-C4I$~S=^$`tCgjfX89ldUmZS5~LM?~D3*_#q zw9)-Vf}AX*YOB!ffLJAa?r`6t9x+3^bkN?cpP7BylXv z#&%paH~O8o4sg(Db>xS4Zb{8KV1PkKsiu3ja{w4-62J3@-QKA1Q=zy0Oy&Npn!7#< zf2hB?-aAW&QX2qX>E4KK>&g0>wp1)WH}{cITQEbDk^`x*0VNP}{lYnFz$ zZ?(|^1!{Ja3l;_N@vFzok=!Q$GHDTa(xS8?+TS4K5zKV6$6S&a7~rD2f}MkEi^op} z4Q0G*lTV74+o$v;iOtDnn&~FfD^@BpJw%Rk_c^fJv-k7h_xP8@sBc@ZMLRGX)&I`R zy+k{_-lEImUFPGt?(JZo=^ah}%c>pJTu*NkkxIbCXe+`-&vJd6MR}})lHoqxcFn#J zQ=x3wtSpavx)E|h8&K*9C|hArzv(}chQ|ueSSCNW@j)`_w^_NboUXis=gZbp8kOPi z6MG(}+we~!2eY7yk8y$UksN3*Htl0is%!3-TV;s8g7-=BA=~%Sq}&cn{Py^{du%jM zlCmlfVl6jk*n9lDAuQNH`CNroS#@gRm8$4t) z^;U_zYR!#zxDl&a3+EK+*#-%6zhINRS)en$PEi{BnWs{T+y)#DsA_b`1T;I<`8G3m z)lxyNHbnmsLr@z}t3$D#LbOg=Ix5QM=10UYl!NE5WP2Nnx?Mms+h4)=7iQ`2ib)OE z9aK-wu-SjN&~I!|U8he|Fvw)~W{tvYH=EcV^%sx7zkS`VcbbqrvQB@m8*>21@qVpC zQ(l*?yB!i8+dKr}TC7P;Q}p+^c|jgP7uVmqv#2*9>gm_?oezbT zX_w7EzF*Lh1YfT!LG-v47@kruRZf0ZJbC~P%cgXzZr6dLC9Y=6y{zo33*m9}7 zJr6Ow?`jIc>_d~rd_p$j12Z>x`bvOTewEGTt1<1;#?Dr~PwYy=Mzb(b2Yn(8$=NB< zC96tOvOt@m4ZYh>BC#(OSZ?}7dDmRskUjbaWJ;G3l63(NmclA=KJEc}PdS!x9Ei*Z zP=d{v6;$D1?EWL%-ItV;BEq_lstFWO%y*1?~90-O~W=4?4 zwJiP|eERJvrO54ldq=anZ}@S-`W-jrFX`NE%L^BdgOj7-)NVqf-BIwjx!`NbV^UKyHU{~3229ONZJbw3LO~K7x#elTgt8b znDk)DF3&4@SGc_3=!({X`dNOHQK{`$DIqVWV)QjAGQ*_c%O%nerORWb0g z!*hZQ>mc{&T_pOube+$5WkK}0f67p08LDgdC#opCK)9x^lslp?{H4T0Y<4GPqmTzL zxzSSJuUc&x8x;=~IkJ^~l#Z1bDA~!kpybZZFf>gp3GV21OM$RDiTmWPLhp z*OL5rOnS7jAq>zw*_E1e0pm2T{YhOvJ|fXn=e+YP5xSo(H1b8mVoY}eOJSyhWoiL&dGvB% zi(YfO1H_^(P|t=Br#ctFattB52-#`)L%Vx!hXL{BMYYZn@^29NhBf)(YxjV6fqC-B z*p(ygJlnS>9Xw3e=YR6NS&ugklSe&YHH44ZGSgF{O+OGUsj-BO z8h~si$01Dnsy$uGT2ox`vwbVg6W=H#rt}m>buw>Pos_F-$2U1`)s1gOX6qs;vymKV zJh0#YpGqymM;d(?p;oO!92JH%X)fWyr_LaL@Rs;GRG5a%`o(j+hsoN2VD}7WD)LXC z?J7zM4od~~XcK$U7pGARKu=f1!?>rmlawU`*3n#Kx~mD>Xv6H)4wtoHIX+tA@uwQ zf2}^)ULC$D@j9jYY;9ofZ^8EaH zj5-G3Kd+$t`Tie#`;>aU`mg#2jnVq_Bz4e zbLKxeCskUsebe9P8Ow2(1b_c3+YiisKCt%fy9DMbz{n^Q39{5$T+OI4lZn)6}p zO=o_!4ArD+3$ke^ROZ7c&31RDV!l7LXV-?yO;XQX=0ta2*44 zI-9ZTRI$1}KjDMbKJaKji<6*;ZWCl)%2Pnjn=YPE{(KqsTb}*C<2d@-KyYYT1l#`$ z21HTMaV{Wt11S3;9h|ZJ64-e!USL;=7NlLm9W(Ov*-DF9M|I^+=wD==%(7G;{^g4a zXnWrrF`jhB7`W~^>vdcp_$VoE*!AjgTndi2$PC7?ioOXh)%sYoS)Z-EMW3G%Q@!i2 zfuP(}h6NEaS+rr3x6ZvDEX71II#Pg5-|ll})4w3X&ZAABF*jjPPW^2zSJSv_&?{{Z zm#=QYV+btkUx>V{!8MX}3M9YBQ#H}_Qs?WrVM%+J$nve@mWgh41I>3gdUbBgNwX3jqP@V z?ArpMVTWw^<%E-%F2Iqz?&Wgexq`AsI@jQ_dP5AidL8U8bj>44jLl>aoQW~Xq>p+! zv0OrEnQk$KXv*5-3pe@eDr2E=I_fs#qvQ6EyZc_LR~>#DHuk%LYqGH~dT_{kFiWK| z5<+`S_E#DEnT{GflEh>Lr#rj4olS-Z20*%}CMb(85JpBQ(%VKk2(jLFn3w3Fy$))A z`u*?{-0kJ7#Qnp;%YE5LVu(>!pwasQE}QFbJFR_n9PO(OW0}I2yS+O?*1)o^2PgxfQ5jJ4jSB&{_Yln9RAR zvoFc~^)m3lDEyJcO0g1qQ-4#IM5xxJqO~fWhH6e@Ul$mMm6=yZf^D(L==fByXEHsp z)=5{z((3kl-$*RyTd{uVWc8?Cae4pRngaGax z$EdkP!8}$2`@ir3uAP3B0GYa-62%&)EcVQU&fgZf0qtRs$}>XVbE)b=X@KYN}IC0RGY$7tu4Y|%2Qu|4KENq zH?gIyYv!JcnV{eNskh~G@cGHF`%9E;gqnfYxIog#^IP<}v~|CZIf^9hvY>@wC8EFa zlY&`da!-Z6x`RHVMUtjghtT?Hm(gau-ul1=hVFx$nAB^Fg+o^7OpOk^5`>Wy-d(_>Xt_fnZB8(s~I!lA5 z%qadd9q<|2%7G6chpL<RKAg1n7B&qLs)%b50xFP95N?rCW5B}j zwz-Uz&B+LI$Im4kvXwog9?>`*KRIIz!&&t$?TQ;;c#Eq~eyX9LWs+hRl+e89zurZg)cvJKQelT6VPgFK$9a84?~7 zT{%^6fTny3)}rMd=TlzB+`ee-r@c$=lWT!lPU(w*z87+(*AK;%M79{FDwP(U5s(s0 z0Q?hf@OY^~OW^#;8BLbe)mEM92j-JQyjWS=zMCD3aHf<`TyKW=hg8p1y#!=Z{8J66 zZjn8#x&n|OtQyfE|Cz@5o0;$z=&EW{n<|H*j;lq?rUNG+;%4HiR#E0A|F!+e-ijn{ zmx(YAwiP$o2=8z7LN4}G-*l?}mHKL{b{+^kVZ<&Iy`M;;o>^OjY>`$DB%lKMbZ4k7 zotT5c!Q)!zGPD|tBRY~=8Lx^r*$<`>ThomQt^tf>bv1l2WhkZ$JH7F^wZRU?dc3mW z8Lnxf99*hmynn#xYc?$HrBT6KnLLSSa%c~aeVu!4xGH1x;EW@ySDZJxblf*ZF_@w4 znw$RFsnjC)xbnhcRP@RDqYjtfSMh8yIR&4Y2|vCb&8hQzjq+1$Po0yiJ?$)O{~1i! z^6v^<_hW}WX2^bDTXe>yTJ_^&xAGC9PmWrYy#4BO0qHMzYz>RRz zR1k#wIfA{ma|Ox4hd%LS&ItnhgOh5B8EsyGq-UjE8%Y6K@1F0WhY;PCJMzs=Er+59 zK+QY)QAqlW(bMtE`E4(1E7b3W`Ups9i}%V(At3)At#|efNywn0DS;>J+a-wyjC@_VLZj0dmn5bQK z$Kywk6NRx8&|_A4XZog9G^w89%}M+^Air)Uv`n}h^~GCcopV#Ne*iSvd(JjV^orCl6mo}5v z@_tUeDe7-XRH;!@-j6ckIB~OfHRLs?h>4$Y zW@*YhY}wAOzv_L?yp?f-%T3iXswbP7&x!CLi4k-}@LThQ)A6H&R<{zS z?9arp_?uIv!g)XkxMnBnM#^lz$}+FUvgvjkikKLy0z=NGtz48y0Z(R%79tw+kjn=q z2dyN^4om$$4T#A`#N=@Et%z;#oe~yb(=&+^udg(j@oU4N7JyK!un#3(oveZgU$1m^ zk7{q(9o)om6kl>f?W&i2r$>CUO4d_}aNQiYhZVdHm^g0otap^6*~om^VZd08_gxWRtFG)54SHg`~bRTbkpy z2ehOr4p?pk&p!bd`(H2i0{(iqmE{7!S@h#(GPO|+6_Xy4y=OC`ibm|jK>oVgb2u;I zEZc-F(X)tGJKv^p>NiPW+ymnK^DLO>)Jr3pN-Xe=-_KTi zzIMK>=2f=?9yxVVIF!&Hs@+B1Ia5rrJX|VYV6r}8on|MAXf~-((H`pNh^H$$%w2En z`q`9b57qP1T9r%qZ*Sc4GB$Nq6AAG|^`9i4wcCpkn54mkTSUaqm?L4REhcVKw2+R% zBIXoe+U}Xa3Cyo{m0rKM`k49ky!@MrxiiA9CTnd8Up!MZcnFKma%n`7kfkj(_37EnJR zG{*j?>g_rHA*#VI7*w~`NveI(GbZy}LN3PltHJxi`ArW@YIT3C!3GY<{o$>Bb5k@j zQ#QD=q*C=sddW=YoZ)j}2Oud;nY8?)|6?EffU+%;x%B&5ewSc##8KT4skHg77y`rU zXvI8SHnzk1K!RaCfdu*YhwUGFQSz_9&5+PEa#*{nyd(`HmT($i_4tvBze5gyjbY^W zL3ohdNtYc@4#L(CnFaitt)rel6P90c3vP~%K`txudd|2u!t?WA00S+v;$1`wK5dF{aPcO?4VBDi6--8Cf5P8?Z}qi%qWWdc&1S>Z zXOgmJ(qidkKVFrB;T8qoLhF#3sMQp5D_{I7YM%Q6gIF1EzFvst>wMM^X*L*m)0uQE zaueBUQ(+Qc&VMt25wJk31b(^-DsJkRz|fc^Kin~}$a?;#68ca?@+dLPs-z_fx7%Jf z?>#vumh-XuDQ{bA+tRg}GHun!9*yi(jnE)ecy`eRK+&9U=EkwLsZSIOH;`AQ?Il;u z9+K{g?90;K1C}UBi6QT>tVKDaUwTcQs7=-`FCEZ>2%FPhW)(=%7184^3j2ERYYF&1 z=8+pPc3@BxJju$^AJcXZ$0wyx?)pAkyyVcAN|Sl=i!IX9&vwz}u+3ROgPwKo702W{ z16Ss#N^zcCiSMCSz5K5>4mvsAdUt zghadDT1_IRG~$GlDl6pCRXzb>?~AGe3(1_6Q9tU>&ur_}Y`dRL0b6WV0S?}}HIveq zkY_*u>X4!Fo(%?s8dHG9#9+7Jp#-mNs#mp602wQ{GwYxttxR;l`G92=2mZKnO{Xf= zjL=M#XimVyOXBPN@4?W7u=Vd(ZWfLE&_3$|+@Pz-n!dUO-rxA$B?ru`-n-x^siE`% zaJz(ZVD**8u*q*JI%@*sj4-h#nBabO5}3l4y;(Tg2R)mSS2k}p2t4Hm*Y|>(Iw;q_ zl%*GBCDlF1vqE>0R97{!iXooom%GDwC}nV+TQ6y3`9ycnOu4=>Do|$|q?+-tYg!H5 z>n-8iAOPFisdsA^aT*JbOh3p2qDp&4m5qC33to}*We?dSwGjSZ7by+-OIPsWq$$#& zdDEQXk2L`B1pTWECZDVbRTtqgCbO`Xt*k9_bfT}ze)+8(opX+=2k4c`2kd2yZEYD2~YyAU%I_=hOiZ1P}0u`ko4JDy4ECf6Gn5a!pUMG z8P~r45~Sn&P-)->-S1>mW#5Uam(dkuT*gn-waf8Htk`Yk4@>+gho)d!56ad`|3=`yeBSh>R2)_ zz<>3(Aq2rYhk0$@D__4!hRxhsuo6>5KB!Z30s_C%Ekb(>n}x_XHEUR@Lb9sx;~HhS ztp$D!EsUxAX8KGNvGOtf^c}*n<6($+hzW-D*M?GN)gNRl8BwOm^5VvU3?ak|YTjG= zDj-od;U%d9_ZAnMldNfOZ*7N(WYMo-3VT+EU%6nqFijNfQDua!R1Oeh9WC9{>0ISp z6Y-o~B3yJAy8|zkbbY7x7x*CAr9c)%g>?cm)%V#9wmu~NQNa<<7`%UG$NTczRVfd3 zU0uMHzYaApT#8DLw%-(RfT@B05Qg545IlJDe@weBc z7Ho%yPkrV#kdc#{+!Tma5IZa3HHgy1?iy|S%~C|+4U##7lUs<@tx_+!N&8jzme2lf z*M*J8B6BYJYFAK)ziOa35zw}FYU%1knb3kb1Jq8Sa%>JaUP5uYQFb&)zWs-N56LnVTbN3_8H-f^ch(N?@Hs@ijeU!?F_h zhuf?`k#!<3=R#YEZKBkI6EY;~ZMuz?@nZfhlYX;l+5+>-IklNmqWHQoJIJFxTZ>Vw z9{KEpp6NL+&^Dcp+&ypqTBfH8GT17k-NZZ_y>5`a2V68#{vj~k%@rhJUA9=8!4zAh zQ70V|axcUbt2A(TPSz?XFE4){R|#?iN0G<^37C&eT2v*gQ^mfw5VAbkfjGE zD{KqA`c}QU1Um4N(DmZy*;RzK_;*C94X1@oQS4^*w@xNqizaorzah!KD=ag3s0`+g zk}l&er{~&0W*Z9^XcFPQ#X_u(_j!2X6t6xl7$_OO?{3@I=KvS=aY`8QX9mq5C`+Xt zARE$hz}W=YnoVa{k3;GXmqwb4j6Cy@bgtH@+M|x7fx*)pHz2{NH)JiVBCT&FmI zAi5}^&P?Uvs{&xsz}>G+&j{N2KJnED&~UHVCvP7bsD3PaPU2sfVC#g0B5$f>?S_ZTm!?=7YC+D zj2sI+(z8{l{%(g?zv_h8*DUv9`_JjWGxbt(5`R;Y`6s4yYt7V(FO7PVd1FGW`_*%Q zVD>hOG!+In#u3vBjpI1c27eVR!ehka_INiD%`F=cYo8*PeHL@G)c$h#oP@fqgNweF z!;~<$-)S2q$hnpxIb#yy;ESV(QH|tn5j~MP9&H?phZ4r>zAy~=*0)5)53L}G_rnOG zthX`$I>F5W{|r*(g_HGVa?|?nyNdiCp6g(8Un;DzBXT2VRRM!TC5mHPmogp8XhmYm zVk?I=sAA`E1=2GkA-ukf9e|c8{EIhu^nRz_|D_EsZ^-ml3wj;F4USPMbM(DX9}Q@c zJ#O8vISXcz{|GBR$LeH%WNMYgRFek7$A}}mgSk+O68s9atP24;n1pRXtR@3`mLL^a3pcr`QzM<@5VYvv_)A9 zzY@v|ppFd(=VE=|Pf?|oDBmlb<9lHqB~{;->c(P}F8e~Qfip9#XN}A5SttPk1y30o zhXnK6$^7<}?gUh#+a=j*lP0f7Do0negjz%A9Tp7jTHI<>LC~Ehx8TIBFtTNMosXoF zVR)q+uOaGz3CI!R&Q78g?Coi?6582-yK&-RPk&EQ(?< zv=e`Ezb-%iFyhFc^;Hd_}sVh2Ftp&th{jV$Yc&h(%6{qKiXZDwV)7Kd9tVa z{>#u$!NF1!$$gw-nG~w5wS7!-EUW#$d5uiLr{7K+e7s#1FSjuMT}$kacN-LR0R>%~ zX)4II^^XL%fs!l9X9?YmjINX7l#W$C{$f1t2KX*7L#STy-8!x|Z_NJ_MQHMS;!T`! zXqwXC{OL)tk^-d{C`(eYG)EE2t; z6v3&GG{!}}cUM z0LggpyE+W6G5aVu%gw4d`Yd2b!sTFcWf+~&HNZ2C9rq*=G10k=Hvfyey5+h9udlEE zMrkXBiBerEB!ivTcO2KSy_VRp>;0>m6ZAR@w8D91cGhj9p*lp?Ah`}BxCbS;06`?Z zt+>ua)-x5&_m+^6K-PYcbF58J@zBl{hw)!nZb0DJni_7Q;5bZDdSq}o~R|ypasnxfQI9kFjN4a#i<-{Qn9x#dyr!Nt1j|Mqr>Q? z#;?GGAk9X!sB=lgP5ku(pdNVevUx(LCs*gz(!IBq3NYc0knB}a?=)Z4A7``7?@1sd zwnTBVByT#OX|Y59ZKPxrZsndUpOy(W5@_k-Bu=OjjQ`^PfGhhiMky)R4$D9 znWUXl5YZNWC!I28H?N<<4#V02E_P_V|D{Dc+UqyD`N}(I^vaUJ@tQ4a&VYkNaazhf zi>3VZ<(>kd9u>9eh))1VpVKe#<|>WV0El2FJ4+4QWUPA%gkuh5;uT-w`-$feJ)%~mE8Wn;f!6)a(;t{YrotL;M@ z*~wyle-hZU-+&bBr-YUTlnySZm-a~8!k?%kMSW790(c#UbRs?0F9$1|mSbZF8Idop zp<5CYn0;77m@TbbzCaP`9o-Q9DnQ~CNM(bVTnW{@FKF?mIsSm?cGV5JP1*S6$PP|V zN@HW*swV2kMYQeI;l=Ikd{HB@d@+`kn8<+K2`8_iMd%_T7oMwX=}&M}j*wsWKrOOZwyYk?H5$r={ko~$g#sI8+ z5pZYFq*Hr*7Y76s1Vj73uOGs=&6y#X!8Xr#;RInaJ>&>pDW(o zhM&u~H-Oud(2>XoiRW=)Vosif zXS#c`cy_eWCD9EaqThHp*ZhZ~m&N9dHA6D1dJ@X-7{7&DcV5*=h>*bbbwHu|q!qlo zv^&EGc%ikA7jb#E=ym7KY|Qfl|3lIH3}&zV&~h#P9^SsPz`XRO-DLCdJ0N4Q>7-2= zqTT0HIWLG?V<9K z?Xiw74)uL~H#u}x{&957e)>Qp1V)43D^LNq^ccv$s|!=AHt~$+J_o>Mn$%6n7Ow-j zWQckG7 z5B>dzaffx=<_B_nyFd8!RtwXd(0#ooDvRzE{uOdRl0q9v0$+fAl2n~AoybrYM`5US zhC8&9j$(HSx9-9uLZr9ET&Q?)1uN!|7$=_pu@6uWmHH-deawEG63}M#Ig|Y(cC~F1 z#SY2FiP6GNPBl%Oz|V(rS0`~Cb#XL50|U!pCMh%*j-Qp2G$jtU?KODRfA#OdVLDXi zr61uhYozbo^1JzcU@3pNQ`;NppboM#zs9p@=E&a*T9Q40T96vE>%8^)v8uCTr+V2V zlqnCpYGVJ$d9+Fvdt>Md?9xE&EM+(%7x0nLF(7TDb-spMc#D1wDrwPVh4qe*rp&O~ z#KQLnDMaaGwk1Bg41-e&m=MPGiz6Q9b~|%jA%t=3`b7l^HC`=Waj>QkJ}4O!RcYOq z8B)+=l~=lzSc?bcZI}^%{)0kLLmxR;;9KPB+6zn@O|lFAY@Dy?EcanAI?hS>+*oNL;5r zk#t=%@?|vcQcKtv3Yfb_?f@j3;AyF3B?22a#jBuj^vPK17M2AAFI+95^ZKIFi*9j+ z1CPfR6wlFC7ekQhownLCIGPpCJFef~y{0wnG#$imgTbK^8nn7~MgVT9syXg z@h!`I#{d2sjmK=+#^pSqx@V5R-WJPCi+oVS7%Oy)O|;H@EqI*+uL~g698KJYI$x|mZgD31=x>+Lh8{a}_M%_$ zn}nZ_{fm_x=h@fuu?OrDE`PM~vav?#0|2sBCz7 z0Q<7DfiS_BvKzcMaGR}g_2H%HXN3Xgk<_;Emg(5#3ZYYf%i3cY^uSPkn#GXiT-@yS z1pmhE#ea&Sk}fGpvZg2vj+-xYZ){z+HEsHd-WgiIDV=J{aWSwBcbtx1{|Oe(Fz4(1bwwwYyDvQSMMDhlIJQb( z&+60<;xv{XnSH@Du0i>OIiUV7Xxv%jCaS zvO3*46_G4+KX&mjyG#)z>p`B8LgPJMjd;cu%TKF0t|!-jISJjGNa79MjXz#fp?Wgx zXqBLWv1cMkXwP*?lcdt$<7EPYAp%qV->WFt~&CZ$uD4jim_0vib9mSe{yInoW3#@VF0i^@L+ zvV*&=2h-S2;$f#iV$48TXUyP@`%nkR4>hW^tSfrdkZWz^RY{lwnqnjwpZ&9j(NCh= zC+;3Qy#)5T6{hk{CSYwQN%OcRYU*~;>R%SgpdxVm&#K)C`WQZQHWV~xkXVx!61iUF zG4F&)MZIZRTVNk`JjG$Qj}Nfd`q~A()9TOOFjV+Ifm@4=q+M&jw?#X4tIr(nVI1p` z2{k(As+F&Yrwcy3*PjJ6A_eiMiNuy{Kf*Zx7sjVM4-AIOpMf#y(Pu;mGxS;iqI+9> zue<(1KjHrFNqcZ9%QbM#|L?(>vC)cQ^zY?T?4=!+B&(kEH=nkRsl*w6lY^`!tW2@u zH~F>>tVP-pP^XAAOrD`w2Ng+XL0g$X1fxFBSdIY98tWL{8H<}34aJGrUM&7@Gc7MF z04^Fd3)-BD0%l|NxQJ%fbja*X@ET~X=ad}ximJCfcTu+!tOpG`#H*vk zl6l{AaN$A*7A5}7x=?ry=)lAsjQ&;4yG+`{{={Qw0y@)`>v!+2-@!g8y+YoT_Je~y z%Bk@tS&n#2Fx4~WY(H&o50{UHSbQIASh8POF5MfNtB||MAQpUqN!#>XE45kd2+_Ma^YQ_hI+F zK1J#;t71Z8)!FPYy~Gn5Svpv z&J~qv*7DVfYRHi?_ZqPih5c3hbd2|H)@BXi+*7&bbpw!}W|VAyb2;8my$c@G>rzKV z(2a%KB7cmyRv5J^U1dpvfGx>GpiBjWE;8TSFV%_Jb>WjV;ht;$+R$HMac9nIeV}w6 z7^FAIv?ns%jjjn7%>TG8#qM_X|AF{wc~8p`2KGBd>DAb$Q|xl`{nWr<=C}#E?#%4j z)v!=Rt6kHYH9&uF`K;sX{{V3iTx@Z8AEnSiWQY-HgKrqkco%)PWS^7epbZpmAynFW zJlX5N3r>uBto((r^wa9jX*5|oh&3@OH6Eh-Zkv7lpylYU@I8eL#9Op1J&d|==#Lv5 z(LK9X50jQK^;r55h$f2rkegQ=W8q_F);RusKVJE9<+hpp8|&&hqG+40Cc#&jygqb6 z;V0N`+Z+(|r;-9#gC$3R0lb*tn$c5#R_xqUd4C6w9;sdo0_OjLEEOOWC^-s>RL;;v zh@Qxiy$7}yA&g3a}1B2uRb3UbJWVDVz45`Z4~~!8M?wDrvFd<`LJ{ zD$LS6VesL9J}U&vHJ7z*X~v0Y~uaW3Sj7onQQsVQJZKzqSo{7m>w8si9c3x5U<3zsbZT{eG&D7#zv z;oOeykPDv=>5dzsx-Mkz%-c~4RZd|c4xk}q>W>2IbNRyjwN*kA_c+9=sU{vW%k(W) zd8Oj#5CIMXYuiK$64;yzSlywT_{O&I8hbUiT;mWv^xxF`_>HpcPk@ujlJm^xkPRkY zTptyP5|5!wAX=S=P}cJun?a6goqRc*6vJ|E)C@g%%id@vSbzLPE%2ot=Y;kU(Hgx$ zijv^rXi7^DpARPx-Vv<#WKRz+wM~CO!&CZoALIUI(yRq^t%)#?3enWZoJu z`;&u?FuXqjsRg^U&Nx~k2*6@Jm9*jd3r93vZ1s#tCZ$=daZ^jR$U(qqb>SF9b3jC8uTklR&e`V)w3ly>$llsEv`Z&ZrfKeo4a6j&3*sW0Jo23YrZcVZ(Tnn- z#LV*O@s2mkCs(VTLbzH!yoCpj&lIGHmu3)mwg(p=4^ErY095T?(k~CVeYT+RVKy&g zUwD5W`S75>i54t1TZpp@TdS*YjR1!4#EdkN`DE91niUJt!voM?(+a(yYlIq4WBt4^ zyl}1gw(iXk=N$$W+tJ>r$^Ax^heRVVl!vH~yn4lHc^bnH>D4Uek)6!G z#gTqcs!d!HjMLcsNs{!ZJ1#pqKTCGPjLf@1WNDcZGGOEOLBR<35X; zZ~rYpftZ*s2%+746Pt=&O0ImyB)#Dl5CKaxbN}BLe+u((#Jg@NM=yeCM<*iK1ifr| zH=JxE*R$nL;MUss4g#a5*tc%TY~+!0P0PprdO(nnt1u&MX`Asna@f%7#BUJQo!QWy zkH*;G9*_3z7oQnO5!d$tny2E9vx%Z><~R=8Su{8P!qBng4=a1XR40 zZRV%riKHScVA>936_fl?U|#sGqECO zED&g4mGLMB5?};xO%?I}#kz=F@rN~cS-M`=^qqd=hr>oT-Vl{W?HD-SNsFF>?v2fG z&avm_1+%vzAbgo2{Fbxye%v$wg9Zju$ogs56E0Rf*j&0wTWwg3N_CT$O>aqu$Thp< zoRz~N&pUaboD`=D6)YgJo#8&`>$9F{bgvp(h22%f1hLnTEvzq_QRN4FEWZ()6yd8Y z&uMbhh!J(TJ9jdr=PX8sKJk*<$4G=7@d*n@oca`kYw3>4m8bSMJX(C2`iFkYPy8d+ zz$H1}cBo4s9S#3x^7No$r;K6qp#o{Z!ZR)g+R{h@tau)vo33uMdoIsd><6O7dvS~xMUzK$G72}&QavB4|>lGn>^_k#z z;y>5@p3J(yntUhWJAsCv?+HqGBDTDhjrpE>vb@vK;&6Oq>u;)Oc6eX{p6Q&{%!{5+L=PlM z^RIl&cLJ>I6xUt#oP*}SC?D~6W6vyvzPj3Xwz&MTzP1G(P>1mZ_M3nkvx`!rXOIX4 z3>513TAN;%pqOpD%)N{K-vQY^7?7~~U7hV7$A+en&w|t)9zZ8Z4Z>oh=yJ8pYQp%+ z4eyp5PjM75hYC8gsaspQnog4_fuwv%b0;i*txOn5#Hu>Sy3I!jxx(p9+{%!2ZLz&z zbyud=`W^^iaR4 z;1^3Nry+?=heFmSd~?UW#h03fTe7FFHve0eGW&iQy7Eo69Z9L$_R)spVIYhZ%e!;V zt~y{X8X?EybRK_(Sa)lz?Tx&H;J951M>NO<9w#E7IED`99Kgswcr&hU8t%&%Mf{qR z5%Izq@Q~z(&y0iJ5CHhIz(3fV<_s7raqsXK0A-4vAy(LCv4!`rXJ*|?dBiwJ-1E9n zv+3Ber~u!ANCR2%p?`w=Q*r=ja0Td3s~Q>Bt>XsL+1(w*NbV0|iih`GGln54&T&0$ zG{*t?e0GTaH}Q<)4NJJ+=)(>8*AmCiwc5@wBYLL zqus0CHoQ0Nq+(Vv$tt0Stm^=+7&RPY`5g|!(RDPS-^q8W+i zLh2j_Y@G;c2HbuaDH(DdI~N#Hb@M3@D-oTbQXVGyhQ(aK=>LX`5rbljhrGuOo#fmk z?EKO$N0Ao|Ba;f24f_}#rf#E?YoPw(h+fb7abI@+nKP!#)Z=JH`^mHLAU%PMAx!kv zs=Jjm9|mzVotGUlq4q=A0t{Zxy2R>Rsf%W=V0YG_%xEyPjL-4S0-H}C+2qe@;v!Pg za#?jYn&$T|nePt-B2~$CTi49}RLsoZ*x;Pj?ZSf;_NZiypZJdTlXNip)W#3WQ3hr$ znjSgk78M9^9VjhRAzd1@j6B7lP1K@f<>KZKI<7>nlf2qaGi|v_6Y}c=>ZfCce(7-4 zeoi~N6ulw`<$o5RG%A1t7wMEcL=(5s$F8an58Y{n!FXfA7A>NKYy0dR*!&UJim`Mp zZ>1H*pB_|4dHt+vFX@Ss`n@#yl)=rNCVmcCos40m0N3!qSUTlp{3!C`MtfkmBN&U- zRKLyb#nb26i`TcMRqwe@Y@yu%=+%8H>37=QVPU(-Rq?{$VOR2OaZf+17XA!D;|fMp;Z@JTcueTlNU3L(kPd3{DC27Du+ z&$9ogn|A z__qUTbkhwFQAH?xdrP8!tW-plBI2Y%3f`>UEInznvf2Hkv@omeOKGRieQBCEizm~P zLxBcBHhP=_K{#Q1oYHI@!E6dK#IvD@ZOADw5mj8aOZalJde;v`bqA0`49A3!rc*Um zqi;@$X&Jw7PKFVuRpQ#zLmW7ZL$lAb>Hbzxlk9SK*E_W?;?ENhdAJ3}+Pz*bD^q_A zP(XD#171rv?gwu%@+DJ8G;SspYpe1)D~whRb-J_)3dqwlzMw^gks8E+<(;s;ai>T^ zrft(5zGFTI*yWH0S3eZjy$y87c$wBK5AXCERqT%>lhk?r8V*P4rHUwoCuA%dAt*%df) zq=l;YS;v!TfR^xIXD#>SE6)wP38&{PEFvwA_d~`U3G`FoQ7HbZ__OQ!>E_CPP}*S{ ztAKX(!KyNzPgqHi!+nIl?Uj|@qTL@eOQW0^-A6I4{3R~;nbV{8tPh~8_b|{ z*@TsTG;Pvp)kRD{6b|h6!4Y2Li^EMOCx={YgvLb^VamEg2c|(tg zx1a^>J<-~7yzrXD@%~uOVCjsE<%6P@pNc|vWhLIi=(Ra1yY(^q*h4AMn>x>+ADN37 zvNlFzZKYq<-xQl-AYBlte|LLJ0rImPhDkVBX^%7-cgG?DnZh!1TR#z(vafQ{{ zZoOU^Zg@;~{zX&OanmmPHy)5I3>ousnPv` zdYYmyv|A|;`C8U0UdIV?Uv|w$C@QfcE+)e!&#s2^YA%6)m4#MZbPS)pGxrnTb%8rF zAsh3HGU(ZS0n)Umqtq6|nj;V)s2`qp|DS`YU-j{4$UXY6ne4^vZX^8MNj+)w`Zh}q z235;!u;`!8OV1POEs`4JXC#oh`DYv(C1q)l(VW zS{6mB7R$q8HbHiC$`mWBV|q zYldD zqt;?^C?6F$p36$BR?M~s7VuzXa#-Lht)>5K!qMfr&8f#GV8VDK5(o4`7Oy5q+QQ5g z1H4mv7=_^`5 z%!37gO)0)ul1;%L5_HJ~obIe|jG+Bqe+5gA#*P64;2*98GbgXNxwMAtg`7-18gU}9 z4dUXQuz9;2piUY%?XZ*%6v6?sE6&H0!6n#(}u^&?J*?-id16a${v4#0I(0u-KRX~aJO-)Kux zC@wq;*{~T^#^|9IbD}JsBpN`}xLzEBJyJHt^oKRmk<#GPw@&Zfotk~%H=wGrg}R2M z9K-}7J!8Z7hVY4snt%22qvsA*2s~CG9eMH9?J~^o_Keb`Z1=RKBTnm#+O-^1gP
cX9;dBjqJ>L`0ote;B)d@S9i$o)2(U$aw7!TIRl>~D&59a zp{=b>cyEOCpAM&t*YB5KC2s7j!gUxb_W7S2JQC9ncCn0oDtdR(Hz|Bp0omk-^fW~z zh|Uoka-#!kPDECJO@aR$A{pyrwj~4LUZLaRn-jO2tQdesa!Ja~H}f7aF-uw(JbN+0QuInR1MB z1*^jJq9tDZ0wwBHHgIO1Exk)Q-pTJzd0gZkP)A>zLiDTUELNGYWCJTiKjT!qYae&H zyBjes+YrDT`;5%q5hlY8Xg}v4F%6(=Q1AZ?dra;~o;BumrRT)zt)+s;E?`hU-_qL! z>!1B{jKir9NhOl@8|+25oETZn0Jryo57~v8UnOz7!1k__-`V@t&%_8|4u-v$51xePT6Z zpbX%ch71XTJtXPWBPT5;oy@;|v z70p<7jLS9tl@jQ5-POV_#4gux?rEZTsJgc;X#Zg;qkfo^;%6lmRVo50QKpHH-#F<|xPhdA0TTIb?XFL+i<*d7o=K;L za@P$+0nDhXHwB7IKANPma@pg_MMeL-N?x*Bi8$Hvt4Wf4;+;eXR(DjVpBwdy`Ir{4 z?U2pxJ%PWO`<8u54SG?fB<_QkOPiiy>+c0FpLSG}JMM)WnY;~YOedMfv!7tl=D0&9 z?1e|5N?Ua+$^BbP)s1uQIj7-8%-ObAI_4b&C(KQ+f-a~(J#o;d{&wKQ#nAH&bS3QM zeX7t{qDGV4SZXyI#pz?Cw@wN0%`MlweyQ;>od zn3^{}M!e*EcLM#4XG^cVqvlEBhMNi>R|Z@5xEn}^2`?FkX0?Jx&hP~uhf|cv$Y0eZ z_irw}=JpiT4zy6mKLjqZ)!Lsa`4k4M-2{#)bKeov&gK(>*TZa=EuE+Ec}j{+K^9YkXQ8Fd8YaEPmVGJ*ebnlOM}0u zm}|^pmfNrj(Mq=JEzA+zB{|bL)0mKha0RVP)^o0Man_zs61e&lAwx=rZ5)Sj-eFo% z-Yrf43VaVB_FG=LToTK8cQa>|O4tB@tb0 z-TGqooX_6J>*-1_XG=F@!2*=hahg1(%gR9v)Fqh#$3P-`7Kl#;LBAx~SMU+I73qjUj?R zG}vYz#+&9ln}@ZsGp?c4O+y|HWJ8hG+!=6skqVxjA)1A5DM|(?f(_W(k5QP|DBTr} zl9Dw3=iwh`wpAM;dkZM*8Yrq2a$OBKyD3>Yw-LDP7v`g6uhR**LD&~S3#NFNJ9!7F zDgByBgI#zd%wX|rBM^Dm^Ep3lzC*4+{{nbG1)rN|pOK|`!(s>yeFP<6Q9L21FsnoO z{L%i@a}j4@-pOq;@s~l{ZQb`{-=5$3{KC&=jUfZ``OZfs9XqZl?tf!MICY4!)A`aB z$D0BjXeE7dt=!)$`CeGBQ!2B6d~UctbS+2C$Aa$pR*eP@%1cMp+$^_DZW!%OSI!t@ zN8p!Xz=K>vOTMAjR}}$Oj1X_+EsFS?1w)_=J)6kS^{EtN&7{EK{a#R5h2~4ySx`e_HfN zXH>RD{LI{lPWY0`W>S69V@$d*Fr*6gNp9${wf8ugrMJ=K`=v|)86CRQM(>S^sk+^F zYv~-kD5rbK$s^;!8S1ZgcEmB2bc^qYZn5VJ#`ak-A62BhB5;eiE_#|xH;w*i5zK+L zi|KaT(VuR~w=e9|>GVv$s8^g!&?$-S*k`mJP2%+IccQ~n1YCkL{-%W&j#@9SL(%I+ zW2jYpOb-6#79Hii_wlER-T>+3e+z(U+lV%uUWsdrZP9bLzCGb>Fx))c3`KO7q_D&| zmJ}xqc_(U&APt3IAm^C(rQufGmh4Om&Nac~k*CbQA;t;le$x^#sQ52S6Xd#XIIf5yZMuM^ReZ0||ZlS&C%d45e z%jg-#g_M1Js;Iy2e)I5S`(5Lx-@bd=I?rCTd0S}LxlZk$%~Zu6+Sm`_+eQA)`sc#h z!u;omebpDt2?awgC`)xgXL56iJwikeWk7el#H#is8x_chHJI;G=S9Vj`o@orNxOwe zCRfxyEctCCCTtRgsgaV^1A(mfpF8nCf7iCcXZR`g5YPDMSUi(4hUapn#7(?ZEbaLn z$+AS0Sut--+^IVe=;Rf?FNE4pW<2q&(bW6jVBD-h}-me7o zn05NW(BQi{1y1R2IUF zCv^LZYtfP1TbBr`8e#kWW9QnhsJ|z9&E9jx`(Fbat(Sih2?9um6CEwxb#g8)edIk6sHFbGmWU7Ww3V z+dszV2Q$r|oEt05zaHYH|F&}mo^`YDl*SHjGWvLD3*iO+5*my&V)uGtZ)ky#IDWt% z7iv3|vdw`BHiW^;&n2cIQnLnRuRty&nj7Q>R( zZgtxqVs7pEsavAY9!wH@|2u#DCyxM`B!YEDn#7JmGEh7kUP$4)NvIXjhIGZs_RS{o zgUnBtO`hWxS1vw@Pl(onso~#gMux?0^ER@ROAqa6JDP@w?q$?;J<~0@y-@Ykn3LSD z>)Y9(kH)Hw_qr_EwD2cr2XxajI>0`oKANQ}Eec887XS6vL-S zMYHF=+QW|?yl1PdNw#oCOcL~mj)v;sNrpc958L<2l*R^}ZvV}PPW8hLFE_1S6Ly?T zs<=&hN>GM2TkLlU4AThH(lc;ndyb${vnjZR6v-k8_$@ffTi(N_U6PmV{5rF)iTWS4 z<9|Oh8_N$^K`3^%lZdkA6yK8}no*Rz#5ZW8B!_Ja{?NJ?l<1@QJ4X;ebYdvm!#^qi zhI0t2?v0px;fw!f_w9V@Q+aSf<^dX!oU4Q46iDz z`1UeBe_Ws=TVN4qVy38dse1F+tGG_1U4H442Q%Y;8UEQj#JH4jt-QJ++^DKxDA{OW z5n4z@4u?0s%`_-gIyg|C<#->#HB9B~v}v|zZtsi;ebH`Tf1_b>={S9H<4sJ&3mu4< zJLVvOW2FOp=Xkm=l3ptM0!A!2biuY)1>HbBiiM}v-UrI0|Mx79ZKIIkw@`m_WdM?= zl)D4Q&k%`#-;L7r4TaF)ZNtrL1Rf#}8w^!{t_A0sT9~%1oK5`$Vnmd!k5T9CSeq%P zWiKwhsVDAsQ2=f*`bC?neD3k}=v1B_XT`T~{@FT$Ij%}pNN%#w8J$|e}Su4HO=KGb5XfE$$xKZdrDD}|@#8+b0 zOi&>_GwfS2_Shd861=m>Y46los$N}FufN;KRfM%6U)xlzM_Xrr_j|PZYzcU1zZxpS zGy||&iu-jY=2z`kUIjTBuaZVTarg)BxNGUP`CuQfaUXln`R?(3k3J2~$<%~9c%#*P z)HyV8$wZCWK-QH2E%n?BYGiH`f7Ur=KMp^a@`QK@9vKD^JeuB*7eJek{-O;jSGtZz ze#Z(UFw%QLH>8N6e?!jy0-pc*9K*^;cGgl3;WsMaF++-YRx4^b1`bT~J&3w<3s1}jX`-)VliF=;$Bb%by9 zKE~^ShvP#3kS=d$G|&6co!5#NZh9S9eqbgTKW$u054{i>8?#1AnKyR&?)hZpPBMD- zh-Vj3yOc7SO?3RK1k<0-?jGOWUO6G4ki8zw^4JWgBc7~)o1GU=ZsVJg> zskhVhn`Qpz!H#ZIqXLqe3~M1gg|a@l;dENuv89tQ;ruSHj3C|Ia(CK-CyXPO)vWpm zN)$ohU*i`n44HDIz+W|R-1b{bubr&a5_Y)+p{=h#^o%^YkDbt-n4|bY!}i^AkJUps z8is^ljP9g!1Qi(u#Z`|31EAOB2?exWVeio$AY|klQkZB2%#Jzbz5T;cx*%-`n;W42 z&DzQZsdbM%JFw$m@7S*A?bDZwlOj%$`nV=kxQ5*qZR50cMKPhv z&STiBZuGQwi2zoT{RMk) z2X)ae#*#*@ktSRHWw%M)9RNA?6ValDnp%?`ZXM`H_*U__g93lBoOL-M!lAi^9wMc0 z*ndFv0*oyD=3&Zu8eUqoeL{)VUmXP0*Z(iph;CF^PtispCd@Wa{6NZ9JHV)?h)@;K z-^Eb9xA>0=6aj8Ijd!4*{Lg$1xGLuErr6Qf-^l~|$DC5gTO;)_BLYFEPNgpTOV{JX zDadxb5wuz5)bdvlrr|9gVVtdrGxyJ4_LFvLo8i_{edD-%!T|tleQ19KVJ~jvN{b5< zRxL0NC%>Mad3x%0IaM_$_JDcyV!w%>>3eI^RoGBjhnZlA?2)aT3=bWM5zU)?Rdx=% z*y=TW%_#hNkg={#AtK9|IWEJl@oidvHYV=i?0>XAGqQ&*Nl|Z=A0gnny$A<)_ zABJpQMjaj7a`}J}o9*xY^uGo~rh=2CvPG86)Z@01L5AI4Z}1tPX1#9jT@h4s6PAkP09&;bwj8c+3eHdLr|`u z{fTd0WyDbXZ$v-rDF(k|f9!LIfSCJ55l?oNO)jCeMq8}CMQOd^^QB4+?A~1ktdawW zA&XS%gA?U{e>8z_I|y!7p`|Ld+xcV?QUc$Yac=_*wy(Y!(D=Mc^^qc?iZr!Xb`SJ* zy|$^20yOmx_r{@O1I%fxgp*pM;}ud(Q-(Qg_;jLm-KTRZ)dW*4!G}#zK2(4~GMYyb zQv<-YxP__}DRuczqWm|Odj6MdbS&IMl0<^}3G3=Bq<^rrK!Sxau|J*`kKz%LM2f50 z9O(81)IE(r^Y}!v7THNnd<;NCzRw6ol>hpuG_}G zQIFfrO$HMu&vTs1Vk|Wysxzb2z@sTPZsoR@;)?mC;cJ1O46rgP)`(H9>bww`xn_ND z=v~#%A&ub=^~id~#ct|TgAYk358ROVG|PZ$M*gC0KlB8WpU%#Y{rq0!Eui{G`C=kT>xQ(K^S8IabDz% z5e-D)mO&i2s+79LC=}t{;PK1k^fb=X{{~?H15~Okzrm{B9bSfBwvOfCHpOd{tq+@s zd5ZWwW~&F}o+ORekoH<4;5s<*ivBNZx4x{N7b*RTV~?i>mhT@|tE#tuNQ8$!x(bxf zDuVgrBhSHc_*$PXaF-`&TfMDKi0TDVxCO_ma-(1eu*k*MvAX5|`X>n-A!)!n{Hzm+0)Tdb8i}~fX zFCzsMl$Ky`+2}R*sNdOL)Jgr`ZZ!P`6Yjs&Y(gJAJd&GyvlVbjtO06qQ zPGrabxXqn{Uq*GI*FSC)ctI!xsCWn}j*?Ws`8EL5WeIUkx9~=igb^i_%!U)Ty}qGc zu6F+)1vg_&C8qD~D2(~jo7+}*Iw3(zX`F!j!kfxgijrt*hVEW1wY{+>1{?oi(y#~~ zKWLWgoM`i>b%+XASvOt2^77u#J#>%6m;JvHw-yu{?~LZ?OBzV+md`b}jxNOf@>lil z?xg4mt;!?Zr_aZHzI~aP0gS}%|2!q@yob>Hou5w6gSl~1K zx|t-2!mq&t!Bzm5{bY`K^oXAj!9a=`Da?y7*?1OkHBS(vKJ7vG{oD3WJE0 ztZovKQeI#c9?IJs12Z-mTXy1aQQ{mF9WJVdOEaJxTj;toG$OutVz0C|q6Myjq!sXe zqDapBC=63<;8LnWWLyFOr_5p{9DzqJw|y-ToW53?a)H#(ed(P(q*8LTwhQJkOMt_^ z!(VdshZ1Bk4nbb&XjWfO`k175r!rn$t03_}3QT{EpL%S{T+lyDZ~`Ye2JctT_!BVC z*7tQdz&f*V{$qjjqrb=f55XD_nk%39@gI0fCTjA|A3!`1+xz$1!kF@j{gxE?j`OP-t{sg6nAB2Q0DO2g=WTFXZtFEpKX z4=eD#QVxCiJJIKWqAjw48ThxEbXAIz@Wk~Ps8G=Ct|=7POsBQ#Yzm`LC?*Tr3%=q` zWD!|X2}}AO&(4CfKLZYlQkpGPG&U`yZ;BU$ki{Qh1?x_Ei;AB@q9Qb)xOB`-bbr20 z8f!}d)CvjJ5G`J`F-@?mew2~h9&&)?g71ITPkUjJQExey$p_c;67!t<*yA|uk#Ccb zZ6%FHMpy$=V2KhtHfXlH-Iz<*Z_eyoy17)V8N!|keZ~wI9CP6Y%&VVO6h9OF zeMQe>kJdruaaYoDYULSj{?TbVkxJgnsm*QRsQKd>kiDw#h(7t_YK5EylY1OYbw`dC z&a&cglB>l?ZeU)ef2J*e?y<82Vq^k!wNE+9TZEYHPD72hX~MnD(}JIfU^VHm)^wsv zS1Fcub&TD!%x6YCid&V2YS`=rOYY0qa!6G1j-3-HW}5~{x&Uhs;Z_+dty$VEt*k~D$V{vE{SxDVQ+v)+sy`71}RdwM0g0Kx-7Z_{MZ;oCXS|fxEyaA88^k zay(Uqb|$|>+W(&8VvP**u5)1%DynpSiHF(7&dhS#w4U-D&7hkj@#%_T$Sfk<=jQ#L zSq5F_zrLaEb1oMgzKF00(>L5T86%<5Q^uYoUTQfU$A+xcY}X8i^*`y@9yC|@wx96q z>D!d6|75-x`;t<`Gr$GdkqBR2TRlh24;OlUG^2JYDHj@AdIAM!(YN6FYP9nui{6Nh zAp*nrOET)cGssF3Ogk~%?Yx>wE1Q1jPFol+v#>V*+0lQ2nxYf;hQE|Aa?>I_K2cn8 z^9{*x`1g)G(7nU}s*4(`7Ohh^S!0mYmkGh*`88$!6JO{V_zk_Gqk>Bd#+U&+RUPQ1 zFJloF+;PHbj!g#2D@f9eV@a?nzCAdJ!RB9^b;V2}9E##`H#zRpERfTqUgOuBK{T~l zaoHjJD}i+1)=ku#ToE<~Bd**o67Zb${P3bMLyWV?< zD1C|MFP^>OtrGG=tz^>cyP%^Q?VU<#uHS@LuYsB*Uw+Yr{|Tw9(KH2#FK3GW{`<*K zz5dm7MEapyKyHoPbNrBg60|S>mZQ1u+!Ozx17YdLjKra&$oFIBz?v(QqI696@c@M@ zt@ZQ~nA_~kCYiLPr{J#UhP!_cp@++VYT~4*t0_Xt#CK*T!U|Oc;oiR}S|w}6RnytC zuXDWJrMFuEJmquL6I7*iXj8)~)Z1Y51Aa7ZZG+3TtwFMZ?; zfl+QF=5xAW<<$}T8&yt}VcLnxSP~(k&A-BHC%`l-OK4SHWR8S&%KJZ7x5$#FqA?YP ze9t78^voykWx1koUR7WFC}Yh>;);*pULQ(@(U)Tv*~-1y-OH{woGAE^l(ZCs(%Y(x zSVA8^`00E`P0Q$2_sHsGL}+gmI~B8k?@;3X>TZ7|Vc=m2){8&yd@Y92Sn><+kiRCf z$y|QbM~QeNaGtB9l;L(M;<3yd%d%F4kzhiU1Qn2Td%*VY{v|m8V5Y&A_7k%&QSv~J zb)7Dvsvvf|cIVP*?RgbizQ0cvx!VkAYUkFVk4Fv=`f%6etc-G&TQ-&7T-S1mcSbjo zLWA?Vx*UCV=<7Y;;tE=y2^MPUo@=Nr&DT^3$uCXO@s%v4l2xEEe?8VYz6%NwY>E$J zN}@C(#DErq(?Z&$_+wB)K@A2k6ojhg3-?cYa|OnwcwG2cz6+)OKF`p zHq>k3lYi#a^c=)$hyAY8>O>+aE8#3O?4mE)g!4G#)Gw6$g;0S2~Yf_B8<2Kq3n_LHQi zdLcbxCYR3~*sH6`I97G~IYp^ge3 z6>i)yzR~r`=7asZ-#7Wv9Y@Rd@2~&OJ9f{{S^ekd+CKp8^rj?>ZTYz`h)F|7b!L5y zKjy%T14s4hY7ew4;%`$i0@KL1h*-(}=g{gBkj{ScZT|8OgkY9C?^_@m(LvG+1*I4T zu;krznY2YhZ7&p6?9hZ>deh$c=(>0aY=G3VU@TBN&{v&$bF#h(Xa!?e>O}KnVJhsJ zPJ~97U8F)y%8<{E-?M^&{)Z) zXp%^2hG#3sNLMzA^YKF)+}Kvsto1u-C5)n@*eczk?>9EX`p1g3mkJ^(A7iSHEA0XG z@d<-&pw36D7JpE4mXE|=eW{pLjPj}#yf725bK>ZEN_@j)L=&G=b^%ds&aK(c?SZ9@ zI75&Y^adZt*v8)Y4qpE+(seYr4iUS-9y5+zX859eZs<3Bbz7Q^l7!qaMhOD$(1%JX zEuzt1(f=enT&mUE*k>xK#4llS!D;pDGIQsU=aElelpb*?KYQ@4N7IgP$uUijoh$%3 z?)Shi^j)3KTB#;=S@+641=)+S+uG8h)1eiK;JrxEeNQunR=B95+FDCJ#O=(ygD@1v zruy#bQeEH=%~tKVV7C(prh@s-9((j>#|2N-b|n`@7a3P0#GTIec}7at&DAT|$QWkB z0@q{Dn}Y2@wNH+xQ5mu0^C_h{7o+~ks_KFd*e@BURzj;se)aiNR0Fx`i?kRymw|Dl zvRw1M~E5U3>7+ulcD<=B9 z)bW%g+~{pfqa=mF$tt?$62q=(KO#sCVAsovMxVvMAKQ3>zE0^dpHt=PZ#-Lico=F= zs_^ydP)u374zvsV8UJWDu2XZiytdE1|NAe&!5zK^b&uz;e&@cYnG7s=^x}uLDyvIg zdILKd?5K~bvO4*MPtz70^l@y{ha7#)5N7CP71P)~jqvDJyjR)h^Qr81t+g+|4Io0l z2L^G=lG8!yG2VPpI?&?nqqS!i(x_&f{@a=^Q0wXtwPe-vH|LQJcHdG(DOO^`z z(X@%zbZke`-DAe!Nq!0@WND9;CR+);y5*mi#^GDuhD8u1Ra4X);XZuyM?sWybEBSx zFSbep?xRiSMk?@zT!iw3jOf5=Aw&=zD{UI>sSsjXxkQ#rC--3mKvTfO>GeC@Qu*ue zG@InF5jAjIc@z~m#iF(5X@>Mhp~|VS4kdQr%))GP(Zd|P5yU89cJP8XDIlz9Hbu8& zqU?e~UdwDY*1eAIc|h4z-H3}g=#nak5pNh+YpLnINz_NF7*~ds*>gp`2cgUP8|*A9 z^^~KY3OiY6#E4b`p_RG0*E;?mrrta*$$aetKC_%@no29nB^RbFX~q=Ialr+iIW{^? zDpOWgE{s?)lNPxvBKI_=Xt-3COQ@W(bjlRX+!ad1B~dC(`Z>f6I4c_u8G$C4KYs?2mV$j_tXyHc5xe#;Mwp z=8l51PL(YmD7a3SZfr;R-G1z4j;M%OCpo_L+SuNUF`40Pm<jbl^1OEIt9>` zP3qhf(G6DrgvWiZUtPOLL9>@p$5hz!taI? z&kBeyk}DjnaiwW@HK@* z-u~&hVXdDIe91$cv!YYB6K^f&DK-QtQw8Vg>-uadXfDwRgN_je=CN_;m$|-&{y1+) zvGgtBn2tsLgX;Xs&IMGAYPm}{T@A^xZeMQtF2gEAFl8LcwMwY#UicfIzwwlY?uMO~ z(j&_>({}igUO{X2!*i__7xjo8iIKDUaF>z=141rIMa3@M!uvf5^pmNxtMgc40Rahr zHH}_!vp^`UrwaR8c2^zTlE&};$s#n`uGQa?ekOT0Vb}6Q9xJ{mG)e`HfrQ1G3?o+l z3rW)xP9G)MF2*9uP5B2A60CV`W+ho%c2IB#cLJ-mw`MN6=>vZ;LWe*KwXN3ruF4(+ zh3m!~$%HmC3!nLyIC~wbnH;Xn+8EVvW-8g=N|gK{Fn5LXst3&jnsL>SRg3*`)5ZK) zqJ)K#Aw}4QmyAshb-krvQvS*S54%)KHhPD)<07!%u zJ%M99fmYWN@YS@2Ad5<$*a1XW_k9hu$qtNw78%>F(KD9Z;?f~QC|d)_`x=#R*{={b2GQa5 z>A?YD%vje(#5iZBX;yQ*ycNu4D&@Al_y1%th!u{+OM0fbr+#O z=R*!98MPP`{g$s9drQ5%ZGba%`6L7R_%u<~jbl&2MZw-$lXATe^HP-6NQ|XUG(dfn zf9c=6*h?t>ZvzTVc~G#@wQviy!#$^d1dQq0(U|3^t`<6y)lROY2q_(mJMnRK0ANwt z^o3Gy*xILxqXk=1=p}T088j)R%;$wIuJgppdhEhFL#p_Ri5^@A6qnkRI%x_nOh4#4 zuPHRiXl9}{%KDRoeLuUOfYcS&{(HKp1_TThDvUFxYZK3bPcCq+$k*lpD>idzC3+T@OCXJI#Kcb z%-7C`gZ*y`UXJX|EgIt7lwY#P5*?$j?=@sKM+xusK&*nQ>-MUAbPlY3oO9ZdBp=;O zg61(h2T@EQd3Y4>e z@l3B}HNn3+HcUS>d5_m|5EHd*>cYG0k~n)coLi>W-O5Mk4~MciqtiUXV>92x?QWNU z7k1OpTVsoGoE=ONS8W+(K#r*@Sg}D{qO+P5$2|d1_5z+W)?W%j!3@!&okN;JVpma+ zFF2cz4nS@Wwpi)o_VY|wJFYnQySmCbMgcAf#KRxMGrzl{nUBqhzOFnWJ|udy(|08* zDm{jm7Z=Mk=C>y$$czKO2!5E8|CM>x#Tzs9wc5ygka)^qC)vd`N;EZ8<-N5{kkOs* zpY3foynF4iB}pjEM37?FNHWCGwa95In$_+g3c{|faCwmMP~MNX77pRV!0JG&@nmg2 z%m-9-(p%zsrYT3C)F&z92FKoV;=>AvPjdu&a+L7g3#}soCV?brzU|~5^)rl>i=!+k z5kf^i#@0n0@l%$lg`V9oOp&3C{cSZ2^WR%HvBex34D{;%qH_Xt;kx&-T&)AjI{+^h zsqkcwb4n{R4{Ay`tdi!`Jys(jz;X@~ZG&dUWFKii6P=oWCvq@guG;%bGAT82VB3D$ z2Nj3?r~hw7_0$8i94=ASML8-lq*?}m4s)`N2)?XmH=wM(nMUg%zcwPOR#nk220zJ3F+t>_?ImBb9BFmU zkHS*^g@@-0_CT9nx9ZR4eZg?%>ewPeH3ypVr!#EfEKg!Yt170Dc$G_YutJ?cdEBC{ za0vU={kF4QUceM89T1(>OF3A7pSSLVQUO>v**+=3@}$cDf}@Ga6$zMC(!{4;bbdl( ztJX@)&;tEzsC2b*QtvS96?2S&xgtxX`$Iw0J{BGN-)`dEU42l|V{h5DYMB;qRM2JQ zE4|f^KcDsyG_R+4L|mDVey(=9xW!gCDcIpDhJ_ff~+3h&4W#zU^rtAIW81~(TzIj9!4{YYaXR)KYqgMtjM}$ z$ue2reru9daXWZ+Uxut6QJ8*JeS4H+eu zA_K=mO3vjpj~;D3ZU9B1XV^A|M9**tab0{u?f^%g-uBKG&`i_M5KVwH3+@VvB@p=s zO5N`q(827C>FtPLx*;}6MB!)a6CFAah1p-J8$J_IcWFsm|IRi@R{fn$&Q(wK6cUQd zY+i(&y66<%pjy1~^t(7}Z__(A{`}CPf5GLKwtgtadOxGWV`ko+N8)ZcnMCX#P18+%HB@hvH8wl>RiEoqOs>yDAHOjcAFI50e_gf}_I2Xs9v zl}$Z6AnQ2}a(QsLM=-c*A~Z}4kG0nz~5>6tA3 zvgPp0)2I$GreOY_3DBTuaI_Dw%Hn4lveNu?swWI}F>KI|e1;?e`(On{i`8h<+6bLc zY*3t<_GbNB$;iH&hGpQ&9`)Ds5{Cs=vmD}L_C~+5b{L)ZLp82+oosRpFI4>Um+I7k z>~EfYc6Hv;?#Z^BDoEP77U%yI88h`mWqno-tLTDzl-e0XGOt(^P3J3mU8JYBTpR9l zeidop8t#-hm9QH8Mt;$9rc+v^EHDD3NCholm4hcYNACh=DSHF#VHP_9aWOW#L^X5c zLZmiIR@O7)P7>kuRm&tDM~pM9x+i|Q`~s^xv78l>yxA+n&7e^(JeBWAcFc{L>PGYH zxj{ShP|#4IYUc8Pn)}e2YpMDPFIJ7$scXRhWdRt{A1(eb!Np$IyT0FOO4L9PdDm{K zr2`9pWkQeJ$-6EZn_c%U8p~z^neAn8nZlo(urGHd9eG(b?jTbX4{aDwtK7~AIo2be zF$QYd4P;$zm*wn7ib6xgc(zeK;W6b!CQHK$`MloDQbU|YpmQlB2-h9vdak%Iud1QQ zs8Pd{OxdzZF0_{AWgQP_T$v6E4uN!~)zVMBVp9Lv3U*NFOg;MO%?_)#e|`Uu8Wco`>}s)I_)+qYm;4(DGIS+4Sj{>LDh6SKqlwx zK<%tKZclDa%cQ9nXLB72h%*$PuX|!#vPeDrW^cij63{4Z=^*fUlNBjJy)qz%KqM!+ z;pz4qRVYv${diNi9l7!U_{3!L{~b8QC~AcpBB-A{J*xCLPX=o9nT~Uc4ZKD}p4W&Y zq``wFz3U|+qsaX^D(#zTX5tMa?@k1S^p^Bd`rFsVCm7nlU47pEov1Pzb+}F3fd}Pq zG=FUlBc5M}nCp zBZbv1N&8^Fk-7Yk012!BdF@BT`uAK#AD)2{=N}Nkzj%6tbMV!VhgvT_AHA`=s*f|p z|N2DQ+Z>J=r_%^O**U+ToD^MKh~L!@uh;`;X}V5PlcI5|pA~(D_O6RprFqdJ;cxXj z0=PWo4I{QMm+eAtEeZxjDl1W*VlTM}uN$#osf(?HtpRP7ex!T(S4z2#T3JQ2uV}fo zQU{+c6+4)R$6fBaue%CIw=blh&9h;(tLFFX7Gu4J@LCd{Y+C)%nC0n)3o8I>0L8Ly zSxOK<7;~&%&9bW6P*-g}2F(1Csh>x?+RX(yZ1V2SZrW2g`=7 z%Qu<8tZRm8vJnI8{&Gg@@g3PMTv5;7Pm*)T2IfUPi?Kp!rgNvX(BNvOo7>9I7CthW zO!)T|*{qC96iv}bWt6rL3}K1el~!CAbnS@oniCo_FFpeEJpXODR9z?fhdF0rlU zU^CaFCu+t{`QkXO$n8o8AU{=^4CZs8AqP)a1}Y_$6S~{e`vj-jub8rsxOjf&&WNl%|hb)GtgBz{1Wqi z>x~@z>!kj-+=?E7l`jrvBQ z*J}5OB}=_Z*^63?xgjMzy=hx{<}U!(S$w$esi5Q}%(B}Xtmggvg#WXZh>=@3W5_e* zS1Sp+a&4QwYFkqcgeZj8CF4$Knw55L<$ z`^ECWvJK8Um>+zoL+a7iuFg5lblmEgG}YB3XfoAvw>Slxzu29SWAp`PD|cn|XJ>N` zMEbq5j|Xve9PH7b{XFNUXwhv^|-TPDpkY9;KU z9@l^Q@~oSsQF)6yWO5MDoqX&cFV%M4^oiWT#x9;4<9fB-Pirqk)DdCB_{}$7GKKVbUOYrIdn`b z;0u^~2(no+1Ej@Bx*nQZp+xCVZF?VyB4{m`oLd$!ZHca^RjrVuXqX%Xq#k+7zbq-p z`|Dux!O9$*EWAiexrWZovYZVunfNiAxpDQnM{I!_()2Dl(YN72qhNy+tz#a&^vbq; zz<+sWIwFsrbFy9y31s(6xD0)CiMUJg_|{ZqQ(3H@78KO+)Wx)#mdj9}V-?+uR_;NNdDBK}oDJ?Q8!GD0npSS$#Q2P7ZchSy-AB}(AX7~5Dff&or^ZhNjv)%j2 zv8`uDXa4xX3tJ+iDQF&bwPO}C&YM1Ljd$0|foS6%!yrFrzo(*{nL|n|);-a$dC8xn zw>GZUs^%Bfow!O$_?3v#T5-fNAd_A?^(S%z%{T1%@fofEaR92b%nog30ZqB?JUAn^ z%w7<_wA~y?9`>x`fdiafo0^cN*Y8@f)YFwlx|q%jc^He1WD>5dN5HOq@1mbj3$%&< z{P^Dz1JPKC%WwMwg-faR0yH6eeYxFdBOqyV1DmS|`Vg_K#|=sZBr@{(+jO+qeqG?JD;U?7bsDIBJc^yPOI&MgdMXX*%38qVThTN>}l+*HR5R> zoo5QTn9le`5hFV%NZo$A5!2J4gNjQv$**lVJql^xE_Y6r7*MbGXHc&H)MIUf1#xrN z86{W8!W#+qd-hhiwG4>2ub+SX_os56eiDq@!`<_3_e;#72Ls#M+?b0-2*7{4)B%Ga z=c>CVM1)vmDsD_@>Tw*mvv~mSn_OMxRumO8^{l?(vr?wYoYQaQEuso~eMGLW@P+(y zVd-dQU!Jw@EFnH~29aAsUxZwvf8X*i9e#g?W?XFMz zf$#yUCaJMfHH*$#r`OfcwYp^GdY%>Yl~$tGjc)K#-YBypV?u=a6R1b)&Kf6m2Q|6vL|a;Iv}2-;P;2s>;kI{Q_%2nKd&*|S2T1we z%x29Gslp#nU97ju%5oJ_bZx+RQ|Scs;bW&>P4=Fy@!WYO)wz;QlirIa{%h*Ch_fP` zqPjwCUe*Dj%d-KM=A5Vg*fA=3zU$UM%J=a&&;R}g_J?mkEP30dzzYGF4uoVbxdVn}0~`ZECb1d1Un$L7J0DVxlN4u{&lVz>k(&bc zHJt`&&_G_X_@|m;OvZXA{CpOAG=V?9t9I60ob2YA1&(y zEZw>?F>yJrba7*O$!Y;v|A{3WZoisubOSmo+egi`m>qC{P4!mGayko8;%5bdRR z?}-ib6ugtDDVVP9(MLht=m5tVyP!1xK6hk4YA{YV_U z94MqC2aXo4QdHELo|%-}w0MqTHwr)Fd4{%rZ@9*@}v zb9Us%kWg(iOWb}JP|#!yIo)|L`E zagouYU;nR4DkU**E`9y$poC6xcqn1Rc`p3tYlU9BI#i=sO&qK zm@BHp*1Xp=&U_-(p4!Bk?my&?ehNSKZ_HPwS+#>n&cB#;{6wuvqBgyW+1qeU%(x}u zM*eHyk`<*W_fc5v+ZiETGKtxs(9z10 zRJjDtSaSkKeWD+Z_Aknv4mm|bfnL=z4@5OuU#yR!EAHyPMZ?CxHvNWXd>zZn`9J)< z-2^zm1U66(w<%1t{bbuZkeoN6kGZjhADec587bPcPgJo|7;#6U4P-)!a4j8R%kNpO zIpoZ6TqVQ%5caL6oi@CC_5S2|AzhbLpuQ;|6+)4DM<&LqBDxz4>f!_>IW~jk4gnfl zmd*u#=uUc7Kcd3x)zX|>CljsB{dQV)BoRN%i)Pm;XPoO2^^cE+d3NXa^|**fR`>YA z>uabp2gv)9PSuUxyRsku!5DwCwwzi^Qat8oUSR%`Beul`3h&w$LB`tT){ywYb~*bH zkRGa-bNg9VJ6lmux{Kc`Je6h8V)4yBZ*aG{GfuwyfW9Vhnv>gfRn6Ckl(~a1aYk`VTAM@(6 zAd{laprRp{J?cTQM$bYvnm%-7CQEPhKkc9{#v$_ev@rjeCwn0;9ZzbqrSe`5?&z6n z*67MYxDx8tn?>{8Of(HzCU?|%Dr#aiS>I5R^eX-66I+7_b&gZ6ZvW#^_<_8K#t&XU zZn}y5#VG&z?+Vcmer&aSa{XqK)9FB!B}BPV2lz!3Kktcdj4p8t{j%G^y+zJ>a^Sme zH*we04MYdh0^ILsJ$Y5shbTA?5g;}gR5%Z>I}A>2g-VFU?b1o&=(}QG5i~V(k=$5) z6$~;;nV~`AEvt&-A;@}P9B-!yHd)pMOVGKA9ZZIxzq=m{DqQgkfdz8^bIvF$2+We2r0U{fA6j)AWckGOMvtOAF_L$x`~&lp zXj>txgn7JRzL}rx6mZDCQKebB6xb9J1T;sVq?)=HGets^V+5cIvfSwT82TqDW{HCEVzf3LVF&z3ePjdmrxtfTR{RzKSXq-7@mtI+!r zcbb<$y%Kk`57rnhr&2LqU7lMN4)yH6GgQGrYs~|af7+PjmT!}tFnozXltBu^Qt~Vz z%r4s19f9M*#B?{D<^EJTo*Edsx|kEIt-YF==Dd+whFHJGF`zzV$K9+!D-_Ben{|@* z<#9GO43IT3ubOn-#d4=zm)#WX_W3Q^^ppc@T0XTW#+p>%z+J zx7IA!U~NU4#+|8W4CUz2(>}YA@ayD8c$O zS-)k3dX0^;zrR>qlX@ZTi~N&`$@#Vu2M6CY9Yj$Sdoa$Xn^RP>QuXk^L@e$S`50Gc z3P_>XY8pcaakL;hy;i5AR)6K51%&BCgOg@k+;)!SEyiR<%X`A3w6aV* zAvNx>T5=CDSqPe`j#W%-F^n)C-M4F8G-@HLH}e>^oT=s4x@GS_Y1OzI>GYzJutDM zp%3zymrBG4S_~YN=Jn6Dzxbgu+x}q@9a)l?s+JOeTF?y8G8C;HN^k<7|S`On} zRTd!c2_}W8)YChVZdYVMg`*Ue*pYb%aQ-it)v=}%tP2$MPXm53Z1F!3?eR&LG;m54 zwgT?!o>VF}tj9@uMX)cDBw5aj>Tv36)p3;C3xk->)?t`S^zd#p%`-yq%drl;t*;*# z8)(wqb<4Y}o_(?Z%6Z$KkVD~82gD}BTNHGbU~Y;JI{{dU5S2vxvnCcOHJ;qj&Jmmw z{k5BA;g;**0Uv%bd25?-=?l1gRdiM7Nz3qWE`xg#~&rmSWk>< zGkEH!`$xjQP$@qmU?1owBnhB1L&Kb(xWM!Tzr}i9O1>_3k zv{#%~c1x69UCOh7m3ontuhl$oQThFck!A1<`YGvH$3m10FZkcPQb5-I=kP;nuIbTC zpyP|MS&;hCJc10U0^9*WHS%o?gdLK`rfTP*OXTw=#ZDCYMAF2rfs&svt^iRZBXl!$nN}5yG9JNx< zcA|q#BS4k@>Wq+5i6W|UNO7`Sqcv4+3=+_MU4&dD*IJ=B8?1zH?1JI^EyE zbP%1bh!k)xi!m&@I9?sHq+8x02ekdp8E6qT|PO^r&TR; zixXd7{wnxana{vM0~=biqT8UNJSS)LNPxnvD`97+h?>%Eg5NL8JTRM8R6Ay4)iLUD zRReVYjQU_8s~-N^V=HpCz6*=t+zGmZR6bFPp-x1}YQFl!OSh2W5ixKyatFb3v!|*W zEy!L56&EUl6eseSr@r9g)S2x^LVUs6XIX*xNGj_VATQ**smFS|@;xGijmD_MVzF~$ zF7_L;^JVgc;1iDWaRU$lg&WW*FO`jX^_$Z*px~1hqj*`+U%K{w8M_wLp`UJ>gy;yF zxLK(ctNHQoU5~|zyABmpOJ+~xEqBC-T%W1C_eSHzgurfrs}XRfdb0^S8^n9o<>G|2 z{`|nD6NX{Y(7L#)*`%?#MVVv^BP7AMIUguy?6V@oLo$^YwGfI*+VowMTlpE);DrPk z&PbO9rPhSDCx={ecD; z(R((CMDn5d^wGEUO=etZVGKN(FU^kOY;SR%e=5Nz-dy>iwKe=n6%Baon<{j^XackH z(#0`XaprBkZEiPC9fPCCyE47~&WJ2XV{5${y?;bj<+3 zUnf5M|Cwx1I-R0@v16zix;!^368kQ_Y48o?>4_|0BVjWT6X2zvmcV%_`T@c|w*dmA z$fgsz#UhM~D@M3vyPTKFjAZ`-z4wDEGaj*>BpOc&@MA9GIxqAb%dVY4ijd-yl5bkp z?At5lJv-1*JFhg<`g@^MaHT7r`g1h3x0d4Dxm)_}$~>p`5gyUF1Fo%;5Soo5L>cJ} zQ%$w<8SAFsYm;A^cYMjdzr819`d(Oi>6w$rsF-teCk(?QBYL;j)P*D@JBawdv#xE; zEA5Rex3yV}^~bTlpY=2e42$QaHEr_>PCzcs%7(-@gGflye7D zephh0rqGJw=811#?2n(Rq$Jzwz5}niKqi}N={yONUH=yh-%tw_lV1P2XWr+{(`&(r zWqRJs20x=aaikxmPc`zNC*sRp>3p;$Mu3iUlbN*n@kvBl08!u{Lv?j;?U@Ci^5W$K z+d}L5N$S;pQhZvZ1z#&wuL||HQiy&vr@0HQTGbbY;CGjEc)KQc5 zQO)ovzXsFcgs>p6Kfd=VpgE@x=}`=_EZWb@v4;xjZa40`y6z+$30)ndIjHG}c6gfo zcHs2Mf5wN`@;Jr8UxzpKDnjg$^vpeMUp_eY8xg)A3raMn=i(K=MpPLA@u$m^N`{SD zAuh4*lcZte$(YcMFX^GVDkzR%u|BNvh!-S<4>CH`s2X59-yVr5)9r%>@yM+HuPJ`P zT1gekaX5f3C4_yP4X@__vu`8Zz*4}SmMn$f0N&ML^u&(~ln|XrMB z(3|{}qEch;uv&wmr9Pi3$b^=)jK<_|@X)cTV?6pe572(%G&dHKmst=_qSwlNQXuu{ zw+OlR_(u^TR39!=YS~}4EgOnS`a9Mcy$2TKHevb4c~Yi8Zce?AF@VP5`XBetbRlxB z^vMQ=^wJlxck(e7xh>=ndA3YK`LXjtrAMc6ER{|t;QYxbx2T-q(*|4j6@*pC20&AJ zS2uz(byXK#E9)`OKz`@Db#r0q($tns=y1kb3^z+PaxTmir^rfuc4Y1RpLa(^;Rm1M z2J-g|;cO>+N1(kMbW{;eWw8gAcPpn0C=MlO;I~$qd*q&_H}45;Z_e_^8{zjmF_7k) z8K$&R`-+j5lHafR<@Ja7wLq<3f2nfCf1@i>+wFFPkQ+!Y>R!2sxOlnR>1nr!O~u-y zAZOs#loNi9D}$sVE353qs5tP-;z;jll4Em4*w%5iCb+^|bew;u_!tY?X*pd&G!;Pr zZ2Y+MeFTIGroPF07+ER(0}C)Z^wLva#5tV_q~L0kMhSpW0Y6p53!9dX~3OZY*7CGNGW4n9= zh3X|LJv2XGQOmMk-^(c;3YyFkM}O`q*yj7W~L6+wiBoW6ft7yS}E027~mV#mcd)4!t4Bqax@JJeA}veO27!?c_` zgk6#%Z6P0d)Z`f4r>L5B#_c+q+^X0_pWIv7tQwnZev&+kP&m_Bu{Ml`T}C=?rG$Yh zKcnpU8c;J{VN+?qI)DW>{wtM+N{NJ}M~^^6gL#-zi(o-Nd+8;!b;@=B59{6n^e}%t zUBzV0eThKtU=*l9Azn&Dkn4^jXPOFB>dkFnZD%G_I_g@X>^JqyvxW*e=Pfl08>JlL zk&y=KmH=eOT}aXt4Yq@9n3tJ5Bf+s!{@rD zdyYz6ZPYWpL20AJLP62x#|T9JDrCkt`zG+)r@6-GKa1<+Z~om;=GypcES&SZ(Yt@P zzGGk9FN%yBxRW;I2`Os2_Z)-cjwU&|kdfy{YSj63-VWB5zr&!lm)$XseM3o$GLy(% z;{yjXa&`%RdSZauRFd?z9>Kqh-6||^veJaGrx870d+3_$X?)rvD+}d=uxC_5BcNf| zxTc=^@d0Dxb$6f`vti#syr)oRiLxqOb-_bxvuL~e7Hl@Y>lZD_IB5YPG)+9qpU5OVkV?Kn-Sa`hL$Xdw4d<2bA}9tt?y7 zV{Zh}N1>J%;X1e|A3Q@^9K^d-o1t`zkeoYP898ECLtMx?v(JE6t%DS@&vlb}nfdV1 z*08EHf?S-HUm9S;Lc%s9wz`1TJtdgxuD>TW-B7^PV~o+ajPG}UQJL{3@@k{#P{!mL zr>^>j-bZ2PHe?rU_Utng4F5%vx{phBt&tCK#0-0;nIS&LJzP!YV7L0MEjT4^PrwZl zo=)~*+Z}Z=D-iPTI3Sk9`b|1_6dD1_>-Wo&I(Jx4me5}ls)03@s{1U=^9-bXy@3|L z4r|l5 zUTwJ&Vr~g}1KJp?c&I^4xd1hYljx=0xJB}(>K^|9uK2{b)!cEN+-QnaL9Bik3L>G$ zMI;@YpN9M3q|o-mc|xM&jlDBHg7%Fi3%Cgn471B9rFVUX2$vGQBvTTaOn!rKIVzB6 z$QU@(;ocRH6=Y=zTe?J(VrL^+v+rN^5w1H&41xn<*JlmJMCC(Qdwz%e%&IoD181Z6 zmY~C=6u~)nz0&BDA$6F3J4H=D!Y#2!7IFvzS|>g)zy=l^nQuz8K7r(T3Rfh;^|~Yeba@$O?B3h!#pB z82XuXrwFmLF|5Mz*0FPdM2hPOwgvcKS?vUfXDEvA(4UikS>hg#+E!$4(z;KbsSbJ9 za(qT}`FE4so;y)qAw>VjGN^0O8#g3_%{(wEbXDFNys%fU?7x;)d+Ta8#D@hhT)Uv# z%!_Npv45*tgY>A;duF!B2NZ+KfcE(9t;*|LFaeOm4L9Aox9hC8G-M%C6>gJH&$T6s zrzyb@va*G)>!Ab123*aXUVGO7Yr1nSAFgy;EmsJVckvRj%Y$g0|D|U0Z|!WcTAV=X>nU!P}Jak15X$iVPGZ2$cSW zp@p)GkPUP{{&-&@NK}4*7uj`r(tJj? z*5TyC5c`YEd5G)?DH3@cjk^uV>(gI`WvkqVs)jwYi~9q9>vgU`C~p?~GDh%A5ntC+ zO?`;Xxap=wYgw^ffb|aRkq-y?Ki=rz+M^YHfv}^lud-O9SqR?_kyWS3^6y&F&V;}E z2mS{VZo~B4-5!qWC%3}FEN}{h@`cFA98athX41jM{bcK4l^W9etFsMdyiML}jIc0R z7cz7h-Kv8gccxUZq@TGr|K1U`tohGV-wMt2lBMFeUJqR-zmPy_1G-7&6Db}IESIsC z4#=PIu%^5=UDwT6{YIG$pN$I4D)q zH)G~!f|flKhY%8d-enEcIso@R2cM^f_ZI$x1}dhFL1fF~_s^rF{}4xxJ@}>s z@eKSyEv$^aqVPtXH5K;ITQ&3kNScj_An&IwUY5@mbC(W;H9YX$bbt_YY~=H&Hs=Ga zP%xz$<9qD1MV#t71NO#giHert(MWAEpe0eTRIE;e^VTS+hV`Qt>Hf(4LMJe}IX$>P z*S;e~H7!#c8nMVRo~g{f*IwY_tNCoK73n_6qR=6lTbrm2T_0Rx!FVh$pa#EM#^QTD zTjxnPSShOn7BC|#R`Ar{uKTKcmMt7J?v}1ooYNU-0XAvuj~$^zJUXYo*mNiWcr~ZU z>V#IJZiS&HyLxPERt!gMLrEudH9~2=#5@_Gj<}wV8VF`tvwn7 znq(X&S)T9$d2XiC{nk%OMn-_>K#yS>D+^QEnr~)JUJ(nWzWL}2%E}NlxQLz@{1)}` zys7cQf==!~uLu206E_s}t!BFZ&&7>vkAL#F_u67h&i`psItKAgkEx3p_i861K!YcF zCV&_)%3zJ-n(TNtQk6rsBQ4H*Q@D0%qouFSlZ)FjgBo}~9FswC2W z-j!J(6QP@X>&-D`MxR+#39YS?RhHk%=2SOk=9ur!!Cvmgi@oLNDI&I~cxX$N8gZqS zAXHFnbaxdlD6JeYX0@)+2SnJa4kfEvANf;)wxdCxsQ;+0mnYf5)0u1WSSPij%haS{|wWTUm2aDJwoFA$J;;@rM zoG-`Zt1uPE^t8%p8EiA@3=b-sCMP8jrmWpjD*R-gt)mG|bGEo>==|}f^AoYH~(r^i`pY~nn*n^K8H^28G_LRmBvWm2HDl*aNFF)x5d)yxy{=Tspw|`br~`MG!u8|>QzykEIC?h zr;Fdl(-hIpmZzj@cwF>&HS5wxO}+8!5KNmsL9f*Pp1P=35c9a&69UwG#kzK#LQLgo z*_3#VpLYJ=BeE`de~&seo|l`;Sl?L+{?WlIx!(vM+uWCk7-ctIgD6&r#0M|XYMYqSI%i8;4h+k02LO3nDmc72)h#R$l&(FJ* zRHF|kW+A;I8V_bi!O>?wnxRqX;qy$E%sTmXC1v$EsUVm+)Bb z%DzTnWsmEJ?l{?0Ic_jWT(zo!Yh$2V>M|byfG|_UWdrX%-1T-Am0P^>rftIx zCAKDUiu1ApgVkU+5-czzkWDe*>Zy@FIWwp^-D(M_(MYi$HbqeaD^`biT>3G!8vd0H^{@u)1@1 zE)!Kw=u}VTGJQ_8+t=|DpdI75YAz004cc1g@w(b4=2LWx1Prq3J$;)o{!6gwQAP~+Ofar2(zvKZZe z%Eu%+p{r*}k5=mJY9?)YWk3hVsskVz&ah5TxCf^Izw=amE9}>DCqAsDNY=Y56Nz_I zU0!u38>1$l^IT{v+A~L{h1c`YGBr&_`nMC={rngOt5b>6^Y{~~aR^h1N^*v-?fP_m z5R+z1n8pl^6mAQwC8_&;$%b#z<$(sm9nV|?G77QnDXP`^he07ia{&3-M!@%btG_&O z{~x=dCv*E*qn)t}iXU^IG3NSKl(xpH;-uA$&%V!fdQ_Qnu^rmtmQ5(RTxXKzs0Ilc zc|vHZ6%1=`z~UUjXdQBwCR*kEO*AH`RB#x1i`jIt(jeb!B5KrJEG<8;Qo|8?Rb2UM zg<+9;n-h3Wb3RtbQ^lnY=*{S4mGAn$l2-cBV7D-ke4SUhY=vr@^8#ZYHmRDF6-&QM z0YJN~8P_&Rcx9{T){PumN$P0aGya+YIW$1IH=kcmQ`y~4(>|N)n;)v4^j+_C z8VrLysya|FtJ>>_^aH=6sp2~OE9+4AV#F)`RUl2jIH+Wlt+ETHt8^ZYVv?8I@TQyp z3Pwg+O%y`k0-6+|vsQJF$y2*|Iw<;bMXB~B%>k|`JypM^9s(^NY2<2w=0C^1!Pwv> z|G5V0U6;_Y+NyOu3EdH|&&&ZQwI%=)n4=5ONn`M#i)fiLb$-75k33i4VdwJ4Uka-o z$!;Ay)tesQhJ|JQv1AeD$GnME-wZh#>g)~exQlR9Xbi!31B-5@00p~)REu!3CduJB zQ*N(Y;h1LCCp|;=kJcBflAHv%zkW1EF&$6n<)uaE=XcW__1J*VfEF=3Jm1j!v*$^> zzcKvo$WPx3&(GWL-}q*r;=$?jhuypV4Xj{zlDmH+?7{j9zI`QoM_ble&_pPsM5~g7 zk$4p6OPE={wd`9r)%)`+?1w?nH8G8jAALh>2zQe8rH0RyFC0OmIpsgaz3%$kM|E}n z+4_N@Jtn}~Ch393)%g|g$`7g^G8hAs&6=L78H5>nczTXGDlH6Fkpf8KEZ(e@k7w(m zpc6x3vu{O7`&`W*QQ(ii!T`t-T>AGW$4ldMRpE<%>6=&j>!b7ZlyL! z^1dhOz3?|gNOCQW_!4aYqYmV5Mg>vI#=imHzUR|+FgFua_o3v(_x|{g^L{%SKVvQzY%p=el z>~_3u4-Vamp#HVA`cHwXEH6zfigD@& zDJo8#ihu74)HHw8wUzk}-tq#jBxcqp@B|&enocXzzM=J5{n&BTsssHtWmWfUL>vkj z9ni41^>6f;a@umQ$o43bbwvG5g`-Tioh;+J^=2z>1=QV?=3B>ToW6Rt+OU!jo7f_x z&{$D}s;w&zNT&-){0z*bwlGTb!f3pUCqe47uq2W{YySMPvgNpj%quEQb=nFPPCIsg zaV~(Ax{J)L`GC;nE0!M^`vHM^`m`#kF2_3pTO#3ZSND)9}X+S2|IoDTq3r}nz{ z?%(fp(P-WT>;8VFegNK<1;r}bymzC?2_=SWD7Pe(_UQUbrN6q~OHA>m_=~UZVcQyO zo8%aTWf90N5~VxI`kgmcDh!|f?d^)ax-XSkzb)ukL5}04kf-YJ3sk~Lm=GVR9Yb|i zrZKnZJ3tda%90X3J)w-{Muxk79qgEFO3{b%iWD0V2jEUkL34F@0ZgLYl*d@xFhiHT z#}!9J_lZH>OW(2UoW}M@p_D?`KTgTSE{YTrk^u{!qP|g^A;Tc54T{!%zWX@T?~~LU zFWoPXrT^4D2sb8j?x4k4Fsw&x77P=t6JpG@I4IDUG`jNRCg+hJeA=rE8v6_dh$+o_ z%!B&Ic2QqK;|lcvED8II1pq&~>+bL3ojDlF(|_G{LzVT_e4W!bcmJD9ekR4MW0g*_ zAtB$_<#O+F6rQWL8(f)}4$buM`_b;j;@iUhE_#cwd}ZvWxuPmZPP0c%G_B0<+(zmQ2MJZx?HkFK$&&h%Ctd|4>PTV66vokzsBw) zi&tdo%N9PWk~q@?H0UinM3Z+KF7H5n8(H7p4GsSda6| zbp3nnIIaH!c@~szlh0|d(FfJqYXA!PSf8w`tsK$~T^tHcsI^~)B}|VcmHU&PMahG7 zDvgSROodt%;Y$p~@6?e{WA#|&8XhiJ?oJuCl=%}-fiu4tB%5@Ba8wa_CC@B5v z>n>hFyM}^s4RZ-!k$OkTa+Jptk~E9?=~=KDZyNR)DMN6^Lpet_`)tEr)OLP<6<~kl zs7>V4UhRN4`Qnb1w_dGk>K7&9iUjol3-Qu+>5$bl``j?UjGD%o9=NhdW1ECzJ48_vKAJI$z6 zR$q?Q#j&?q1FQ3QzwmknzN_GsUD-wcnBAnVOfiJ%%T>bKpK>Bi^sRa>e?;HPJO>|n zAEH;a=}?XOar`C99{!SUTr2r7roi(AETOJ004>)=54Jcx81%AR$VD>r-<2O*dg>`a zulCBzFQadz_ZXWEWzos#uhci>{|{4d8rH;BEecwc)LOw-F(NI9K&Gu# zY89ikih>XYHOe9?$d-_4>w*ZHDk>t&s8mr=BOstah!Pf&C1QjSki;ZFh%DI%nMo!y z=b`Waoa=n?^6~@3Co}hb|F-)%?5D@nTzg7yd{hyoNy6hCD7PmR^wE z+kO@4(P`c<<~Tc?21rI?2QZ9RWyz!$X9hd z=`wlK@Oe;b46P0+J>KfwXSmHh7a;IiL9(|%mWJX1v82(zih6m`hT8WU>-$qD;u$)V z^kAWOQ{n{l7v1cR>t>hRD>8<2{#OXJdIE$A`Y0@71hZbW#%dAmGq_qTXTj_z1U^CN zU0(;FAlh4dnvTo{Q-#|+ZvnIO-K!U89QMh zgHoH+oVhJEA!(r1tFMsNQ8>J=xdnW+SPp7Y>aLI-sW1F`pU)PsUm^L|-XfuEKJ0)( zRhD2%NLAL*;uxnrh4c{m)--cx<*cmVeIqeU{g}e?HlWte{w! zHI3!_qPA7I{?ptfP*bS+WqF@|&dBfw`)q?tHscXV(#@Ew1KsZ1kQBWk@G7t00iiJl zC>36mUrap3rAvkq<^&HaOSP zW-_3V)o3t<_Uj|kk#mea7m=@WB6nsy6kI8pET|{wyX;tmA8oRt7R+Oj@7i12FI0}f z)(?WCRPJHlAEJvg9`I$xeG_Inh z`aI`&`NfbbQsW=WieisC~mYY=)gVT|iDmI;rcjH;a%uQFhh1ON@TE%eT;TA@% zFsSA1Lioei%V(?NZ09q|Co)Am?jy&;;;09KZvHjY+n-o&Um(@d7G$GpQ~TG7ykResyA*vz(pQ&JWkLmO_mZ>So#bTrdzbwcDg z{DXSUj<)&uOiS zPO@LjlH)-ni(_1murF{G>)5O7$2u%0do8(~7ENZNixGLmsSlb9g3klc@b&_%Z6&c# z_zTrzI+tqHdnl4o*D4*|pjd{YLHPdyNTbvyRh4Hx^V< zIx+FB^rkdkw+Lll<Ua9{8QWNmRIikd1%fY}04#g*U%Gn z6F0>0_`;ZdgqLmI*$=O|;R#8>oWCo0PthBm`iw<%xW~y}76Rhpr(I@-@#s^SJu0olcTR}XCS)?=RJbs zyXQnMou8i-?U!+bmo$%&%R@)l;z#(C@k=G#x=oU6k0^PjqRW)+my;YMvHZElROnv1RiaFV(*67Y>9JcV&7z?*+=MajCsd4()^6vj3tw~^8 zNtwzbYicR)%2=dKwv_K1i6q0nudCAXp_JtVQ17#q;v6kcsq6Z&F$>k4e@HLj+vN65 zvi~Z?Vgyt$MTxqsJ9Cb$)~5iK9u`WzssOXMGu-H}*Jl9m+Ao?mu-&HF0-1GLX*t?v zwd85fp(Xgn##$dz*>sYz=)%~hf_>)}&$2~cOid}rUT+xLx>iiDjpALG>x%w>=bgT7 z`sQfO=R5w}AemSo&UM&TGJE1Qy}%_btd$VahQW$->Dd}MK=EcjLu{c@W}zL7eJ z;_9;NUwf`3fv!;dK}ZMlDTS>(5V3;#^9Amk^F;@oIP5BHCNknelL6ZBkmYTWGu>JS ztjtB%GSqwddpj9;NMHqkstKg+9X83lkbT}Pa*y`?hmL%f*4E*V_o=z_uV>4?ome5` zTj_h2IqOZdw%Ob;pTAWH91zaju+knwQ8tMmYGB(_xc2|JyL#;(*-F!FW++r9q7W)E z=K3+BU$TG6X!4@<(fn3N*rc_mMQ3Vop}PcSH!eT(xIBr8H;#NfxB36u1%R7hYE(;N zIKpq59>1K^w_mEFBbv*CVB1^oU-MqjgpI!OKlWo_M^w_d3 z$4>9Y_p@(-Gh_#{o&Buog$M^fdE&*~kmDZv=>m%}#MV3~85<_$`i>T-9IC&&v|s4i zD6wZ`)FDPl6|o$y%_Vi^LoOV=QYW>|s_lNQlz07TWQ0}_TnYI|F!`kJx$wHh=B=f$ z3gb*2m_V&9Pw3oO4OEAk9ScmT^BCnAXV50LP@5_Tk(K_4`N~yJEI(atJ(rhn3xI0{ znZly9I>Ch_ChtK{XjQLx!p|3N*pVy0*K5CLHQF<-45Wpq(s@*lyk1fS>7{l3x%eEK zKY?E8-pn>=Ahtz~OD`E)4l0GKT-SjKw85LE66a%AApVD++P!9Z&46G)2nzCl{m}a- zG5sTYc-efo-nd) z?YOw?vrA3hM?NmF^{X-wF8+o1;gJCI4x$mep*|@Oe?(w8`h}6aXtCm?%eic1)!w=v zE#g=-I>WbGmNA96WsCC_W<}2-Wl(k* zau4atuJn9QP2?Qk^=9cE)O9L__sRXwrVrO`;_cE{a;N_T304_4GA+Kt-*Rf((UF(f zeE_zub^oolXONkc1ov4f%LK&1Xv@vz%-`7#$Po{5Gg47g<|a`)1m=m9EjM9n2`Oi!YM)YLm@0r?-}UhE4;|Xpmf|5gt5QCuvj_TGB#T_JTZ= z)3I&36P7$knlATfWJ*W)sg!&0RMh36fE@mN{%8DqA)JN{ZlSjRo;ad^q_p!4vNAu882@Zh z$>Zjxj?@l`8=d#G)Q$9-Inw*lmJ7n-D^EGi+r1xsIq&0GLvwStB0#e_b#;VAUw-!! zH8Z^Y%ay-2QKSP)*jd+k@h@MuenV>Bo{|6cYAlmc-~fe|E^EazZqpW}UjFo1ZR{YI zdgpB4c%USE^ldJuJ=Jn|%_~p&ev7^+b!~KmQ|&S0(xL};Jp6peTTk!|Z&AB&YNHe) zOyoGor`dRoU@wk#U;8dca%XYmL>ZWx1fB5`dAVbpsErsC^ae|9%t{=$m8nHBf%l3| zd~bd?LS|S+wyrSieY|sbD8_n1)ZI#Kq$ivW;|uD~4JA^=>yz~5il(A0`QXCXi7d$_ z^GR06VI2$IrJ>)nq8_KI@#%+HQA2VK6GKf56OpA6(d-4|ky zBR>|!JziPL8Twv>VzZ!J-B7=8sA{}hQ55kG`W~Rk6x>8#AVSBx2gLLB8HT?3Jfgly zT`g!^-L9?|o!Jw3Kcvcm>XIKX4P>uvjDK$}YF>2<-$ABBwtsk^QVWjF$LBdQ&THn2 zEEJ(#ARIocGVfr`th(y?Tn}2xDTYmpu`a{M82$_rUqRcmCRQQBi#7hX$ggiHld0st zB>-qIT0afmYg8wYBN&SxGWIZ=55KDkI<8HLt4SnjMgo0F*wK4~=XDgCxs+h8jM1iE zAb%&%C0D)gYoTx#`6C=G%I)F_Vm#vSyim@QUxstBFN#9Qy`ehY9NN1vNVN=)H-!4C zZmU|Tlo4_M>V)xgFX-zc-Qo{$!1srblUF8~P9Xc*$#&p9IKmt(5CF5;1c6nK$z@-(&mO;s6%5;kK~?t_gYuQ{`E} zezU}^0r)s^ts-sPku>aff+DIgUMN)a@RmvfI}(errLHUrdR*+Cs<2#h*Y(`FL=3!_}CzXqFCZGc)zl!UjRR zwF_K6_J8c@Wao)vbt7_$B-d22VXTjYt+wR>i7!O8&>;6srHsKpIo1c+s7oqci(!v; zOOfX=57NS^Yet@A)npG@$6Mg=xlwDIQ=$S9EMgt>s}lr+$*Ra69uytzwxbI?Hv`wOp)B#VHAzjp1cR;T6smeY^Yc503f#dd& z%Y*1~>^f>`z?Ts$kk4vVQnSnU;7s*Hy}(eg}bh zO0E%uAFr%xqxk+a&+`>WCP2*-=ONO}@LR`mDth3MQl~6gu%eX$!*iB}K(V(z0YL59Phd#q)q9s!VI3zHw!$ zd_@!&emr-KSM(pk^ionUbf3L``!bbl%h$VRX%6Mih;hgbt>^*G1DFsH?tYZXUX41w z97o(!kC(VgD!v4*B*?*iB@GP#Oe$qyBw)y&?KO_QnysC~!PgCZ9_MrFJU75TcJNC_ z_Ro%eLboPK?GBQC$mpYQLF`jUbLx16x?9Mce$u55l2G6-4;2C>Na z3s2z=jCgjO2N%epr=y;ue4BcN^Mp)kDj6k;dM96VT)18#b{{KqNBE``vSW1i#)QXV zeAPa~Qj#Rm?w_TGpbPQVgCm_KPtm>GXE)A~jsFP^GahIa*~}6>fHna%&2!!bPtyFs zp6a`%F}Nn&VvzGJHmt8^Y*PIx&-BqhqYCX38YWV|FIx^I97XsTMMN`vBBo7St&dsK1I|y zL9k9S{I>HFU32^KK)_}_;><@KC=9;t4&lM>Xz}h#hfE^qOKH}U1hdX)$5`o!j0;C> z-g->{cBv_u&}@d||3-%_x;uh^0V1c1SL~?Ra-CGnQQB?o%sDD!EF7R z_{Jl*=AMmq%$eu1==kbW-dK{lPIRoH$b7aP#S7CNXrccV{`8|l!`$wxH~N~!HRvmd zAH3T6`7Y=_7%vy5O?BPCzAUilrIUlJVhZh6lYK2`F7N8BTCcox(MpchT1_1=u8ovwRe7IQA4U9OcLhs{QLoH8YTbc7z2-2sf)dGrGq@LzDBGj4QQ^(qzWtbRp-mYqxZ0` zh28ZaYjwOglJY#XKUye()V0mfk@nQHs+E3!pP#UB?#sRo_KyrnvA z(ky3O=f+M@(0$0p@h8zwJlk0)yTcv!Vjp0RL<}=}w@_F;XsEr82a z`ba1g{hZTCO!Ds}q5O&uGCcpe0@I!}M^VoSRQ4}yMgW>1>x?d~@GP z+bw#wHs|Ejg@B*%Lb12E9)qoNK-rhJSLEs2r*B7@z3WW#XuY0<56OOgLHbqhhhvXddXmHJrUe>NIDvw?rg3 zz+I6aE(gV+!8MOBIX4w<|CBqHLF+sIyl9=da@)sb#Ye>SJtf~pA3LnKDCzufil2@L zzr*f!_q^;EZ*mi8HgoL$h;i6_;ZCUC2VltwwE&hp({AX67rC{*@0N-)@3_XAFQx@J6%}5Dhot~e7^g0W6sKbGFhb*UA5188 ztvjBcHTH~PXWOqLhOSn7;e{+k5kgNESyUoRnXu5fja`ueKgLS=v3kX*Csv$a zvlVOtGKwUvl*T1&qVyYAzsp=7QeQ4TP0{Wl(__mD)2|>&1Lke`JqGl^z`!?Qm4syV z9^xJF9u5QVVMe$J30btZx0?LrKHch?-~P^@j7=;`8MP&{;1OHY`gbt7XKAJFd$Tyi zDzaQ>9hngRW;R-V{U#o_t08D~RHwnVT1gj9#rR7Rif)_l0Ojp3kG`{+%+x+i6S-d3 z3ZgF!0J#M-{@J}G0lWS^4}nJOnHh;9``zrAlFlv3X!9K!Kb?9fX9^Zu_#bP`$9aE* zybC<{*Y(FUH-%EZF{~JW@@(0=i|T-=CC^*h>&I0qWp86WO46^lvIo}fxr;twZ-EW3 zoSwxhDZ&g;hN52rw!2?(*llnG(PIXR8W1nMbVlZM>tAy?wO`1=mlS^gpjfhiQNv_= z%wr^Bk+Y_p28`!Jnn1d+FMH4ZeFO!V9%x3B3-JK<5=)eza)GKf3_r>Vg{#T6n;&s8#&IW|QJ+Jj0dZKt|lIC#pllnWcZ3no^bD_JwU>+~0Cb2t_UM1-G-_0yNp>3i7u$G|^#J9_r&O1&R>&C+c{ zP38MzG8e#TLme~zLdGm=p0fGQ)B5qdgj8tfnb)%}ez7W!NTOA6fzuLjlTJ+RR0!)X z<0a}sJV`sz4(pLz@*z2F6AWuBvZJ5>PXS=@Mfq3qvsvVbjhv@NFqi00Fk z;Zq%=46gs&61$Zh`ZnG#g5u18m#-r?i?2p2fx)Gr89`r{p*H#*I)0Sdq7FfiD%8xb z^w0{nWk}hE>z9=JG>WE&l;!}t@Zi-vQeri0Hks`rir6Y$hkmc3v@y2$TG2`{V6?+3@6{SlwzM{ErWTx7YUsXn6oQ znPu>ZnvevTy9B=D9e~u&UmTs`j*rQe(C=YqSM**>iX)c-j+2883pu9{L3pkOV>&~r z5nlThj&ZAT_TL@e6AMIaS7LD$8Yr6o0w@ei&=Fy~c0AEuP$MOFey&3{cx|z(K{qd3 zy|d>^1E?{53Jp4zXeCMr9rU6MJG^ue_JKF3< zgNNMNMBR0L!*UmnZpcZ%!61)MeOK!3P+GqH86gvQ?T$)FL+}`a1zEiFt~+{xHlY?}{dIk6#K|VCL#sTAtAs z;U|artjG|UZQVLw^xiu9915igMXhWmdv z>PB0!hHFqy4`kU4UAI1noXEQi&zA{ioLRv(MC&t+o}*cdln{3&D#{r7;^Keg%j4O> zmUAQP94Fq*(!AOsI4#~EG9&Qwd{^yI*m%egs(JBQc1dklK+~Gcq;e)(1!l}sR2P*e z@uKQ=ebX`|E_y(V?9GT>xi{kL+8iiMrOSYFD*@}HI3S;YhIWf5Qn$+IQ|^Et;g*TF z<5?x&kmD|jY;Znlz12n}7R9Wg=)*S9@3a3Mmxf)WrpjSaPL);LWR;};IhZ=x=?frO zjYaq;sm3`^&BRSrjCi3Llw0n8#t9<0&RSn_j5W*%Q=I+y3VaOoZ5*J3 zdmGq8#zYy6mJy9iw%5Mj$;X!l5(km?eViAPOKC+S#KRg6rgSh~Eef4W+ntmzIIjt` zu~4aaz~PboQeYTyjBTV-YEx|6zV3#uT<@M4mJzG`_G$PMgW+)adA-QXHR40Rb}s|% zm!1g;+0*a-MS?46WI;CFFD8{{()4?$uJ+i{ z7lGaiLO9O?opI&OT#EbsuxQPh5Z~1NVbL`GkD%R{I&TG_DCLDHdm{0D#)&RnkXrEX zOO^oE82FPM8{l)b1p@wVjs#c_ITT-VEmhrjj04{(wFfDE)P zje`bfT;Q+CENZLIvngkA{jbvu4P);F%SbMT#u!?F17ww> z>yyaBxpWH?*H|Ny+fE{$G>nh8lN&aOQdr4(J0E}pL& zx|lXc_M$3-@vh%=pt3fR54q0vdA65L zCw8v@mBv=t23&E$7WV6Ab%*Vy%XWFq7lmLQ*g+!6Y_Lc0VqL@1+6KX*(n(^VnmnQ& z@)M1m{~w}gYD>O7aniBPW{}wsLIy}?n6>?$T+}7sO-9??3TSJ=Yb=Y(xZ)yXN4N;H z%})Gpe0fVM*ry~e0OePB-q}R+z;~@r@`U|%VfSu}0szGS3G2Ky-onUulj{SrvyIa+^B|E(Oz8b^$vx(LVH+u4oU z0ea1l zF>&r03T1PZsazp~Q&^9wm-9cajw7T|+fZoABq6^g}Bqt#ffD9fw~G6-6x0bLzh=R#{z$%#&fX z@}W5#gX2CF%-MEadS0;d9(#;e4zF}UKk7GzbJGHBrUc5`#RjoBnOBxPVD;j z9_00W&Nbz^%I3)5GUrHr5T8#h?ERAcC1!e@H6*`Z(eG_Vw`*6yrx^?5ze;dl+fBG1`!L}oPnDYTI-zn=M{a=Ax=+hwQ_ZM3klZT|YK(}`ypj@dM4%gA^J z0!`W{PZu;$y*aMwOT*BkLP>#MxIYBBzyX_eQcQf@e!>ozqS|o@>_J=m?}o4r4tFZ(B-98ZAIAsaj(6(5w%cN zT>cj(Gr_NA|_0XViocNnJ|wDl!8h^L$_L_esyNmrImb7|08jQ;A@MS*MkjeKP6xbOTSVy*D-X)6}0O>QqC}-+RdotN#q#ob~S1#ZJ2V z-JA5G_O<(?Zkdd{7(=<@Ytc$#9Kh;aG;L_!Y0dK-8X#5tY;WulagO<|16)72IbJB* z((=el-pqV}FDNR^PoXqJ)km${azd6KserY(n0zxgZimdfgtoJq= zOv3TfN_4P6@#ZSqL2U3sRR?AAd*-QUwy}zYuI!*-2A;qdEn~xsqCznkIT>ME*(gG z9Ok$tY^`{j3&XLqo;usB-@S2Fom)|hTiBY$_~z2&Q`)qi1>{~n`W3l?<1sf|??ak( zElA9>%W$pB06B}sh#jDC%KhHL*T?`gnjJepZe!OLw1Fj_|N5;)vAEDF<}t({h5#dy&p!dcGIQIk1sUyi>=$PFG&ez-9QMuQ zr5B3MY!>K&9w4|%1Q^oDWn}nF64=+^X~>wT3APikoyfK3$*!+9syl#}{NFI2R!#z_ zRZE(QR2yLtYvr;hGI+?-N`g_$%E#Q`L1K;svoRiw3LZas>F*@6Qd8o|jvOt+;t3zG z!2DHi%(OS+OKnfz-&l2CB!FJ>i&c_AZweazjU?S+a)v?K#FgJm;RCuQhmtREa&YE( zobby!@TNQ!%m(h4T)ikgjvHxrMw9E`MPbP7$zGC@`k;#)Sy5sdwVO1??5 zC)&o~{di)#^IO2Rj5?Qg9kE9L?>9Mn*8x+WGRbBjBiCB%j&4axRmpRh~B!( zf-MiK5nT8$St-u@$nI7Bp+LX2^k6*ReOisr&CaRZcenoG>MXfD$DkOy{ zC*_pAx4(OeodBL`+exrDv%Q%3aX`}o8M(y`6Dfm8?Qj1i?8cp73?Byh*NjmA*DSSmOwb2+or`mBGZ|WKWmat!XR=5)g?3uiGEQBT zAfD&XA|_nlpZxSy&}84M0j4Wb^uD?fOCGjul7)dkH!_ui6C@o0uqSu`_%g1m8MRLa z7$fn<+v`0^Jaj26MV4B#Putc#b^ikUZn3(ydAYpDwdRw7HNG`I3Vl@bQ2#JU<#mY) z+WpyfS={vXi7UG!lJw3iLyrkg)4gqlEZ_)GyWzVlJ!$!PCub_r3e3g6{d;fZh&)m0 zW>7rfQ(XC<`Mjf1@>ftU@&a6{Kqa)HfR%elYW>Ir+o+}2q;@6!wOmFyS;I`(esvhL z1r#OHM}ul%1GXD~wnd#AH32%oc$bxKD@yg~YR8cm>g71-uS!-0f%j+R6Pd{q+5yI9 zHP+OWn>|RW#l}c5xmlM{}#h%47AaGn$1-(^^{XvkB&vVJ2bF7 zTJ`Ma(AUC%m}vkdYqHi4bpBPS@>Ll}r}wqG!Qku7sPp9tsTDxS**l%m$igV!QjK@H z=iKn{)6}cSYqDYRtHBlqtzT`LJxkAvdtxGwk-OJ*wLOBapcb28Qgi}*BYvWB%w|-- zgZNkSFvbbe^r&3kQKqG*h-89rlW8(dqB{PeCV3jz2cl53l$t{2Mlfb4N4k~dqMh^$ z`5Yh-Vm(M!T;Xr38itHtn2Ez_C^p{SZ6i8(Qf9NvIIk=E% z-bpyn;R_kIz48VmF76S@dk#ktPUOUw=p&G9?Ok9NApm<21<&BYWNU{p(fqHb!69%{ zd-9;o>?>o$ap7rle?Wi!L{8ueE#}_97*0gV}1M7mOCC$nLPYvA?1SCbv&sj z8ao*qVG_)8YJ5?(Ri#tkTCy%~`(`}8Jj=C~zpc%T*;tKf+e7QRluIU} z2km>qzX~{y<=Bqj*bajvFCodQCo{h0xt7=ZxrzSqDhd47C(O(JkCP{Ej<~P*ndTI3 z80lTsc<9x6squTg^vS-S8jzWFJr1pCz#MCzmV$jIsi@~53r(xuG|NF?Io#1oCl|1v z`DMX`8xev*U`>h`l0Bd-P#S|2B9!|6|Flo-t|_8C(ultf2xi`yU=x%F**4P%M&_%v znT+Teu_Ads8A?{#tX)=Pj@iiy>1_uDP6C=`h(I7(87QK5MyWAP79bO}^P=lPylz1) zmad~6Cr&raKFlQeay72%$gOJ9{iLsdXY-JV$hQQs;TE~$Qtl^D)@;qX57}q!54Jm% zc_d->zTDfvhkIxAtSW8UnL9F6YmnBRL$v7>SGIE=eEAX0wn|pVR4p$`2PrW27Tl}T zAD=8GU~g?5I)eZVgUTNdCnIsgF7}lq4$$t0EY;p5CbbmTcaa@fnU!R{hk+a!_qsN*bfRZh(2s5eSuOP+Y~E{ zY8mERip*tL8pvrhz${x((L-_+$6ATlzRsPJ$+zu6dNQ($p*~S+jWr{lLMv!}66-*Y zImmi%LTdDu3!=!>c^q{M?WZ6K=GYXx z->5_dXxi80)YTf-vSvYc+0?T`Ptb#@z~@cf3VD; z`Tj%B!E>fqnFf0}ynPq#FMEe{=Tt9}S4aLrIK)lBu4DrYljX$0HYN<#WccVture@p zA~s{kQO&%+*>f3f8Fa>{#fc`^3v^6G{lCF6 z4EdK(={e>X>O#-wb)6GQ%hjTXTRPRtT3WD2ye*xZfvNl<4O|s(Bdg_<_Epy7wXnd5 zo3D~mGsP+@5I`sGE6X16i;u`OU`vHrXK&1o`+S;`^_o7)nHe|`C)k_4-v_xP> zdRmT;SFY2v*1U9sHsgkz<2E4Yzi{!d16)2SRZ0*8uU>5zNbZ~bYXeyY+DgcQYGROx z6y4io`vwfRR&DX*06$Xd4-aB*atc?Pir6AVgg}uW0?`qxvCu>pOz_R>LK_8wDEY9q z4B`^wB z^aPP_Y!Q9rm^nN3Y;8E1%m4vUIg6|pCdf<|e26*K&dN!+rcd;D6U zl?XvS9aHP6Y%+!?{OI&;5Tzt!b@ZL>oytsr5s?>Ql>#pgxGcvm0*jG3hSU<)0^_7V z#+LY~VKQ04hbAXZb&grhW+oPlH^vtLM^+2q(J@5Z?8zzZ!3M9}m?FrF-wfulpcofL zmYUuoScuO?)%WV~;nQ2E+ud#G>#q9{dtk-FQ)xq@_1-bMj@>fXcB$zK8HS6ZbnQoLtRTNAXZJ($qypydQQ;FVz^98GS-0}nyR$dP&yYiBQoikA0Jn_##VXN456 zQ%|pfFRgffJ8G(B&Y}G;b}jrMUf*}{ygdA&|L@fO@1`BTSdu>>-XK_i*x`nkQ?AQ2 zsj=CWQVy)2jnQlS-yjpXst`>Qz0WW$z*p6RCjo;m!-nw|@fvA|?I?kP20-2Kr-p0| zg3upANRd|1wSD-}L@7G?SoBVYsUtp(Z%haq0oasVnQMC8v!+T;5YNmU+ZFU7{EQPo zOw7w1Dwe_Q!H{0!%1MryGYT}S1(5A96r;>_h%1l({fBK=!-Dp9)!&MI@jhg0%p=F& zE$%PTnh)mPLt=_;<9>__nXrwreV*R71rRLlFkvt9Y#phKE%D_Y0Xk-WH&*|hk=%k; z8k~fpL6LwS*2$&&jrXB#s{h-T*8BfjRhU}2T2A4Md%Pu`aj6o#)vBc@lP}fPa>R%z zcQ)}}3cA&K-!Y6fwJhq7f;>mS24kA9fSYU|rJ z@A307_b2H1xcg3up|B+!JRfhBOI1#7lzVLfrAOPLc(4a#BtTYQc~OPvv&wJ2UqS%8 zpY;g-!R)Jt6NwCxC6RZ*!vZxo_kD`fp=utd0Rw0N%*#+Uuztg`zuNyZUfoWK4Fku% zsvw!n5X$a)HI{AQBlZ(%F#LG;+8sAe4%N#PcYpZ46#YBkr|hR)U#;)gR2&Lvb? z=?Fd(a9uj(EuL7Xmic)W_`?aLe_d*$TLIVM~DV^?VLQXx#I^y|HzZDCkB?`HYZ5joUIFQLZv`y*}pH z`G$ElE+fnMzdkAjJ2Grrp9~q-PoD?0?f^9Ay5=O`v7qNZCT&EyI9yVKur)RX<;w?RJ1UA@arz#?-#jp9fIzt#vy z4SvBK5VxN{-o?`fF5;OhJR(!u91*AnZG)tq0%*vczwP7Gp_Zhn!Oz`a`g8^Orwm7B zmEY+7sJXP6T1)fw>OWsj!&&I;k8=O3DPn@lhWu!y3h%#~=|BiC3XnZrXk0IYJ@ZG3 z_43JuV8&`wz#H! zxOID1T>(+II764IRmiQ9f83@~qOBgj2NYciH%>JnW5|1XNG1mQ4za7c?P@GWopSX+d4V$jC zGH>>Nly&D3jqf!twcgb~hBodjpkhoZ3q|5JcLC;~Jb;P}!$1B@2G%fv_}lc=w3VMG#tAKXM#r%}U7&=jLfpHrQ& zS3@2L8H;a7@m@r#t3afF!W zOR$a98OI-^l3%|g%Q%zI*yPuzS=a}DJ2yV@kePVK|K!~wBXOv2#8$24!kBF!Gsz2P zu1Jls7ESE6ja#b|T^K_+k&gzQw;4Ar-+Mf}R`8#gTfufvWCzj&zPnF!Rc{TQ*9x=+ zp;hHbL##2%Xj(zMW!W=aRy~m(2Asr_qr+rMYE2~(5-@r(!YA})(r+BFdGZmU&;FnG zuKlTrD+&WGbq1~2u@o(0`cwys(OO^%B~kHF#E2FR1q{If1xW)%UZDZDN=uzuw2luX zAgG8Ukc5XJUPcJ=sz+OeicBX${wryyBMjBSvGz_3l zd~hvV?fI@oqG8msisY8#pZe7JhIcr!P?8OjhSXfiT@VS|b~#{kt6>Y?SW-^Ma6jGy z*o;k%3|ktMjXUm4Yv-^lSt|Nb`?a<6Y|m|k@v*kCU32FIM>tjHh$6Gh++54HY8LXY^^$t=Da4tLg+S24U@_9oLget*XCxmt zeUe!(&cMrqlk~pFZgt_5nU(DO4r7n+)n?9H#)k6d_UhLOYe&5|wZ-^4m+4oO;&2{+ z>v-8xm?(266`9a98zGS2b)~1-h}fibpl2bPF0xUu#sP5NyB3wEQ4APcqpjzn%=MnyUQiuRt2zHDUp{sq%niwk zZx0a$R=#DZE{$UR8sK(U6IVP90;tG1o#}m8PzU@h6|ft2%yCr6;&JIXVB7b-Bz#7W4d)Q$lW2q&-+!Ptc9Me{}1v z_O!fJyTt3E|Mqx38976N%sq;SVg-Gf3o*(YOn{ftnJuy)3+L;jvbY5RvR0$uJS_N7 zs_e#X=Z^+zf-1!3;r*pfm>6o&;dO?du^sd1`gGPcadQ;Z7;Pia&9$iH3ShHtX2jj=wTbDbB#BcyD?%p|5Q4lBYfbW+ z!bB*dhJ`7k>daXYQaULaAqyQM&uYFqi+lSQl($H4-)g_W&!4!T)V+xmfOX8v2K3sX zeZCOLR|!9e2sCj&{z`GIuBoA~@F!MioTBvigORfv*dfSzev`+ z{1)R?Ju2+qkCsba!>`gnYTNbMtMFH>j+weKsfNh2+*YW)m?oJM zbQyA5e(o}E$6tj(d`TNjy1DT>&2A&*$qnWs{bhZpbZe6jA=Q=cl4#-x+W3j`TIKpA zjIN-FWz{FrsxB%MzRge!Qnysw**TO4?b&s-)T5bK##uTHzR4vlPS z?l$E&hI2R(Fu^k@mUg@%q_5>P;h6t(uzduThvd~CQiU&oMU);6ouMh6j(bFhe!+Ak zh1##EF!jTO9lzxa>~iDh4#9(E^rC?5=8pX+FX{%E+9a>|BMb_pDE0U|;m*F61sc=B zd_AdvVV { - return Platform.OS === 'ios' ? ( - - {children} - - ) : ( - <>{children} - ); +type TextInputDemoProps = { + variant: TextInputVariant; }; -const TextInputExample = () => { - const [state, dispatch] = React.useReducer(inputReducer, initialState); - const { - text, - customIconText, - name, - outlinedText, - largeText, - flatTextPassword, - flatLongText, - outlinedLargeText, - outlinedCustomLabel, - outlinedTextPassword, - outlinedLongText, - nameNoPadding, - customStyleText, - nameRequired, - flatDenseText, - flatDense, - outlinedDenseText, - outlinedDense, - flatMultiline, - flatTextArea, - flatUnderlineColors, - outlinedMultiline, - outlinedTextArea, - outlinedColors, - maxLengthName, - flatTextSecureEntry, - outlineTextSecureEntry, - iconsColor: { - flatLeftIcon, - flatRightIcon, - outlineLeftIcon, - outlineRightIcon, - customIcon, - }, - } = state; - - const _isUsernameValid = (name: string) => /^[a-zA-Z]*$/.test(name); - +const TextInputDemo = ({ variant }: TextInputDemoProps) => { const theme = useTheme(); - const inputActionHandler = (type: keyof State, payload: string) => - dispatch({ - type: type, - payload: payload, - }); + const [value, setValue] = React.useState(''); + + const [controls, setControls] = React.useState({ + error: false, + disabled: false, + readOnly: false, + leadingIcon: false, + trailingIcon: false, + counter: false, + showPrefix: false, + showSuffix: false, + multiline: false, + }); - const changeIconColor = (name: keyof State['iconsColor']) => { - const color = state.iconsColor[name]; + const [modifiers, setModifiers] = React.useState({ + label: 'Label', + helperText: 'Supporting text', + placeholder: 'Placeholder', + prefix: '$', + suffix: '/100', + }); - const newColors = { - ...state.iconsColor, - [name]: !color ? theme.colors.primary : undefined, - }; + const toggleControl = (key: keyof DemoControls) => + setControls((prev) => ({ ...prev, [key]: !prev[key] })); - dispatch({ - type: 'iconsColor', - payload: newColors, - }); - }; + const setModifier = (key: keyof DemoModifiers, text: string) => + setModifiers((prev) => ({ ...prev, [key]: text })); - const [fontsLoaded] = useFonts({ - Abel: require('../../assets/fonts/Abel-Regular.ttf'), - }); + const leadingIcon = (props: TextInputAccessoryProps) => ( + + ); - const [expandedId, setExpandedId] = React.useState('flat'); + const trailingIcon = (props: TextInputAccessoryProps) => ( + setValue('')} /> + ); - const onAccordionPress = (id: string | number) => - setExpandedId(expandedId === id ? undefined : id); + const inputColor = theme.colors.onSurfaceVariant; + const borderColor = theme.colors.outlineVariant; - return ( - - - - - inputActionHandler('text', text)} - left={ - { - changeIconColor('flatLeftIcon'); - }} - /> - } - maxLength={100} - right={} - /> - - inputActionHandler('customIconText', text) - } - maxLength={100} - right={} - left={ - ( - { - changeIconColor('customIcon'); - }} - /> - )} - /> - } - /> - - inputActionHandler('largeText', largeText) - } - left={} - right={ - { - changeIconColor('flatRightIcon'); - }} - /> - } - /> - - inputActionHandler('flatTextPassword', flatTextPassword) - } - secureTextEntry={flatTextSecureEntry} - right={ - - dispatch({ - type: 'flatTextSecureEntry', - payload: !flatTextSecureEntry, - }) - } - forceTextInputFocus={false} - /> - } - /> - - inputActionHandler('flatLongText', flatLongText) - } - /> - - - - inputActionHandler('outlinedText', outlinedText) - } - left={ - { - changeIconColor('outlineLeftIcon'); - }} - /> - } - maxLength={100} - right={} - /> - - inputActionHandler('outlinedLargeText', outlinedLargeText) - } - left={} - right={ - { - changeIconColor('outlineRightIcon'); - }} - /> - } - /> - Custom label} - placeholder="Type something" - value={outlinedCustomLabel} - onChangeText={(outlinedCustomLabel) => - inputActionHandler('outlinedCustomLabel', outlinedCustomLabel) - } - /> - - inputActionHandler('outlinedTextPassword', outlinedTextPassword) - } - secureTextEntry={outlineTextSecureEntry} - right={ - - dispatch({ - type: 'outlineTextSecureEntry', - payload: !outlineTextSecureEntry, - }) - } - /> - } - /> - - inputActionHandler('outlinedLongText', outlinedLongText) - } - /> - - - - - { - changeIconColor('flatLeftIcon'); - }} - /> - } - right={} - /> - - - { - changeIconColor('flatLeftIcon'); - }} - /> - } - right={} - /> - - - - inputActionHandler('flatDenseText', flatDenseText) - } - left={} - right={ - - focused ? theme.colors?.primary : undefined - } - /> - } - /> - - inputActionHandler('flatDense', flatDense) - } - /> - - inputActionHandler('outlinedDenseText', outlinedDenseText) - } - left={} - /> - - inputActionHandler('outlinedDense', outlinedDense) - } - /> - - - - inputActionHandler('flatMultiline', flatMultiline) - } - /> - - inputActionHandler('flatTextArea', flatTextArea) - } - /> - - - - - inputActionHandler('outlinedMultiline', outlinedMultiline) - } - /> - - inputActionHandler('outlinedTextArea', outlinedTextArea) - } - /> - - - - - - - inputActionHandler('name', name)} - /> - - Error: Only letters are allowed - - - - - inputActionHandler('maxLengthName', maxLengthName) - } - maxLength={MAX_LENGTH} - /> - - - Error: Numbers and special characters are not allowed - - - {maxLengthName.length} / {MAX_LENGTH} - - - - - - - * - {' '} - Label as component - - } - style={styles.noPaddingInput} - placeholder="Enter username, required" - value={nameRequired} - error={!nameRequired} - onChangeText={(nameRequired) => - inputActionHandler('nameRequired', nameRequired) - } - /> - - Error: Username is required - - - - - - inputActionHandler('flatUnderlineColors', flatUnderlineColors) - } - underlineColor={Palette.primary70} - activeUnderlineColor={Palette.tertiary50} - /> - - inputActionHandler('outlinedColors', outlinedColors) - } - outlineColor={Palette.primary70} - activeOutlineColor={Palette.tertiary50} - /> - - inputActionHandler('outlinedLongLabel', outlinedLongLabel) - } - /> + const modifierInputStyle: TextStyle = { + flex: 1, + color: inputColor, + fontSize: 14, + paddingVertical: 4, + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: borderColor, + }; - - inputActionHandler('customStyleText', customStyleText) - } - contentStyle={styles.inputContentStyle} - /> + const SWITCH_CONTROLS: { label: string; key: keyof DemoControls }[] = [ + { label: 'Error', key: 'error' }, + { label: 'Disabled', key: 'disabled' }, + { label: 'Readonly', key: 'readOnly' }, + { label: 'Leading icon', key: 'leadingIcon' }, + { label: 'Trailing icon', key: 'trailingIcon' }, + { label: 'Counter', key: 'counter' }, + { label: 'Prefix', key: 'showPrefix' }, + { label: 'Suffix', key: 'showSuffix' }, + { label: 'Multiline', key: 'multiline' }, + ]; + + const MODIFIER_FIELDS: { label: string; key: keyof DemoModifiers }[] = [ + { label: 'Label', key: 'label' }, + { label: 'Helper', key: 'helperText' }, + { label: 'Placeholder', key: 'placeholder' }, + { label: 'Prefix', key: 'prefix' }, + { label: 'Suffix', key: 'suffix' }, + ]; - - - inputActionHandler('nameNoPadding', nameNoPadding) - } - /> - - Error: Only letters are allowed - - + return ( + + {/* Live TextInput */} + + + + + {/* Controls */} + Controls + {SWITCH_CONTROLS.map(({ label, key }) => ( + toggleControl(key)}> + + {label} + + + + + + ))} + + + + {/* Modifiers */} + Modifiers + {MODIFIER_FIELDS.map(({ label, key }) => ( + + + {label} + + setModifier(key, text)} + style={modifierInputStyle} + placeholderTextColor={theme.colors.outline} + placeholder={`Enter ${label.toLowerCase()}…`} + /> + + ))} + + ); +}; - - - - - - - - - - - - - - - - - - - - - - - - - {fontsLoaded ? ( - - - - ) : null} - - - - - - - - - - - - - - - - - - - - - - } - /> - - - - - - - - } - /> - - - - - - +const TextInputExample = () => { + return ( + + + + + + + + ); }; TextInputExample.title = 'TextInput'; const styles = StyleSheet.create({ - helpersWrapper: { - flexDirection: 'row', - justifyContent: 'space-between', - }, - wrapper: { - flex: 1, - }, - helper: { - flexShrink: 1, - }, - counterHelper: { - textAlign: 'right', - }, - inputContainerStyle: { - margin: 8, - }, - inputContentStyle: { - paddingLeft: 50, - fontWeight: 'bold', - fontStyle: 'italic', - }, - fontSize: { - fontSize: 32, - }, - textArea: { - height: 80, - }, - // eslint-disable-next-line react-native/no-color-literals - noPaddingInput: { - backgroundColor: 'transparent', + container: { + paddingHorizontal: 16, + paddingVertical: 8, + } satisfies ViewStyle, + demoContainer: { + gap: 4, + } satisfies ViewStyle, + divider: { + marginVertical: 8, + } satisfies ViewStyle, + subheader: { paddingHorizontal: 0, - }, - centeredText: { - textAlign: 'center', - }, - fixedHeight: { - height: 100, - }, - row: { - margin: 8, + } satisfies TextStyle, + switchRow: { + flexDirection: 'row', + alignItems: 'center', justifyContent: 'space-between', + paddingVertical: 8, + paddingHorizontal: 8, + } satisfies ViewStyle, + modifierRow: { flexDirection: 'row', - }, - month: { - flex: 1, - marginRight: 4, - }, - year: { - flex: 1, - marginLeft: 4, - }, - inputLabelText: { - color: Palette.tertiary70, - }, - left: { - width: '30%', - }, - right: { - width: '70%', - }, - autoText: { - textAlign: 'auto', - }, + alignItems: 'center', + gap: 12, + paddingVertical: 8, + paddingHorizontal: 8, + } satisfies ViewStyle, + modifierLabel: { + width: 80, + } satisfies TextStyle, }); export default TextInputExample; diff --git a/jest/testSetup.js b/jest/testSetup.js index 5110c85551..c00e611084 100644 --- a/jest/testSetup.js +++ b/jest/testSetup.js @@ -18,17 +18,14 @@ jest.mock('@react-native-vector-icons/material-design-icons', () => { const MockIcon = ({ name, color, size, style, ...props }) => { return ( - + {name || '□'} ); }; MockIcon.displayName = 'MockedMaterialDesignIcon'; - + return { __esModule: true, default: MockIcon, diff --git a/src/components/HelperText/HelperText.tsx b/src/components/HelperText/HelperText.tsx deleted file mode 100644 index 6fc44d58a5..0000000000 --- a/src/components/HelperText/HelperText.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import * as React from 'react'; -import { - Animated, - LayoutChangeEvent, - StyleProp, - StyleSheet, - TextStyle, - View, -} from 'react-native'; - -import { getTextColor } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { $Omit, ThemeProp } from '../../types'; -import AnimatedText from '../Typography/AnimatedText'; - -export type Props = $Omit< - $Omit, 'padding'>, - 'type' -> & { - /** - * Type of the helper text. - */ - type: 'error' | 'info'; - /** - * Text content of the HelperText. - */ - children: React.ReactNode; - /** - * Whether to display the helper text. - */ - visible?: boolean; - /** - * Whether to apply padding to the helper text. - */ - padding?: 'none' | 'normal'; - /** - * Whether the text input tied with helper text is disabled. - */ - disabled?: boolean; - style?: StyleProp; - /** - * @optional - */ - theme?: ThemeProp; - /** - * TestID used for testing purposes - */ - testID?: string; -}; - -/** - * Helper text is used in conjuction with input elements to provide additional hints for the user. - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { View } from 'react-native'; - * import { HelperText, TextInput } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [text, setText] = React.useState(''); - * - * const onChangeText = text => setText(text); - * - * const hasErrors = () => { - * return !text.includes('@'); - * }; - * - * return ( - * - * - * - * Email address is invalid! - * - * - * ); - * }; - * - * export default MyComponent; - * ``` - */ -const HelperText = ({ - style, - type = 'info', - visible = true, - theme: themeOverrides, - onLayout, - padding = 'normal', - disabled, - ...rest -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const { current: shown } = React.useRef( - new Animated.Value(visible ? 1 : 0) - ); - - let { current: textHeight } = React.useRef(0); - - const { scale } = theme.animation; - - const { maxFontSizeMultiplier = 1.5 } = rest; - - React.useEffect(() => { - if (visible) { - // show text - Animated.timing(shown, { - toValue: 1, - duration: 150 * scale, - useNativeDriver: true, - }).start(); - } else { - // hide text - Animated.timing(shown, { - toValue: 0, - duration: 180 * scale, - useNativeDriver: true, - }).start(); - } - }, [visible, scale, shown]); - - const handleTextLayout = (e: LayoutChangeEvent) => { - onLayout?.(e); - textHeight = e.nativeEvent.layout.height; - }; - - const { color: textColor, opacity: textOpacity } = getTextColor({ - theme, - disabled, - type, - }); - - return ( - - - {rest.children} - - - ); -}; - -const styles = StyleSheet.create({ - text: { - fontSize: 12, - paddingVertical: 4, - }, - padding: { - paddingHorizontal: 12, - }, -}); - -export default HelperText; diff --git a/src/components/HelperText/utils.ts b/src/components/HelperText/utils.ts deleted file mode 100644 index 86c39c2e45..0000000000 --- a/src/components/HelperText/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { getStateLayer } from '../../theme/utils/state'; -import type { InternalTheme } from '../../types'; - -type BaseProps = { - theme: InternalTheme; - disabled?: boolean; - type?: 'error' | 'info'; -}; - -export function getTextColor({ theme, disabled, type }: BaseProps) { - if (type === 'error') { - return getStateLayer(theme, 'error', 'enabled'); - } - return getStateLayer( - theme, - 'onSurfaceVariant', - disabled ? 'disabled' : 'enabled' - ); -} diff --git a/src/components/TextInput/Addons/Outline.tsx b/src/components/TextInput/Addons/Outline.tsx deleted file mode 100644 index c40f5ffd62..0000000000 --- a/src/components/TextInput/Addons/Outline.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import * as React from 'react'; -import { - StyleSheet, - ColorValue, - StyleProp, - View, - ViewStyle, -} from 'react-native'; - -import { TextInputLabelProp } from '../types'; - -type OutlineProps = { - activeColor: ColorValue; - backgroundColor: ColorValue; - hasActiveOutline?: boolean; - outlineColor?: ColorValue; - roundness?: number; - label?: TextInputLabelProp; - style?: StyleProp; -}; - -export const Outline = ({ - label, - activeColor, - backgroundColor, - hasActiveOutline, - outlineColor, - roundness, - style, -}: OutlineProps) => ( - -); - -const styles = StyleSheet.create({ - outline: { - position: 'absolute', - left: 0, - right: 0, - top: 6, - bottom: 0, - }, - noLabelOutline: { - top: 0, - }, -}); diff --git a/src/components/TextInput/Addons/Underline.tsx b/src/components/TextInput/Addons/Underline.tsx deleted file mode 100644 index b329ef1190..0000000000 --- a/src/components/TextInput/Addons/Underline.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import * as React from 'react'; -import { - Animated, - ColorValue, - StyleProp, - StyleSheet, - ViewStyle, -} from 'react-native'; - -import type { ThemeProp } from '../../../types'; - -type UnderlineProps = { - parentState: { - focused: boolean; - }; - error?: boolean; - colors?: { - error?: ColorValue; - }; - activeColor: ColorValue; - underlineColorCustom?: ColorValue; - hasActiveOutline?: boolean; - disabledOpacity?: number; - style?: StyleProp; - theme?: ThemeProp; -}; - -export const Underline = ({ - parentState, - error, - colors, - activeColor, - underlineColorCustom, - hasActiveOutline, - disabledOpacity, - style, - theme: _themeOverrides, -}: UnderlineProps) => { - let backgroundColor = parentState.focused - ? activeColor - : underlineColorCustom; - - if (error) backgroundColor = colors?.error; - - const activeScale = 2; - - return ( - - ); -}; - -const styles = StyleSheet.create({ - underline: { - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - height: 2, - zIndex: 1, - }, - md3Underline: { - height: 1, - }, -}); diff --git a/src/components/TextInput/Adornment/TextInputAdornment.tsx b/src/components/TextInput/Adornment/TextInputAdornment.tsx deleted file mode 100644 index 6295908025..0000000000 --- a/src/components/TextInput/Adornment/TextInputAdornment.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import React from 'react'; -import type { - LayoutChangeEvent, - TextStyle, - StyleProp, - Animated, - DimensionValue, -} from 'react-native'; - -import { AdornmentSide, AdornmentType, InputMode } from './enums'; -import TextInputAffix, { AffixAdornment } from './TextInputAffix'; -import TextInputIcon, { IconAdornment } from './TextInputIcon'; -import type { - AdornmentConfig, - AdornmentStyleAdjustmentForNativeInput, -} from './types'; -import { getConstants } from '../helpers'; - -export function getAdornmentConfig({ - left, - right, -}: { - left?: React.ReactNode; - right?: React.ReactNode; -}): Array { - let adornmentConfig: any[] = []; - if (left || right) { - [ - { side: AdornmentSide.Left, adornment: left }, - { side: AdornmentSide.Right, adornment: right }, - ].forEach(({ side, adornment }) => { - if (adornment && React.isValidElement(adornment)) { - let type; - if (adornment.type === TextInputAffix) { - type = AdornmentType.Affix; - } else if (adornment.type === TextInputIcon) { - type = AdornmentType.Icon; - } - adornmentConfig.push({ - side, - type, - }); - } - }); - } - - return adornmentConfig; -} - -export function getAdornmentStyleAdjustmentForNativeInput({ - adornmentConfig, - leftAffixWidth, - rightAffixWidth, - paddingHorizontal, - inputOffset = 0, - mode, -}: { - inputOffset?: number; - adornmentConfig: AdornmentConfig[]; - leftAffixWidth: number; - rightAffixWidth: number; - mode?: 'outlined' | 'flat'; - paddingHorizontal?: DimensionValue; -}): AdornmentStyleAdjustmentForNativeInput | {} { - const { OUTLINED_INPUT_OFFSET, ADORNMENT_OFFSET } = getConstants(); - - if (adornmentConfig.length) { - const adornmentStyleAdjustmentForNativeInput = adornmentConfig.map( - ({ type, side }: AdornmentConfig) => { - const isLeftSide = side === AdornmentSide.Left; - const inputModeAdornemntOffset = - mode === InputMode.Outlined - ? ADORNMENT_OFFSET + OUTLINED_INPUT_OFFSET - : ADORNMENT_OFFSET; - const paddingKey = `padding${captalize(side)}`; - const affixWidth = isLeftSide ? leftAffixWidth : rightAffixWidth; - const padding = - typeof paddingHorizontal === 'number' - ? paddingHorizontal - : inputModeAdornemntOffset; - const offset = affixWidth + padding; - - const isAffix = type === AdornmentType.Affix; - const marginKey = `margin${captalize(side)}`; - - return { - [marginKey]: isAffix ? 0 : offset, - [paddingKey]: isAffix ? offset : inputOffset, - }; - } - ); - const allStyleAdjustmentsMerged = - adornmentStyleAdjustmentForNativeInput.reduce( - (mergedStyles, currentStyle) => { - return { - ...mergedStyles, - ...currentStyle, - }; - }, - {} - ); - return allStyleAdjustmentsMerged; - } else { - return [{}]; - } -} - -const captalize = (text: string) => - text.charAt(0).toUpperCase() + text.slice(1); - -export interface TextInputAdornmentProps { - forceFocus: () => void; - adornmentConfig: AdornmentConfig[]; - topPosition: { - [AdornmentType.Affix]: { - [AdornmentSide.Left]: number | null; - [AdornmentSide.Right]: number | null; - }; - [AdornmentType.Icon]: number; - }; - onAffixChange: { - [AdornmentSide.Left]: (event: LayoutChangeEvent) => void; - [AdornmentSide.Right]: (event: LayoutChangeEvent) => void; - }; - left?: React.ReactNode; - right?: React.ReactNode; - textStyle?: StyleProp; - visible?: Animated.Value; - isTextInputFocused: boolean; - paddingHorizontal?: DimensionValue; - maxFontSizeMultiplier?: number | undefined | null; - disabled?: boolean; -} - -const TextInputAdornment: React.FunctionComponent = ({ - adornmentConfig, - left, - right, - onAffixChange, - textStyle, - visible, - topPosition, - isTextInputFocused, - forceFocus, - paddingHorizontal, - maxFontSizeMultiplier, - disabled, -}) => { - if (adornmentConfig.length) { - return ( - <> - {adornmentConfig.map(({ type, side }: AdornmentConfig) => { - let inputAdornmentComponent; - if (side === AdornmentSide.Left) { - inputAdornmentComponent = left; - } else if (side === AdornmentSide.Right) { - inputAdornmentComponent = right; - } - - const commonProps = { - side: side, - testID: `${side}-${type}-adornment`, - isTextInputFocused, - paddingHorizontal, - disabled, - }; - if (type === AdornmentType.Icon) { - return ( - - ); - } else if (type === AdornmentType.Affix) { - return ( - - ); - } else { - return null; - } - })} - - ); - } else { - return null; - } -}; - -export default TextInputAdornment; diff --git a/src/components/TextInput/Adornment/TextInputAffix.tsx b/src/components/TextInput/Adornment/TextInputAffix.tsx deleted file mode 100644 index 94a0c08662..0000000000 --- a/src/components/TextInput/Adornment/TextInputAffix.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import React from 'react'; -import { - Animated, - DimensionValue, - GestureResponderEvent, - LayoutChangeEvent, - Pressable, - StyleProp, - StyleSheet, - Text, - TextStyle, - ViewStyle, -} from 'react-native'; - -import { AdornmentSide } from './enums'; -import { getTextColor } from './utils'; -import { useInternalTheme } from '../../../core/theming'; -import type { ThemeProp } from '../../../types'; -import { getConstants } from '../helpers'; - -export type Props = { - /** - * Text to show. - */ - text: string; - onLayout?: (event: LayoutChangeEvent) => void; - /** - * Function to execute on press. - */ - onPress?: (e: GestureResponderEvent) => void; - /** - * Accessibility label for the affix. This is read by the screen reader when the user taps the affix. - */ - accessibilityLabel?: string; - /** - * Style that is passed to the Text element. - */ - textStyle?: StyleProp; - /** - * @optional - */ - theme?: ThemeProp; -}; - -type ContextState = { - topPosition: number | null; - onLayout?: (event: LayoutChangeEvent) => void; - visible?: Animated.Value; - textStyle?: StyleProp; - side: AdornmentSide; - paddingHorizontal?: DimensionValue; - maxFontSizeMultiplier?: number | undefined | null; - testID?: string; - disabled?: boolean; -}; - -const AffixContext = React.createContext({ - textStyle: { fontFamily: '', color: '' }, - topPosition: null, - side: AdornmentSide.Left, -}); - -const AffixAdornment: React.FunctionComponent< - { - affix: React.ReactNode; - testID: string; - } & ContextState -> = ({ - affix, - side, - textStyle, - topPosition, - onLayout, - visible, - paddingHorizontal, - maxFontSizeMultiplier, - testID, - disabled, -}) => { - return ( - - {affix} - - ); -}; - -/** - * A component to render a leading / trailing text in the TextInput - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { TextInput } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [text, setText] = React.useState(''); - * - * return ( - * } - * /> - * ); - * }; - * - * export default MyComponent; - * ``` - */ - -const TextInputAffix = ({ - text, - textStyle: labelStyle, - theme: themeOverrides, - onLayout: onTextLayout, - onPress, - accessibilityLabel = text, -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const { AFFIX_OFFSET } = getConstants(); - - const { - textStyle, - onLayout, - topPosition, - side, - visible, - paddingHorizontal, - maxFontSizeMultiplier, - testID, - disabled, - } = React.useContext(AffixContext); - - const offset = - typeof paddingHorizontal === 'number' ? paddingHorizontal : AFFIX_OFFSET; - - const style = { - top: topPosition, - [side]: offset, - } as ViewStyle; - - const { color: textColor, opacity: textOpacity } = getTextColor({ - theme, - disabled, - }); - - const content = ( - - {text} - - ); - - return ( - - {onPress ? ( - - {content} - - ) : ( - content - )} - - ); -}; - -TextInputAffix.displayName = 'TextInput.Affix'; - -const styles = StyleSheet.create({ - container: { - position: 'absolute', - justifyContent: 'center', - alignItems: 'center', - }, -}); - -export default TextInputAffix; - -// @component-docs ignore-next-line -export { TextInputAffix, AffixAdornment }; diff --git a/src/components/TextInput/Adornment/TextInputIcon.tsx b/src/components/TextInput/Adornment/TextInputIcon.tsx deleted file mode 100644 index 4652c7612c..0000000000 --- a/src/components/TextInput/Adornment/TextInputIcon.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import React from 'react'; -import { - ColorValue, - GestureResponderEvent, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; - -import { getIconColor } from './utils'; -import { useInternalTheme } from '../../../core/theming'; -import type { $Omit, ThemeProp } from '../../../types'; -import type { IconSource } from '../../Icon'; -import IconButton from '../../IconButton/IconButton'; -import { ICON_SIZE } from '../constants'; -import { getConstants } from '../helpers'; - -export type Props = $Omit< - React.ComponentProps, - 'icon' | 'theme' | 'color' | 'iconColor' -> & { - /** - * @renamed Renamed from 'name' to 'icon` in v5.x - * Icon to show. - */ - icon: IconSource; - /** - * Function to execute on press. - */ - onPress?: (e: GestureResponderEvent) => void; - /** - * Whether the TextInput will focus after onPress. - */ - forceTextInputFocus?: boolean; - /** - * Color of the icon or a function receiving a boolean indicating whether the TextInput is focused and returning the color. - */ - color?: - | ColorValue - | ((isTextInputFocused: boolean) => ColorValue | undefined); - style?: StyleProp; - /** - * @optional - */ - theme?: ThemeProp; -}; - -type StyleContextType = { - style: StyleProp; - isTextInputFocused: boolean; - forceFocus: () => void; - testID: string; - disabled?: boolean; -}; - -const StyleContext = React.createContext({ - style: {}, - isTextInputFocused: false, - forceFocus: () => {}, - testID: '', -}); - -const IconAdornment: React.FunctionComponent< - { - testID: string; - icon: React.ReactNode; - topPosition: number; - side: 'left' | 'right'; - disabled?: boolean; - } & Omit -> = ({ - icon, - topPosition, - side, - isTextInputFocused, - forceFocus, - testID, - disabled, -}) => { - const { ICON_OFFSET } = getConstants(); - - const style = { - top: topPosition, - [side]: ICON_OFFSET, - }; - const contextState = { - style, - isTextInputFocused, - forceFocus, - testID, - disabled, - }; - - return ( - {icon} - ); -}; - -/** - * A component to render a leading / trailing icon in the TextInput - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { TextInput } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [text, setText] = React.useState(''); - * - * return ( - * } - * /> - * ); - * }; - * - * export default MyComponent; - * ``` - */ - -const TextInputIcon = ({ - icon, - onPress, - forceTextInputFocus = true, - color: customColor, - theme: themeOverrides, - ...rest -}: Props) => { - const { style, isTextInputFocused, forceFocus, testID, disabled } = - React.useContext(StyleContext); - - const onPressWithFocusControl = React.useCallback( - (e: GestureResponderEvent) => { - if (forceTextInputFocus && !isTextInputFocused) { - forceFocus(); - } - - onPress?.(e); - }, - [forceTextInputFocus, forceFocus, isTextInputFocused, onPress] - ); - - const theme = useInternalTheme(themeOverrides); - - const { color: iconColor, opacity: iconOpacity } = getIconColor({ - theme, - disabled, - isTextInputFocused, - customColor, - }); - - return ( - - - - ); -}; -TextInputIcon.displayName = 'TextInput.Icon'; - -const styles = StyleSheet.create({ - container: { - position: 'absolute', - width: ICON_SIZE, - height: ICON_SIZE, - justifyContent: 'center', - alignItems: 'center', - }, - iconButton: { - margin: 0, - }, -}); - -export default TextInputIcon; - -// @component-docs ignore-next-line -export { IconAdornment }; diff --git a/src/components/TextInput/Adornment/enums.tsx b/src/components/TextInput/Adornment/enums.tsx deleted file mode 100644 index 9a364f7215..0000000000 --- a/src/components/TextInput/Adornment/enums.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export enum AdornmentType { - Icon = 'icon', - Affix = 'affix', -} -export enum AdornmentSide { - Right = 'right', - Left = 'left', -} -export enum InputMode { - Outlined = 'outlined', - Flat = 'flat', -} diff --git a/src/components/TextInput/Adornment/types.tsx b/src/components/TextInput/Adornment/types.tsx deleted file mode 100644 index fbd81c936a..0000000000 --- a/src/components/TextInput/Adornment/types.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type { AdornmentSide, AdornmentType } from './enums'; - -export type AdornmentConfig = { - side: AdornmentSide; - type: AdornmentType; -}; -export type AdornmentStyleAdjustmentForNativeInput = { - adornmentStyleAdjustmentForNativeInput: Array< - { paddingRight: number; paddingLeft: number } | {} - >; -}; diff --git a/src/components/TextInput/Adornment/utils.ts b/src/components/TextInput/Adornment/utils.ts deleted file mode 100644 index 3a6e819963..0000000000 --- a/src/components/TextInput/Adornment/utils.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { ColorValue } from 'react-native'; - -import { tokens } from '../../../theme/tokens'; -import { getStateLayer } from '../../../theme/utils/state'; -import type { InternalTheme } from '../../../types'; - -const stateOpacity = tokens.md.sys.state.opacity; - -type BaseProps = { - theme: InternalTheme; - disabled?: boolean; -}; - -export function getTextColor({ theme, disabled }: BaseProps) { - return getStateLayer( - theme, - 'onSurfaceVariant', - disabled ? 'disabled' : 'enabled' - ); -} - -export function getIconColor({ - theme, - isTextInputFocused, - disabled, - customColor, -}: BaseProps & { - isTextInputFocused: boolean; - customColor?: - | ColorValue - | ((isTextInputFocused: boolean) => ColorValue | undefined); -}) { - const color = - typeof customColor === 'function' - ? customColor(isTextInputFocused) - : customColor ?? theme.colors.onSurfaceVariant; - - const opacity = - disabled && !customColor ? stateOpacity.disabled : stateOpacity.enabled; - - return { color, opacity }; -} diff --git a/src/components/TextInput/Label/InputLabel.tsx b/src/components/TextInput/Label/InputLabel.tsx deleted file mode 100644 index 6fd869a1c3..0000000000 --- a/src/components/TextInput/Label/InputLabel.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import React from 'react'; -import { - Animated, - ColorValue, - Platform, - StyleSheet, - useWindowDimensions, - View, -} from 'react-native'; - -import AnimatedText from '../../Typography/AnimatedText'; -import { getConstants } from '../helpers'; -import type { InputLabelProps } from '../types'; - -const InputLabel = (props: InputLabelProps) => { - const { - labeled, - wiggle, - error, - focused, - labelLayoutWidth, - labelLayoutHeight, - labelBackground, - label, - labelError, - onLayoutAnimatedText, - onLabelTextLayout, - hasActiveOutline, - activeColor, - placeholderStyle, - baseLabelTranslateX, - baseLabelTranslateY, - font, - fontSize, - lineHeight, - fontWeight, - placeholderOpacity, - wiggleOffsetX, - labelScale, - topPosition, - paddingLeft, - paddingRight, - backgroundColor, - roundness, - placeholderColor, - disabledOpacity, - opacity, - errorColor, - labelTranslationXOffset, - maxFontSizeMultiplier, - testID, - inputContainerLayout, - scaledLabel, - } = props; - - const { INPUT_PADDING_HORIZONTAL } = getConstants(); - const { width } = useWindowDimensions(); - - const isWeb = Platform.OS === 'web'; - - const paddingOffset = - paddingLeft && paddingRight ? { paddingLeft, paddingRight } : {}; - - const labelTranslationX = { - transform: [ - { - // Offset label scale since RN doesn't support transform origin - translateX: labeled.interpolate({ - inputRange: [0, 1], - outputRange: [baseLabelTranslateX, labelTranslationXOffset || 0], - }), - }, - ], - }; - - const labelStyle = { - ...font, - fontSize, - lineHeight, - fontWeight, - opacity: labeled.interpolate({ - inputRange: [0, 1], - outputRange: [hasActiveOutline ? 1 : 0, 0], - }), - transform: [ - { - // Wiggle the label when there's an error - translateX: wiggle - ? error.interpolate({ - inputRange: [0, 0.5, 1], - outputRange: [0, wiggleOffsetX, 0], - }) - : 0, - }, - { - // Move label to top - translateY: - baseLabelTranslateY !== 0 - ? labeled.interpolate({ - inputRange: [0, 1], - outputRange: [baseLabelTranslateY, 0], - }) - : 0, - }, - { - // Make label smaller - scale: - labelScale !== 0 - ? labeled.interpolate({ - inputRange: [0, 1], - outputRange: [labelScale, 1], - }) - : labeled, - }, - ], - }; - - const labelWidth = - (inputContainerLayout.width + INPUT_PADDING_HORIZONTAL / 2) / - (scaledLabel ? labelScale : 1); - - const commonStyles = [ - placeholderStyle, - { - top: topPosition, - }, - { - maxWidth: labelWidth, - }, - labelStyle, - paddingOffset || {}, - ]; - - const textColor = ( - labelError && errorColor ? errorColor : placeholderColor - ) as ColorValue; - - return ( - // Position colored placeholder and gray placeholder on top of each other and crossfade them - // This gives the effect of animating the color, but allows us to use native driver - - - - {labelBackground?.({ - labeled, - labelLayoutWidth, - labelLayoutHeight, - labelStyle, - placeholderStyle, - baseLabelTranslateX, - topPosition, - label, - backgroundColor, - roundness, - maxFontSizeMultiplier: maxFontSizeMultiplier, - testID, - })} - - {label} - - - {label} - - - - - ); -}; - -const styles = StyleSheet.create({ - overflow: { - overflow: 'hidden', - }, - labelContainer: { - zIndex: 3, - }, -}); - -export default React.memo(InputLabel); diff --git a/src/components/TextInput/Label/LabelBackground.tsx b/src/components/TextInput/Label/LabelBackground.tsx deleted file mode 100644 index 409606d208..0000000000 --- a/src/components/TextInput/Label/LabelBackground.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import * as React from 'react'; -import { Animated, StyleSheet } from 'react-native'; - -import AnimatedText from '../../Typography/AnimatedText'; -import type { LabelBackgroundProps } from '../types'; - -const LabelBackground = ({ - labeled, - labelLayoutWidth, - labelLayoutHeight, - placeholderStyle, - baseLabelTranslateX, - topPosition, - backgroundColor, - roundness, - labelStyle, - maxFontSizeMultiplier, - testID, -}: LabelBackgroundProps) => { - const opacity = labeled.interpolate({ - inputRange: [0, 0.6], - outputRange: [1, 0], - }); - - const labelTranslationX = { - translateX: labeled.interpolate({ - inputRange: [0, 1], - outputRange: [-baseLabelTranslateX, 0], - }), - }; - - const labelTextScaleY = { - scaleY: labeled.interpolate({ - inputRange: [0, 1], - outputRange: [0.2, 1], - }), - }; - - const labelTextTransform = [...labelStyle.transform, labelTextScaleY]; - - const isRounded = roundness > 6; - const roundedEdgeCover = isRounded ? ( - - ) : null; - - return [ - roundedEdgeCover, - , - ]; -}; - -export default LabelBackground; - -const styles = StyleSheet.create({ - view: { - position: 'absolute', - top: 6, - left: 10, - width: 12, - }, - // eslint-disable-next-line react-native/no-color-literals - outlinedLabel: { - position: 'absolute', - left: 8, - paddingHorizontal: 0, - color: 'transparent', - }, -}); diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx index 7c289d9152..2f7b122613 100644 --- a/src/components/TextInput/TextInput.tsx +++ b/src/components/TextInput/TextInput.tsx @@ -1,202 +1,251 @@ -import * as React from 'react'; +import React from 'react'; import { - Animated, - LayoutChangeEvent, + AccessibilityProps, + BlurEvent, + ColorValue, + FocusEvent, + Pressable, StyleProp, + Text, TextInput as NativeTextInput, + TextInputProps as NativeTextInputProps, TextStyle, + View, ViewStyle, - NativeSyntheticEvent, - TextLayoutEventData, } from 'react-native'; -import TextInputAffix, { - Props as TextInputAffixProps, -} from './Adornment/TextInputAffix'; -import TextInputIcon, { - Props as TextInputIconProps, -} from './Adornment/TextInputIcon'; -import TextInputFlat from './TextInputFlat'; -import TextInputOutlined from './TextInputOutlined'; -import type { RenderProps, TextInputLabelProp } from './types'; -import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; -import { roundLayoutSize } from '../../utils/roundLayoutSize'; - -const BLUR_ANIMATION_DURATION = 180; -const FOCUS_ANIMATION_DURATION = 150; - -export type Props = React.ComponentPropsWithRef & { - /** - * Mode of the TextInput. - * - `flat` - flat input with an underline. - * - `outlined` - input with an outline. - * - * In `outlined` mode, the background color of the label is derived from `colors?.background` in theme or the `backgroundColor` style. - * This component render TextInputOutlined or TextInputFlat based on that props - */ - mode?: 'flat' | 'outlined'; - /** - * The adornment placed on the left side of the input. It can be either `TextInput.Icon` or `TextInput.Affix`. - */ - left?: React.ReactNode; - /** - * The adornment placed on the right side of the input. It can be either `TextInput.Icon` or `TextInput.Affix`. - */ - right?: React.ReactNode; - /** - * If true, user won't be able to interact with the component. - */ - disabled?: boolean; +import Animated, { AnimatedStyle } from 'react-native-reanimated'; + +import { useTextInput } from './hooks'; +import { styles } from './styles'; +import TextInputErrorIcon from './TextInputErrorIcon'; +import type { TextInputAccessoryProps } from './TextInputIcon'; +import type { InternalTheme, ThemeProp } from '../../types'; + +export type TextInputAnimationState = { + animatedLabelWrapperStyle: StyleProp>>; + animatedLabelTextStyle: StyleProp>>; + animatedContainerStyle: StyleProp>>; + animatedActiveOutlineStyle?: StyleProp>>; +}; + +export type TextInputAnimationHandlers = { + runFocusAnimation: (hasText: boolean) => void; + runBlurAnimation: (hasText: boolean) => void; + syncFloatToValue: (hasText: boolean) => void; +}; + +export type TextInputFlags = { + isRTL: boolean; + isDisabled: boolean; + isEditable: boolean | undefined; + hasError: boolean; + hasCounter: boolean; + hasAccessory: boolean; + isFloating: boolean; + hasPrefix: boolean; + hasSuffix: boolean; +}; + +export type TextInputColors = { + selectionColor: ColorValue; + cursorColor: ColorValue; + placeholderTextColor: ColorValue; +}; + +export type GetAccessibilityDataReturn = { + input: AccessibilityProps; + supportingText: AccessibilityProps; + counter: AccessibilityProps; +}; + +export type GetAccessibilityDataProps = { + data: TextInputProps; + inputLength: number; + hasError: boolean; + hasCounter: boolean; + isDisabled: boolean; +}; + +export type TextInputVariant = 'filled' | 'outlined'; + +export type TextInputSharedApi = { + input: React.RefObject; + theme: InternalTheme; + isFocused: boolean; + isRTL: boolean; + isDisabled: boolean; + hasAccessory: boolean; + hasError: boolean; + hasSuffix: boolean; + animatedLabelWrapperStyle: StyleProp>>; + animatedLabelTextStyle: StyleProp>>; + animatedActiveOutlineStyle?: StyleProp>>; +}; + +export type SharedTextInputStyleData = { + isRTL: boolean; + animatedLabelTextStyles: StyleProp>>; + supportingTextStyles: StyleProp; + counterStyles: StyleProp; + prefixStyles: StyleProp; + suffixStyles: StyleProp; + leadingAccessoryStyles: StyleProp; + trailingAccessoryStyles: StyleProp; +}; + +export type FilledTextInputHookData = SharedTextInputStyleData & { + input: React.RefObject; + isDisabled: boolean; + hasError: boolean; + hasSuffix: boolean; + animatedLabelWrapperStyles: StyleProp>>; + containerStyles: StyleProp; + fieldStyles: StyleProp; + disabledBackgroundStyles: StyleProp | undefined; + outlineStyles: StyleProp; + animatedActiveOutlineStyles: StyleProp>>; + inputStyles: StyleProp; +}; + +export type OutlinedTextInputHookData = SharedTextInputStyleData & { + input: React.RefObject; + isDisabled: boolean; + hasError: boolean; + hasSuffix: boolean; + animatedLabelWrapperStyles: StyleProp>>; + containerStyles: StyleProp; + fieldStyles: StyleProp; + disabledBackgroundStyles: undefined; + outlineStyles: StyleProp; + inputStyles: StyleProp; +}; + +export type TextInputLayoutData = + | FilledTextInputHookData + | OutlinedTextInputHookData; + +export type TextInputLayoutState = Omit< + TextInputLayoutData, + 'input' | 'isDisabled' | 'hasError' | 'hasSuffix' +>; + +export type TextInputHookReturn = SharedTextInputStyleData & { + input: React.RefObject; + isDisabled: boolean; + isEditable: boolean | undefined; + hasPrefix: boolean; + hasCounter: boolean; + hasSuffix: boolean; + hasError: boolean; + placeholderTextColor: ColorValue; + selectionColor: ColorValue; + cursorColor: ColorValue; + animatedActiveOutlineStyles: + | StyleProp>> + | undefined; + animatedContainerStyle: StyleProp>>; + animatedLabelWrapperStyles: StyleProp>>; + containerStyles: StyleProp; + fieldStyles: StyleProp; + disabledBackgroundStyles: StyleProp | undefined; + outlineStyles: StyleProp; + inputStyles: StyleProp; + placeholder: string | undefined; + counterText: string; + accessibilityProps: GetAccessibilityDataReturn; + renderLeadingAccessory: + | ((props: TextInputAccessoryProps) => React.ReactNode) + | undefined; + renderTrailingAccessory: + | ((props: TextInputAccessoryProps) => React.ReactNode) + | undefined; + onChangeText: (text: string) => void; + onFocus: (e: FocusEvent) => void; + onBlur: (e: BlurEvent) => void; + focusInput: () => void; +}; + +export type TextInputRenderProps = React.ComponentPropsWithRef< + typeof NativeTextInput +>; + +export type TextInputHandles = Pick< + NativeTextInput, + 'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps' | 'setSelection' +>; + +export type TextInputProps = NativeTextInputProps & { /** - * The text or component to use for the floating label. + * Imperative handle exposing a subset of native `TextInput` methods + * with side-effect handling (e.g. `clear()` syncs internal state and animations). */ - label?: TextInputLabelProp; + ref?: React.Ref; /** - * Placeholder for the input. + * Determines the visual style of the text input. + * + * - `filled` — filled background with an animated underline; higher visual emphasis. + * - `outlined` — stroke outline only; lower visual emphasis. */ - placeholder?: string; + variant?: TextInputVariant; /** - * Whether to style the TextInput with error style. + * When `true`, the field uses error styling and replaces the trailing accessory + * with an error indicator when no `endAccessory` is provided. */ error?: boolean; /** - * Callback that is called when the text input's text changes. Changed text is passed as an argument to the callback handler. - */ - onChangeText?: Function; - /** - * Selection color of the input. On iOS, it sets both the selection color and cursor color. - * On Android, it sets only the selection color. - */ - selectionColor?: string; - /** - * @platform Android only - * Cursor (or "caret") color of the input on Android. - * This property has no effect on iOS. - */ - cursorColor?: string; - /** - * Inactive underline color of the input. - */ - underlineColor?: string; - /** - * Active underline color of the input. - */ - activeUnderlineColor?: string; - /** - * Inactive outline color of the input. - */ - outlineColor?: string; - /** - * Active outline color of the input. - */ - activeOutlineColor?: string; - /** - * Color of the text in the input. - */ - textColor?: string; - /** - * Sets min height with densed layout. For `TextInput` in `flat` mode - * height is `64dp` or in dense layout - `52dp` with label or `40dp` without label. - * For `TextInput` in `outlined` mode - * height is `56dp` or in dense layout - `40dp` regardless of label. - * When you apply `height` prop in style the `dense` prop affects only `paddingVertical` inside `TextInput` - */ - dense?: boolean; - /** - * Whether the input can have multiple lines. + * The label text to display above the input. */ - multiline?: boolean; + label?: string; /** - * @platform Android only - * The number of lines to show in the input (Android only). + * Supporting text to display below the input (Material Design 3). */ - numberOfLines?: number; + supportingText?: string; /** - * Callback that is called when the text input is focused. + * When `true`, displays a character counter below the input on the trailing + * side, showing `currentLength/maxLength`. Requires `maxLength` to be set. */ - onFocus?: (args: any) => void; + counter?: boolean; /** - * Callback that is called when the text input is blurred. + * This is separate from `editable={false}`, which makes the text read-only while the + * input can still be focused and text selected. */ - onBlur?: (args: any) => void; - /** - * - * Callback to render a custom input component such as `react-native-text-input-mask` - * instead of the default `TextInput` component from `react-native`. - * - * Example: - * ```js - * - * - * } - * /> - * ``` - */ - render?: (props: RenderProps) => React.ReactNode; - /** - * Value of the text input. - */ - value?: string; + disabled?: boolean; /** - * Pass `fontSize` prop to modify the font size inside `TextInput`. - * Pass `height` prop to set `TextInput` height. When `height` is passed, - * `dense` prop will affect only input's `paddingVertical`. - * Pass `paddingHorizontal` to modify horizontal padding. - * This can be used to get MD Guidelines v1 TextInput look. + * A short text string displayed at the start of the input (e.g. `"$"`). */ - style?: StyleProp; + prefix?: string; /** - * @optional + * A short text string displayed at the end of the input (e.g. `"/100"`). */ + suffix?: string; theme?: ThemeProp; /** - * testID to be used on tests. + * An optional component to render on the start side of the input (leading in LTR). + * Can be a custom component or `TextInput.Icon`. */ - testID?: string; + startAccessory?: (props: TextInputAccessoryProps) => React.ReactNode; /** - * Pass custom style directly to the input itself. - * Overrides input style - * Example: `paddingLeft`, `backgroundColor` + * An optional component to render on the end side of the input (trailing in LTR). + * Can be a custom component or `TextInput.Icon`. */ - contentStyle?: StyleProp; + endAccessory?: (props: TextInputAccessoryProps) => React.ReactNode; /** - * Pass style to override the default style of outlined wrapper. - * Overrides style when mode is set to `outlined` - * Example: `borderRadius`, `borderColor` + * Callback to render a custom input component in place of the native `TextInput`. + * Receives all props that would be passed to `TextInput`, allowing integration + * with third-party inputs such as masked inputs. */ - outlineStyle?: StyleProp; - /** - * Pass style to override the default style of underlined wrapper. - * Overrides style when mode is set to `flat` - * Example: `borderRadius`, `borderColor` - */ - underlineStyle?: StyleProp; + render?: (props: TextInputRenderProps) => React.ReactNode; }; -interface CompoundedComponent - extends React.ForwardRefExoticComponent< - Props & React.RefAttributes - > { - Icon: React.FunctionComponent; - Affix: React.FunctionComponent>; -} - -type TextInputHandles = Pick< - NativeTextInput, - 'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps' | 'setSelection' ->; - -const DefaultRenderer = (props: RenderProps) => ; +const defaultRenderer = (props: TextInputRenderProps) => ( + +); /** - * A component to allow users to input text. + * A text input lets users enter and edit text. It shows an optional floating label, + * supports `filled` and `outlined` variants, optional supporting text (including + * error state), and start/end accessories. * * ## Usage * ```js @@ -204,13 +253,31 @@ const DefaultRenderer = (props: RenderProps) => ; * import { TextInput } from 'react-native-paper'; * * const MyComponent = () => { - * const [text, setText] = React.useState(""); + * const [text, setText] = React.useState(''); + * + * const searchAccessory = (accessoryProps) => ( + * + * ); + * + * const clearAccessory = ({ style, disabled }) => ( + * setText('')} + * role="button" + * aria-label="Clear text" + * > + * + * + * ); * * return ( * setText(text)} + * onChangeText={setText} + * startAccessory={searchAccessory} + * endAccessory={clearAccessory} * /> * ); * }; @@ -220,358 +287,152 @@ const DefaultRenderer = (props: RenderProps) => ; * * @extends TextInput props https://reactnative.dev/docs/textinput#props */ -const TextInput = forwardRef( - ( - { - mode = 'flat', - dense = false, - disabled = false, - error: errorProp = false, - multiline = false, - editable = true, - contentStyle, - render = DefaultRenderer, - theme: themeOverrides, - ...rest - }: Props, - ref - ) => { - const theme = useInternalTheme(themeOverrides); - const isControlled = rest.value !== undefined; - const validInputValue = isControlled ? rest.value : rest.defaultValue; - - const { current: labeled } = React.useRef( - new Animated.Value(validInputValue ? 0 : 1) - ); - const { current: error } = React.useRef( - new Animated.Value(errorProp ? 1 : 0) - ); - const [focused, setFocused] = React.useState(false); - const [displayPlaceholder, setDisplayPlaceholder] = - React.useState(false); - const [uncontrolledValue, setUncontrolledValue] = React.useState< - string | undefined - >(validInputValue); - // Use value from props instead of local state when input is controlled - const value = isControlled ? rest.value : uncontrolledValue; - - const [labelTextLayout, setLabelTextLayout] = React.useState({ - width: 33, - }); - - const [inputContainerLayout, setInputContainerLayout] = React.useState({ - width: 65, - }); - - const [labelLayout, setLabelLayout] = React.useState<{ - measured: boolean; - width: number; - height: number; - }>({ - measured: false, - width: 0, - height: 0, - }); - const [leftLayout, setLeftLayout] = React.useState<{ - height: number | null; - width: number | null; - }>({ - width: null, - height: null, - }); - const [rightLayout, setRightLayout] = React.useState<{ - height: number | null; - width: number | null; - }>({ - width: null, - height: null, - }); - - const timer = React.useRef(undefined); - const root = React.useRef(null); - - const { scale } = theme.animation; - - React.useImperativeHandle(ref, () => ({ - focus: () => root.current?.focus(), - clear: () => root.current?.clear(), - setNativeProps: (args: Object) => root.current?.setNativeProps(args), - isFocused: () => root.current?.isFocused() || false, - blur: () => root.current?.blur(), - forceFocus: () => root.current?.focus(), - setSelection: (start: number, end: number) => - root.current?.setSelection(start, end), - })); - - React.useEffect(() => { - // When the input has an error, we wiggle the label and apply error styles - if (errorProp) { - // show error - Animated.timing(error, { - toValue: 1, - duration: FOCUS_ANIMATION_DURATION * scale, - // To prevent this - https://github.com/callstack/react-native-paper/issues/941 - useNativeDriver: true, - }).start(); - } else { - // hide error - { - Animated.timing(error, { - toValue: 0, - duration: BLUR_ANIMATION_DURATION * scale, - // To prevent this - https://github.com/callstack/react-native-paper/issues/941 - useNativeDriver: true, - }).start(); - } - } - }, [errorProp, scale, error]); - - React.useEffect(() => { - // Show placeholder text only if the input is focused, or there's no label - // We don't show placeholder if there's a label because the label acts as placeholder - // When focused, the label moves up, so we can show a placeholder - if (focused || !rest.label) { - // If the user wants to use the contextMenu, when changing the placeholder, the contextMenu is closed - // This is a workaround to mitigate this behavior in scenarios where the placeholder is not specified. - if (rest.placeholder) { - // Display placeholder in a delay to offset the label animation - // If we show it immediately, they'll overlap and look ugly - timer.current = setTimeout( - () => setDisplayPlaceholder(true), - 50 - ) as unknown as NodeJS.Timeout; - } - } else { - // hidePlaceholder - setDisplayPlaceholder(false); - } - - return () => { - if (timer.current) { - clearTimeout(timer.current); - } - }; - }, [focused, rest.label, rest.placeholder]); - - React.useEffect(() => { - labeled.stopAnimation(); - // The label should be minimized if the text input is focused, or has text - // In minimized mode, the label moves up and becomes small - // workaround for animated regression for react native > 0.61 - // https://github.com/callstack/react-native-paper/pull/1440 - if (value || focused) { - // minimize label - Animated.timing(labeled, { - toValue: 0, - duration: BLUR_ANIMATION_DURATION * scale, - // To prevent this - https://github.com/callstack/react-native-paper/issues/941 - useNativeDriver: true, - }).start(); - } else { - // restore label - Animated.timing(labeled, { - toValue: 1, - duration: FOCUS_ANIMATION_DURATION * scale, - // To prevent this - https://github.com/callstack/react-native-paper/issues/941 - useNativeDriver: true, - }).start(); - } - }, [focused, value, labeled, scale]); - - const onLeftAffixLayoutChange = React.useCallback( - (event: LayoutChangeEvent) => { - const height = roundLayoutSize(event.nativeEvent.layout.height); - const width = roundLayoutSize(event.nativeEvent.layout.width); - - if (width !== leftLayout.width || height !== leftLayout.height) { - setLeftLayout({ - width, - height, - }); - } - }, - [leftLayout.height, leftLayout.width] - ); - - const onRightAffixLayoutChange = React.useCallback( - (event: LayoutChangeEvent) => { - const width = roundLayoutSize(event.nativeEvent.layout.width); - const height = roundLayoutSize(event.nativeEvent.layout.height); - - if (width !== rightLayout.width || height !== rightLayout.height) { - setRightLayout({ - width, - height, - }); - } - }, - [rightLayout.height, rightLayout.width] - ); - - const handleFocus = (args: any) => { - if (disabled || !editable) { - return; - } - - setFocused(true); - - rest.onFocus?.(args); - }; - - const handleBlur = (args: Object) => { - if (!editable) { - return; - } - - setFocused(false); - rest.onBlur?.(args); - }; - - const handleChangeText = (value: string) => { - if (!editable || disabled) { - return; - } - - if (!isControlled) { - // Keep track of value in local state when input is not controlled - setUncontrolledValue(value); - } - rest.onChangeText?.(value); - }; - - const handleLayoutAnimatedText = React.useCallback( - (e: LayoutChangeEvent) => { - const width = roundLayoutSize(e.nativeEvent.layout.width); - const height = roundLayoutSize(e.nativeEvent.layout.height); - - if (width !== labelLayout.width || height !== labelLayout.height) { - setLabelLayout({ - width, - height, - measured: true, - }); - } - }, - [labelLayout.height, labelLayout.width] - ); - - const handleLabelTextLayout = React.useCallback( - ({ nativeEvent }: NativeSyntheticEvent) => { - setLabelTextLayout({ - width: nativeEvent.lines.reduce( - (acc, line) => acc + Math.ceil(line.width), - 0 - ), - }); - }, - [] - ); - - const handleInputContainerLayout = React.useCallback( - ({ nativeEvent: { layout } }: LayoutChangeEvent) => { - setInputContainerLayout({ - width: layout.width, - }); - }, - [] - ); - - const forceFocus = React.useCallback(() => root.current?.focus(), []); - - const { maxFontSizeMultiplier = 1.5 } = rest; - - const scaledLabel = !!(value || focused); - - if (mode === 'outlined') { - return ( - { - root.current = ref; - }} - onFocus={handleFocus} - forceFocus={forceFocus} - onBlur={handleBlur} - onChangeText={handleChangeText} - onLayoutAnimatedText={handleLayoutAnimatedText} - onInputLayout={handleInputContainerLayout} - onLabelTextLayout={handleLabelTextLayout} - onLeftAffixLayoutChange={onLeftAffixLayoutChange} - onRightAffixLayoutChange={onRightAffixLayoutChange} - maxFontSizeMultiplier={maxFontSizeMultiplier} - contentStyle={contentStyle} - scaledLabel={scaledLabel} - /> - ); - } - - return ( - { - root.current = ref; - }} - onFocus={handleFocus} - forceFocus={forceFocus} - onBlur={handleBlur} - onInputLayout={handleInputContainerLayout} - onChangeText={handleChangeText} - onLayoutAnimatedText={handleLayoutAnimatedText} - onLabelTextLayout={handleLabelTextLayout} - onLeftAffixLayoutChange={onLeftAffixLayoutChange} - onRightAffixLayoutChange={onRightAffixLayoutChange} - maxFontSizeMultiplier={maxFontSizeMultiplier} - contentStyle={contentStyle} - scaledLabel={scaledLabel} - /> - ); - } -) as CompoundedComponent; -// @component ./Adornment/TextInputIcon.tsx -TextInput.Icon = TextInputIcon; - -// @component ./Adornment/TextInputAffix.tsx -// @ts-ignore Types of property 'theme' are incompatible. -TextInput.Affix = TextInputAffix; +function TextInput(props: TextInputProps) { + /* eslint-disable @typescript-eslint/no-unused-vars -- peel TextInput-only props before NativeTextInput spread */ + const { + ref, + error, + label, + supportingText, + variant, + theme, + prefix, + suffix, + counter, + disabled, + startAccessory, + endAccessory, + render = defaultRenderer, + ...textInputProps + } = props; + + const { + input, + isDisabled, + isEditable, + hasPrefix, + hasSuffix, + hasCounter, + hasError, + leadingAccessoryStyles, + trailingAccessoryStyles, + fieldStyles, + disabledBackgroundStyles, + outlineStyles, + animatedActiveOutlineStyles, + animatedLabelWrapperStyles, + animatedLabelTextStyles, + animatedContainerStyle, + containerStyles, + inputStyles, + prefixStyles, + suffixStyles, + supportingTextStyles, + counterStyles, + placeholderTextColor, + selectionColor, + cursorColor, + placeholder, + counterText, + accessibilityProps, + renderLeadingAccessory, + renderTrailingAccessory, + focusInput, + onChangeText, + onFocus, + onBlur, + } = useTextInput(props); + + return ( + + + {/* Disabled tint overlay — filled variant only. A childless + absolutely-positioned View whose translucent fill is applied via the + `opacity` style, so it never affects label/input rendering and works + with PlatformColor on Android. */} + {!!disabledBackgroundStyles && ( + + )} + + {/* Inactive indicator — always-visible 1px bottom border (filled) or + full border (outlined); height and color reflect error/disabled state + but do not change on focus */} + + + {/* Active indicator — filled variant only; 2px bar that expands from + the center outward via scaleX (0 → 1) on focus and collapses on blur */} + {!!animatedActiveOutlineStyles && ( + + )} + + {!!label && ( + + + {label} + + + )} + + {renderLeadingAccessory + ? renderLeadingAccessory({ + style: leadingAccessoryStyles, + error: hasError, + disabled: isDisabled, + multiline: !!textInputProps.multiline, + }) + : null} + + + {hasPrefix && {prefix}} + + {render({ + ref: input, + selectionColor, + cursorColor, + placeholderTextColor, + ...accessibilityProps.input, + ...textInputProps, + editable: isEditable, + placeholder, + style: inputStyles, + onChangeText, + onFocus, + onBlur, + })} + + {hasSuffix && {suffix}} + + + {renderTrailingAccessory ? ( + renderTrailingAccessory({ + style: trailingAccessoryStyles, + error: hasError, + disabled: isDisabled, + multiline: !!textInputProps.multiline, + }) + ) : hasError ? ( + + ) : null} + + + + {!!supportingText && ( + + {supportingText} + + )} + + {hasCounter && ( + + {counterText} + + )} + + + ); +} export default TextInput; diff --git a/src/components/TextInput/TextInputErrorIcon.tsx b/src/components/TextInput/TextInputErrorIcon.tsx new file mode 100644 index 0000000000..3af74ec3ad --- /dev/null +++ b/src/components/TextInput/TextInputErrorIcon.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { StyleProp, View, ViewStyle } from 'react-native'; + +import { ACCESSORY_SIZE } from './constants'; +import { useInternalTheme } from '../../core/theming'; +import type { ThemeProp } from '../../types'; +import Icon from '../Icon'; + +interface TextInputErrorIconProps { + style?: StyleProp; + theme?: ThemeProp; +} + +const TextInputErrorIcon = ({ + style: wrapperStyle, + theme: themeOverride, +}: TextInputErrorIconProps) => { + const theme = useInternalTheme(themeOverride); + + return ( + + + + ); +}; + +export default TextInputErrorIcon; diff --git a/src/components/TextInput/TextInputFlat.tsx b/src/components/TextInput/TextInputFlat.tsx deleted file mode 100644 index f7235ea5d2..0000000000 --- a/src/components/TextInput/TextInputFlat.tsx +++ /dev/null @@ -1,468 +0,0 @@ -import * as React from 'react'; -import { - Platform, - StyleSheet, - TextInput as NativeTextInput, - TextStyle, - View, - Animated, -} from 'react-native'; - -import { Underline } from './Addons/Underline'; -import { AdornmentSide, AdornmentType, InputMode } from './Adornment/enums'; -import TextInputAdornment, { - TextInputAdornmentProps, -} from './Adornment/TextInputAdornment'; -import { - getAdornmentConfig, - getAdornmentStyleAdjustmentForNativeInput, -} from './Adornment/TextInputAdornment'; -import { - ADORNMENT_SIZE, - LABEL_PADDING_TOP_DENSE, - LABEL_WIGGLE_X_OFFSET, - MAXIMIZED_LABEL_FONT_SIZE, - MINIMIZED_LABEL_FONT_SIZE, - MINIMIZED_LABEL_Y_OFFSET, - MIN_DENSE_HEIGHT, - MIN_DENSE_HEIGHT_WL, -} from './constants'; -import { - adjustPaddingFlat, - calculateFlatAffixTopPosition, - calculateFlatInputHorizontalPadding, - calculateInputHeight, - calculateLabelTopPosition, - calculatePadding, - getConstants, - getFlatInputColors, - Padding, -} from './helpers'; -import InputLabel from './Label/InputLabel'; -import type { ChildTextInputProps, RenderProps } from './types'; -import { useLocale } from '../../core/locale'; - -const TextInputFlat = ({ - disabled = false, - editable = true, - label, - error = false, - selectionColor: customSelectionColor, - cursorColor, - underlineColor, - underlineStyle, - activeUnderlineColor, - textColor, - dense, - style, - theme, - render = (props: RenderProps) => , - multiline = false, - parentState, - innerRef, - onFocus, - forceFocus, - onBlur, - onChangeText, - onLayoutAnimatedText, - onLabelTextLayout, - onLeftAffixLayoutChange, - onRightAffixLayoutChange, - onInputLayout, - left, - right, - placeholderTextColor, - testID = 'text-input-flat', - contentStyle, - scaledLabel, - ...rest -}: ChildTextInputProps) => { - const isAndroid = Platform.OS === 'android'; - const { direction } = useLocale(); - const isRTL = direction === 'rtl'; - const { colors } = theme; - const roundness = theme.shapes.corner.extraSmall; - const font = theme.fonts.bodyLarge; - const hasActiveOutline = parentState.focused || error; - - const { LABEL_PADDING_TOP, FLAT_INPUT_OFFSET, MIN_HEIGHT, MIN_WIDTH } = - getConstants(); - - const { - fontSize: fontSizeStyle, - lineHeight: lineHeightStyle, - fontWeight, - height, - paddingHorizontal, - textAlign, - ...viewStyle - } = (StyleSheet.flatten(style) || {}) as TextStyle; - const fontSize = fontSizeStyle || MAXIMIZED_LABEL_FONT_SIZE; - const lineHeight = - lineHeightStyle || (Platform.OS === 'web' ? fontSize * 1.2 : undefined); - - const isPaddingHorizontalPassed = - paddingHorizontal !== undefined && typeof paddingHorizontal === 'number'; - - const adornmentConfig = getAdornmentConfig({ - left, - right, - }); - - let { paddingLeft, paddingRight } = calculateFlatInputHorizontalPadding({ - adornmentConfig, - }); - - if (isPaddingHorizontalPassed) { - paddingLeft = paddingHorizontal as number; - paddingRight = paddingHorizontal as number; - } - - const { leftLayout, rightLayout } = parentState; - - const rightAffixWidth = right - ? rightLayout.width || ADORNMENT_SIZE - : ADORNMENT_SIZE; - - const leftAffixWidth = left - ? leftLayout.width || ADORNMENT_SIZE - : ADORNMENT_SIZE; - - const adornmentStyleAdjustmentForNativeInput = - getAdornmentStyleAdjustmentForNativeInput({ - adornmentConfig, - rightAffixWidth, - leftAffixWidth, - paddingHorizontal, - inputOffset: FLAT_INPUT_OFFSET, - mode: InputMode.Flat, - }); - - const { - inputTextColor, - activeColor, - disabledOpacity, - underlineColorCustom, - placeholderColor, - errorColor, - backgroundColor, - selectionColor, - } = getFlatInputColors({ - underlineColor, - activeUnderlineColor, - customSelectionColor, - textColor, - disabled, - error, - theme, - }); - - const containerStyle = { - backgroundColor, - borderTopLeftRadius: roundness, - borderTopRightRadius: roundness, - }; - - const labelScale = MINIMIZED_LABEL_FONT_SIZE / fontSize; - const fontScale = MAXIMIZED_LABEL_FONT_SIZE / fontSize; - - const labelWidth = parentState.labelLayout.width; - const labelHeight = parentState.labelLayout.height; - const labelHalfWidth = labelWidth / 2; - const labelHalfHeight = labelHeight / 2; - - const baseLabelTranslateX = - (isRTL ? 1 : -1) * (labelHalfWidth - (labelScale * labelWidth) / 2) + - (1 - labelScale) * (isRTL ? -1 : 1) * paddingLeft; - - const minInputHeight = dense - ? (label ? MIN_DENSE_HEIGHT_WL : MIN_DENSE_HEIGHT) - LABEL_PADDING_TOP_DENSE - : MIN_HEIGHT - LABEL_PADDING_TOP; - - const inputHeight = calculateInputHeight(labelHeight, height, minInputHeight); - - const topPosition = calculateLabelTopPosition( - labelHeight, - inputHeight, - multiline && height ? 0 : !height ? minInputHeight / 2 : 0 - ); - - if (height && typeof height !== 'number') { - // eslint-disable-next-line - console.warn('Currently we support only numbers in height prop'); - } - - const paddingSettings = { - height: height ? +height : null, - labelHalfHeight, - offset: FLAT_INPUT_OFFSET, - multiline: multiline ? multiline : null, - dense: dense ? dense : null, - topPosition, - fontSize, - lineHeight, - label, - scale: fontScale, - isAndroid, - styles: StyleSheet.flatten( - dense ? styles.inputFlatDense : styles.inputFlat - ) as Padding, - }; - - const pad = calculatePadding(paddingSettings); - - const paddingFlat = adjustPaddingFlat({ - ...paddingSettings, - pad, - }); - - const baseLabelTranslateY = - -labelHalfHeight - (topPosition + MINIMIZED_LABEL_Y_OFFSET); - - const { current: placeholderOpacityAnims } = React.useRef([ - new Animated.Value(0), - new Animated.Value(1), - ]); - - const placeholderOpacity = hasActiveOutline - ? parentState.labeled - : placeholderOpacityAnims[parentState.labelLayout.measured ? 1 : 0]; - - // We don't want to show placeholder if label is displayed, because they overlap. - // Before it was done by setting placeholder's value to " ", but inputs have the same props - // what causes broken styles due to: https://github.com/facebook/react-native/issues/48249 - const placeholderTextColorBasedOnState = parentState.displayPlaceholder - ? placeholderTextColor ?? placeholderColor - : 'transparent'; - - const minHeight = - height || - (dense ? (label ? MIN_DENSE_HEIGHT_WL : MIN_DENSE_HEIGHT) : MIN_HEIGHT); - - const flatHeight = - inputHeight + - (!height ? (dense ? LABEL_PADDING_TOP_DENSE : LABEL_PADDING_TOP) : 0); - - const iconTopPosition = (flatHeight - ADORNMENT_SIZE) / 2; - - const leftAffixTopPosition = leftLayout.height - ? calculateFlatAffixTopPosition({ - height: flatHeight, - ...paddingFlat, - affixHeight: leftLayout.height, - }) - : null; - - const rightAffixTopPosition = rightLayout.height - ? calculateFlatAffixTopPosition({ - height: flatHeight, - ...paddingFlat, - affixHeight: rightLayout.height, - }) - : null; - - const labelProps = { - label, - onLayoutAnimatedText, - onLabelTextLayout, - placeholderOpacity, - labelError: error, - placeholderStyle: styles.placeholder, - baseLabelTranslateY, - baseLabelTranslateX, - font, - fontSize, - lineHeight, - fontWeight, - labelScale, - wiggleOffsetX: LABEL_WIGGLE_X_OFFSET, - topPosition, - paddingLeft: isAndroid ? (isRTL ? paddingRight : paddingLeft) : paddingLeft, - paddingRight: isAndroid - ? isRTL - ? paddingLeft - : paddingRight - : paddingRight, - hasActiveOutline, - activeColor, - placeholderColor, - disabledOpacity, - errorColor, - roundness, - maxFontSizeMultiplier: rest.maxFontSizeMultiplier, - testID, - contentStyle, - inputContainerLayout: parentState.inputContainerLayout, - labelTextLayout: parentState.labelTextLayout, - opacity: - parentState.value || parentState.focused - ? parentState.labelLayout.measured - ? 1 - : 0 - : 1, - }; - - const affixTopPosition = { - [AdornmentSide.Left]: leftAffixTopPosition, - [AdornmentSide.Right]: rightAffixTopPosition, - }; - const onAffixChange = { - [AdornmentSide.Left]: onLeftAffixLayoutChange, - [AdornmentSide.Right]: onRightAffixLayoutChange, - }; - - let adornmentProps: TextInputAdornmentProps = { - paddingHorizontal, - adornmentConfig, - forceFocus, - topPosition: { - [AdornmentType.Affix]: affixTopPosition, - [AdornmentType.Icon]: iconTopPosition, - }, - onAffixChange, - isTextInputFocused: parentState.focused, - maxFontSizeMultiplier: rest.maxFontSizeMultiplier, - disabled, - }; - if (adornmentConfig.length) { - adornmentProps = { - ...adornmentProps, - left, - right, - textStyle: { ...font, fontSize, lineHeight, fontWeight }, - visible: parentState.labeled, - }; - } - - return ( - - - - {!isAndroid && multiline && !!label && !disabled && ( - // Workaround for: https://github.com/callstack/react-native-paper/issues/2799 - // Patch for a multiline TextInput with fixed height, which allow to avoid covering input label with its value. - - )} - {label ? ( - - ) : null} - {render?.({ - ...rest, - ref: innerRef, - onChangeText, - placeholder: rest.placeholder, - editable: !disabled && editable, - selectionColor, - cursorColor: - typeof cursorColor === 'undefined' ? activeColor : cursorColor, - placeholderTextColor: placeholderTextColorBasedOnState, - onFocus, - onBlur, - underlineColorAndroid: 'transparent', - multiline, - style: [ - styles.input, - multiline && height ? { height: flatHeight } : {}, - paddingFlat, - { - paddingLeft, - paddingRight, - ...font, - fontSize, - lineHeight, - fontWeight, - color: inputTextColor, - opacity: disabledOpacity, - textAlignVertical: multiline ? 'top' : 'center', - textAlign: textAlign ? textAlign : isRTL ? 'right' : 'left', - minWidth: Math.min( - parentState.labelTextLayout.width + 2 * FLAT_INPUT_OFFSET, - MIN_WIDTH - ), - }, - Platform.OS === 'web' ? { outline: 'none' } : undefined, - adornmentStyleAdjustmentForNativeInput, - contentStyle, - ], - testID, - })} - - - - ); -}; - -export default TextInputFlat; - -const styles = StyleSheet.create({ - placeholder: { - position: 'absolute', - left: 0, - }, - labelContainer: { - paddingTop: 0, - paddingBottom: 0, - flexGrow: 1, - }, - input: { - margin: 0, - flexGrow: 1, - }, - inputFlat: { - paddingTop: 24, - paddingBottom: 4, - }, - inputFlatDense: { - paddingTop: 22, - paddingBottom: 2, - }, - patchContainer: { - height: 24, - zIndex: 2, - }, - densePatchContainer: { - height: 22, - zIndex: 2, - }, -}); diff --git a/src/components/TextInput/TextInputIcon.tsx b/src/components/TextInput/TextInputIcon.tsx new file mode 100644 index 0000000000..e76528324f --- /dev/null +++ b/src/components/TextInput/TextInputIcon.tsx @@ -0,0 +1,98 @@ +import React from 'react'; +import { StyleProp, View, ViewStyle } from 'react-native'; + +import { ACCESSORY_SIZE } from './constants'; +import { styles } from './styles'; +import { getIconColor } from './utils'; +import { useInternalTheme } from '../../core/theming'; +import type { $Omit } from '../../types'; +import IconButton from '../IconButton/IconButton'; + +export type TextInputAccessoryProps = { + style: StyleProp; + multiline: boolean; + disabled: boolean; + error: boolean; +}; + +export type TextInputIconProps = TextInputAccessoryProps & + $Omit, keyof TextInputAccessoryProps>; + +/** + * A component to render a leading / trailing icon in the TextInput + * (return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as + * `TextInputAccessoryProps`, which TextInput passes into those render props. + * + * ## Usage + * ```js + * import * as React from 'react'; + * import { TextInput } from 'react-native-paper'; + * + * const MyComponent = () => { + * const [text, setText] = React.useState(''); + * + * const searchAccessory = (props) => ( + * + * ); + * + * const clearAccessory = (props) => ( + * setText('')} /> + * ); + * + * return ( + * + * ); + * }; + * + * export default MyComponent; + * ``` + * + * @extends IconButton props https://callstack.github.io/react-native-paper/docs/components/IconButton + */ +const TextInputIcon = ({ + icon, + iconColor, + size, + style, + error, + disabled, + theme: themeOverride, + onPress, + ...rest +}: TextInputIconProps) => { + const theme = useInternalTheme(themeOverride); + + const iconSize = size ?? ACCESSORY_SIZE; + + const color = getIconColor({ + theme, + iconColor, + hasError: error, + isDisabled: disabled, + }); + + const onPressHandler = disabled ? undefined : onPress; + + return ( + + + + ); +}; + +TextInputIcon.displayName = 'TextInput.Icon'; + +export default TextInputIcon; diff --git a/src/components/TextInput/TextInputOutlined.tsx b/src/components/TextInput/TextInputOutlined.tsx deleted file mode 100644 index be4fae7487..0000000000 --- a/src/components/TextInput/TextInputOutlined.tsx +++ /dev/null @@ -1,447 +0,0 @@ -import * as React from 'react'; -import { - Animated, - View, - TextInput as NativeTextInput, - StyleSheet, - Platform, - TextStyle, - ColorValue, - LayoutChangeEvent, -} from 'react-native'; - -import { Outline } from './Addons/Outline'; -import { AdornmentType, AdornmentSide } from './Adornment/enums'; -import TextInputAdornment, { - getAdornmentConfig, - getAdornmentStyleAdjustmentForNativeInput, - TextInputAdornmentProps, -} from './Adornment/TextInputAdornment'; -import { - MAXIMIZED_LABEL_FONT_SIZE, - MINIMIZED_LABEL_FONT_SIZE, - LABEL_WIGGLE_X_OFFSET, - ADORNMENT_SIZE, - OUTLINE_MINIMIZED_LABEL_Y_OFFSET, - LABEL_PADDING_TOP, - MIN_DENSE_HEIGHT_OUTLINED, - LABEL_PADDING_TOP_DENSE, -} from './constants'; -import { - calculateLabelTopPosition, - calculateInputHeight, - calculatePadding, - adjustPaddingOut, - Padding, - calculateOutlinedIconAndAffixTopPosition, - getOutlinedInputColors, - getConstants, -} from './helpers'; -import InputLabel from './Label/InputLabel'; -import LabelBackground from './Label/LabelBackground'; -import type { RenderProps, ChildTextInputProps } from './types'; -import { useLocale } from '../../core/locale'; - -const TextInputOutlined = ({ - disabled = false, - editable = true, - label, - error = false, - selectionColor: customSelectionColor, - cursorColor, - underlineColor: _underlineColor, - outlineColor: customOutlineColor, - activeOutlineColor, - outlineStyle, - textColor, - dense, - style, - theme, - render = (props: RenderProps) => , - multiline = false, - parentState, - innerRef, - onFocus, - forceFocus, - onBlur, - onChangeText, - onLayoutAnimatedText, - onLabelTextLayout, - onLeftAffixLayoutChange, - onRightAffixLayoutChange, - onInputLayout, - onLayout, - left, - right, - placeholderTextColor, - testID = 'text-input-outlined', - contentStyle, - scaledLabel, - ...rest -}: ChildTextInputProps) => { - const adornmentConfig = getAdornmentConfig({ left, right }); - const { direction } = useLocale(); - const isRTL = direction === 'rtl'; - - const { colors } = theme; - const roundness = theme.shapes.corner.extraSmall; - const font = theme.fonts.bodyLarge; - const hasActiveOutline = parentState.focused || error; - - const { INPUT_PADDING_HORIZONTAL, MIN_HEIGHT, ADORNMENT_OFFSET, MIN_WIDTH } = - getConstants(); - - const { - fontSize: fontSizeStyle, - fontWeight, - lineHeight: lineHeightStyle, - height, - backgroundColor = colors?.background, - textAlign, - ...viewStyle - } = (StyleSheet.flatten(style) || {}) as TextStyle; - const fontSize = fontSizeStyle || MAXIMIZED_LABEL_FONT_SIZE; - const lineHeight = - lineHeightStyle || (Platform.OS === 'web' ? fontSize * 1.2 : undefined); - - const { - inputTextColor, - activeColor, - disabledOpacity, - outlineColor, - placeholderColor, - errorColor, - selectionColor, - } = getOutlinedInputColors({ - activeOutlineColor, - customOutlineColor, - customSelectionColor, - textColor, - disabled, - error, - theme, - }); - - const densePaddingTop = label ? LABEL_PADDING_TOP_DENSE : 0; - const paddingTop = label ? LABEL_PADDING_TOP : 0; - const yOffset = label ? OUTLINE_MINIMIZED_LABEL_Y_OFFSET : 0; - - const labelScale = MINIMIZED_LABEL_FONT_SIZE / fontSize; - const fontScale = MAXIMIZED_LABEL_FONT_SIZE / fontSize; - - const labelWidth = parentState.labelLayout.width; - const labelHeight = parentState.labelLayout.height; - const labelHalfWidth = labelWidth / 2; - const labelHalfHeight = labelHeight / 2; - - const baseLabelTranslateX = - (isRTL ? 1 : -1) * - (labelHalfWidth - - (labelScale * labelWidth) / 2 - - (fontSize - MINIMIZED_LABEL_FONT_SIZE) * labelScale); - - let labelTranslationXOffset = 0; - const isAdornmentLeftIcon = adornmentConfig.some( - ({ side, type }) => - side === AdornmentSide.Left && type === AdornmentType.Icon - ); - const isAdornmentRightIcon = adornmentConfig.some( - ({ side, type }) => - side === AdornmentSide.Right && type === AdornmentType.Icon - ); - - if (isAdornmentLeftIcon) { - labelTranslationXOffset = - (isRTL ? -1 : 1) * ADORNMENT_SIZE + ADORNMENT_OFFSET; - } - - const minInputHeight = - (dense ? MIN_DENSE_HEIGHT_OUTLINED : MIN_HEIGHT) - paddingTop; - - const inputHeight = calculateInputHeight(labelHeight, height, minInputHeight); - - const topPosition = calculateLabelTopPosition( - labelHeight, - inputHeight, - paddingTop - ); - - if (height && typeof height !== 'number') { - // eslint-disable-next-line - console.warn('Currently we support only numbers in height prop'); - } - - const paddingSettings = { - height: height ? +height : null, - labelHalfHeight, - offset: paddingTop, - multiline: multiline ? multiline : null, - dense: dense ? dense : null, - topPosition, - fontSize, - lineHeight, - label, - scale: fontScale, - isAndroid: Platform.OS === 'android', - styles: StyleSheet.flatten( - dense ? styles.inputOutlinedDense : styles.inputOutlined - ) as Padding, - }; - - const pad = calculatePadding(paddingSettings); - - const paddingOut = adjustPaddingOut({ ...paddingSettings, pad }); - - const baseLabelTranslateY = -labelHalfHeight - (topPosition + yOffset); - - const { current: placeholderOpacityAnims } = React.useRef([ - new Animated.Value(0), - new Animated.Value(1), - ]); - - const placeholderOpacity = hasActiveOutline - ? parentState.labeled - : placeholderOpacityAnims[parentState.labelLayout.measured ? 1 : 0]; - - const placeholderStyle = { - position: 'absolute', - left: 0, - paddingHorizontal: INPUT_PADDING_HORIZONTAL, - }; - - const placeholderTextColorBasedOnState = parentState.displayPlaceholder - ? placeholderTextColor ?? placeholderColor - : 'transparent'; - - const labelBackgroundColor: ColorValue = - backgroundColor === 'transparent' - ? theme.colors.background - : backgroundColor; - - const labelProps = { - label, - onLayoutAnimatedText, - onLabelTextLayout, - placeholderOpacity, - labelError: error, - placeholderStyle, - baseLabelTranslateY, - baseLabelTranslateX, - font, - fontSize, - lineHeight, - fontWeight, - labelScale, - wiggleOffsetX: LABEL_WIGGLE_X_OFFSET, - topPosition, - hasActiveOutline, - activeColor, - placeholderColor, - disabledOpacity, - backgroundColor: labelBackgroundColor, - errorColor, - labelTranslationXOffset, - roundness, - maxFontSizeMultiplier: rest.maxFontSizeMultiplier, - testID, - contentStyle, - inputContainerLayout: { - width: - parentState.inputContainerLayout.width + - (isAdornmentRightIcon || isAdornmentLeftIcon - ? INPUT_PADDING_HORIZONTAL - : 0), - }, - opacity: - parentState.value || parentState.focused - ? parentState.labelLayout.measured - ? 1 - : 0 - : 1, - }; - - const onLayoutChange = React.useCallback( - (e: LayoutChangeEvent) => { - onInputLayout(e); - onLayout?.(e); - }, - [onLayout, onInputLayout] - ); - - const minHeight = (height || - (dense ? MIN_DENSE_HEIGHT_OUTLINED : MIN_HEIGHT)) as number; - - const outlinedHeight = - inputHeight + (dense ? densePaddingTop / 2 : paddingTop); - const { leftLayout, rightLayout } = parentState; - - const leftAffixTopPosition = calculateOutlinedIconAndAffixTopPosition({ - height: outlinedHeight, - affixHeight: leftLayout.height || 0, - labelYOffset: -yOffset, - }); - - const rightAffixTopPosition = calculateOutlinedIconAndAffixTopPosition({ - height: outlinedHeight, - affixHeight: rightLayout.height || 0, - labelYOffset: -yOffset, - }); - const iconTopPosition = calculateOutlinedIconAndAffixTopPosition({ - height: outlinedHeight, - affixHeight: ADORNMENT_SIZE, - labelYOffset: -yOffset, - }); - - const rightAffixWidth = right - ? rightLayout.width || ADORNMENT_SIZE - : ADORNMENT_SIZE; - - const leftAffixWidth = left - ? leftLayout.width || ADORNMENT_SIZE - : ADORNMENT_SIZE; - - const adornmentStyleAdjustmentForNativeInput = - getAdornmentStyleAdjustmentForNativeInput({ - adornmentConfig, - rightAffixWidth, - leftAffixWidth, - mode: 'outlined', - }); - const affixTopPosition = { - [AdornmentSide.Left]: leftAffixTopPosition, - [AdornmentSide.Right]: rightAffixTopPosition, - }; - const onAffixChange = { - [AdornmentSide.Left]: onLeftAffixLayoutChange, - [AdornmentSide.Right]: onRightAffixLayoutChange, - }; - - let adornmentProps: TextInputAdornmentProps = { - adornmentConfig, - forceFocus, - topPosition: { - [AdornmentType.Icon]: iconTopPosition, - [AdornmentType.Affix]: affixTopPosition, - }, - onAffixChange, - isTextInputFocused: parentState.focused, - maxFontSizeMultiplier: rest.maxFontSizeMultiplier, - disabled, - }; - if (adornmentConfig.length) { - adornmentProps = { - ...adornmentProps, - left, - right, - textStyle: { ...font, fontSize, lineHeight, fontWeight }, - visible: parentState.labeled, - }; - } - - return ( - - {/* - Render the outline separately from the container - This is so that the label can overlap the outline - Otherwise the border will cut off the label on Android - */} - - - {label ? ( - - ) : null} - {render?.({ - ...rest, - ref: innerRef, - onLayout: onLayoutChange, - onChangeText, - placeholder: rest.placeholder, - editable: !disabled && editable, - selectionColor, - cursorColor: - typeof cursorColor === 'undefined' ? activeColor : cursorColor, - placeholderTextColor: placeholderTextColorBasedOnState, - onFocus, - onBlur, - underlineColorAndroid: 'transparent', - multiline, - style: [ - styles.input, - !multiline || (multiline && height) ? { height: inputHeight } : {}, - paddingOut, - { - ...font, - fontSize, - lineHeight, - fontWeight, - color: inputTextColor, - opacity: disabledOpacity, - textAlignVertical: multiline ? 'top' : 'center', - textAlign: textAlign ? textAlign : isRTL ? 'right' : 'left', - paddingHorizontal: INPUT_PADDING_HORIZONTAL, - minWidth: Math.min( - parentState.labelTextLayout.width + - 2 * INPUT_PADDING_HORIZONTAL, - MIN_WIDTH - ), - }, - Platform.OS === 'web' ? { outline: 'none' } : undefined, - adornmentStyleAdjustmentForNativeInput, - contentStyle, - ], - testID, - } as RenderProps)} - - - - ); -}; - -export default TextInputOutlined; - -const styles = StyleSheet.create({ - labelContainer: { - paddingBottom: 0, - flexGrow: 1, - }, - input: { - margin: 0, - flexGrow: 1, - }, - inputOutlined: { - paddingTop: 8, - paddingBottom: 8, - }, - inputOutlinedDense: { - paddingTop: 4, - paddingBottom: 4, - }, -}); diff --git a/src/components/TextInput/constants.ts b/src/components/TextInput/constants.ts new file mode 100644 index 0000000000..524593278f --- /dev/null +++ b/src/components/TextInput/constants.ts @@ -0,0 +1,137 @@ +import { PixelRatio } from 'react-native'; + +import { Easing } from 'react-native-reanimated'; + +import { tokens } from '../../theme/tokens'; +import { motionDuration, motionEasing } from '../../theme/tokens/sys/motion'; +import { defaultShapes } from '../../theme/tokens/sys/shape'; + +export const fontScale = PixelRatio.getFontScale(); + +/** + * Common constants for the text input component. + */ + +export const BASELINE_TEXT_INPUT_HEIGHT = 56; +export const BASELINE_TEXT_INPUT_PADDING_VERTICAL = 8; + +export const TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL = 16; +export const TEXT_INPUT_ACCESSORY_MARGIN_HORIZONTAL = 12; + +export const TEXT_INPUT_HEIGHT = Math.ceil( + BASELINE_TEXT_INPUT_HEIGHT * fontScale +); +export const TEXT_INPUT_PADDING_VERTICAL = Math.ceil( + BASELINE_TEXT_INPUT_PADDING_VERTICAL * fontScale +); + +export const TEXT_INPUT_BORDER_RADIUS = defaultShapes.corner.extraSmall; + +export const LABEL_START_OFFSET_WITHOUT_ACCESSORY = + TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL; + +export const ACCESSORY_SIZE = 24; + +export const PREFIX_END_PADDING = 2; +export const SUFFIX_START_PADDING = 2; + +export const ERROR_ICON_SIZE = 16; + +export const INPUT_FONT_SIZE = tokens.md.sys.typescale.bodyLarge.fontSize; +export const ACTIVE_LABEL_FONT_SIZE = + tokens.md.sys.typescale.bodySmall.fontSize; +export const INACTIVE_LABEL_FONT_SIZE = INPUT_FONT_SIZE; +export const SUPPORTING_TEXT_FONT_SIZE = + tokens.md.sys.typescale.bodySmall.fontSize; + +export const SUPPORTING_TEXT_MARGIN_TOP = 4; + +export const ANIMATION_DURATION_MS = motionDuration.short3; + +export const ACTIVE_INDICATOR_SIZE = 2; +export const INACTIVE_INDICATOR_SIZE = 1; + +export const TIMING_CONFIG = { + duration: ANIMATION_DURATION_MS, + easing: Easing.bezier(...motionEasing.standard), +} as const; + +/** + * Constants for the filled variant. + */ + +const FILLED_LINE_HEIGHT_DELTA = 3; + +export const FILLED_LABEL_START_OFFSET_WITH_ACCESSORY = + ACCESSORY_SIZE + + TEXT_INPUT_ACCESSORY_MARGIN_HORIZONTAL + + TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL; + +export const FILLED_ACTIVE_LABEL_TOP_POSITION = TEXT_INPUT_PADDING_VERTICAL; + +export const FILLED_INACTIVE_LABEL_TOP_POSITION = Math.ceil( + ((BASELINE_TEXT_INPUT_HEIGHT - + 2 * BASELINE_TEXT_INPUT_PADDING_VERTICAL - + INPUT_FONT_SIZE) / + 2 + + BASELINE_TEXT_INPUT_PADDING_VERTICAL) * + fontScale +); + +export const FILLED_MULTILINE_PADDING_TOP = + Math.ceil(ACTIVE_LABEL_FONT_SIZE * fontScale) + TEXT_INPUT_PADDING_VERTICAL; + +export const FILLED_DISABLED_CONTAINER_OPACITY = 0.04; + +export const FILLED_PADDING_BOTTOM = + TEXT_INPUT_PADDING_VERTICAL + FILLED_LINE_HEIGHT_DELTA; + +/** + * Constants for the outlined variant. + */ + +const OUTLINED_LINE_HEIGHT_DELTA = 2; + +export const OUTLINED_DISABLED_OUTLINE_OPACITY = 0.12; + +export const OUTLINED_MULTILINE_PADDING_TOP = Math.ceil( + ((BASELINE_TEXT_INPUT_HEIGHT - + 2 * BASELINE_TEXT_INPUT_PADDING_VERTICAL - + INPUT_FONT_SIZE) / + 2 - + OUTLINED_LINE_HEIGHT_DELTA) * + fontScale +); + +export const OUTLINED_LABEL_PADDING_HORIZONTAL = 4; + +export const OUTLINED_LABEL_START_OFFSET_WITH_ACCESSORY = + ACCESSORY_SIZE + + TEXT_INPUT_ACCESSORY_MARGIN_HORIZONTAL + + TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL - + OUTLINED_LABEL_PADDING_HORIZONTAL; + +export const OUTLINED_ACTIVE_LABEL_TOP_POSITION = Math.ceil( + (-BASELINE_TEXT_INPUT_PADDING_VERTICAL + OUTLINED_LINE_HEIGHT_DELTA) * + fontScale +); + +export const OUTLINED_INACTIVE_LABEL_TOP_POSITION = Math.ceil( + ((BASELINE_TEXT_INPUT_HEIGHT - + 2 * BASELINE_TEXT_INPUT_PADDING_VERTICAL - + INPUT_FONT_SIZE) / + 2 + + BASELINE_TEXT_INPUT_PADDING_VERTICAL - + OUTLINED_LINE_HEIGHT_DELTA) * + fontScale +); + +/** Positive distance; apply sign in animation using `isRTL` from `useLocale`. */ +export const OUTLINED_LABEL_TRANSLATE_DISTANCE_WITH_ACCESSORY = + ACCESSORY_SIZE + + TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL - + OUTLINED_LABEL_PADDING_HORIZONTAL; + +/** Positive distance; apply sign in animation using `isRTL` from `useLocale`. */ +export const OUTLINED_LABEL_TRANSLATE_DISTANCE_WITHOUT_ACCESSORY = + OUTLINED_LABEL_PADDING_HORIZONTAL; diff --git a/src/components/TextInput/constants.tsx b/src/components/TextInput/constants.tsx deleted file mode 100644 index d85d28e542..0000000000 --- a/src/components/TextInput/constants.tsx +++ /dev/null @@ -1,39 +0,0 @@ -export const MAXIMIZED_LABEL_FONT_SIZE = 16; -export const MINIMIZED_LABEL_FONT_SIZE = 12; -export const LABEL_WIGGLE_X_OFFSET = 4; - -export const ADORNMENT_SIZE = 24; -export const MIN_WIDTH = 100; - -//Text input affix offset -export const MD3_AFFIX_OFFSET = 16; - -// Text input icon -export const ICON_SIZE = 24; -export const MD3_ICON_OFFSET = 16; - -// Text input common -export const MD3_MIN_HEIGHT = 56; -export const MD3_ADORNMENT_OFFSET = 16; -export const LABEL_PADDING_TOP_DENSE = 24; -export const LABEL_PADDING_TOP = 8; - -// Text input flat -export const MD3_LABEL_PADDING_TOP = 26; - -export const MD3_LABEL_PADDING_HORIZONTAL = 16; - -export const MD3_FLAT_INPUT_OFFSET = 16; - -export const MINIMIZED_LABEL_Y_OFFSET = -18; -export const MIN_DENSE_HEIGHT_WL = 52; -export const MIN_DENSE_HEIGHT = 40; - -// Text input outlined -export const MD3_INPUT_PADDING_HORIZONTAL = 16; - -// extra space to avoid overlapping input's text and icon -export const MD3_OUTLINED_INPUT_OFFSET = 16; - -export const OUTLINE_MINIMIZED_LABEL_Y_OFFSET = -6; -export const MIN_DENSE_HEIGHT_OUTLINED = 48; diff --git a/src/components/TextInput/helpers.tsx b/src/components/TextInput/helpers.tsx deleted file mode 100644 index c633314991..0000000000 --- a/src/components/TextInput/helpers.tsx +++ /dev/null @@ -1,503 +0,0 @@ -import type { ColorValue } from 'react-native'; - -import { AdornmentSide, AdornmentType } from './Adornment/enums'; -import type { AdornmentConfig } from './Adornment/types'; -import { - MIN_WIDTH, - ADORNMENT_SIZE, - MD3_ADORNMENT_OFFSET, - MD3_AFFIX_OFFSET, - MD3_FLAT_INPUT_OFFSET, - MD3_ICON_OFFSET, - MD3_INPUT_PADDING_HORIZONTAL, - MD3_LABEL_PADDING_HORIZONTAL, - MD3_LABEL_PADDING_TOP, - MD3_MIN_HEIGHT, - MD3_OUTLINED_INPUT_OFFSET, -} from './constants'; -import type { TextInputLabelProp } from './types'; -import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; - -const stateOpacity = tokens.md.sys.state.opacity; - -type PaddingProps = { - height: number | null; - labelHalfHeight: number; - multiline: boolean | null; - dense: boolean | null; - topPosition: number; - fontSize: number; - lineHeight?: number; - label?: TextInputLabelProp | null; - scale: number; - offset: number; - isAndroid: boolean; - styles: { paddingTop: number; paddingBottom: number }; -}; - -type AdjProps = PaddingProps & { - pad: number; -}; - -export type Padding = { paddingTop: number; paddingBottom: number }; - -export const calculateLabelTopPosition = ( - labelHeight: number, - height: number = 0, - optionalPadding: number = 0 -): number => { - const customHeight = height > 0 ? height : 0; - - return Math.floor((customHeight - labelHeight) / 2 + optionalPadding); -}; - -export const calculateInputHeight = ( - labelHeight: number, - height: any = 0, - minHeight: number -): number => { - const finalHeight = height > 0 ? height : labelHeight; - - if (height > 0) return height; - return finalHeight < minHeight ? minHeight : finalHeight; -}; - -export const calculatePadding = (props: PaddingProps): number => { - const { height, multiline = false } = props; - - let result = 0; - - if (multiline) { - if (height && multiline) { - result = calculateTextAreaPadding(props); - } else { - result = calculateInputPadding(props); - } - } - - return Math.max(0, result); -}; - -const calculateTextAreaPadding = (props: PaddingProps) => { - const { dense } = props; - - return dense ? 10 : 20; -}; - -const calculateInputPadding = ({ - topPosition, - fontSize, - multiline, - scale, - dense, - offset, - isAndroid, -}: PaddingProps): number => { - const refFontSize = scale * fontSize; - let result = Math.floor(topPosition / 2); - - result = - result + - Math.floor((refFontSize - fontSize) / 2) - - (scale < 1 ? offset / 2 : 0); - - if (multiline && isAndroid) - result = Math.min(dense ? offset / 2 : offset, result); - - return result; -}; - -export const adjustPaddingOut = ({ - pad, - multiline, - label, - scale, - height, - fontSize, - lineHeight, - dense, - offset, - isAndroid, -}: AdjProps): Padding => { - const fontHeight = lineHeight ?? fontSize; - const refFontHeight = scale * fontSize; - let result = pad; - - if (!isAndroid && height && !multiline) { - return { - paddingTop: Math.max(0, (height - fontHeight) / 2), - paddingBottom: Math.max(0, (height - fontHeight) / 2), - }; - } - if (!isAndroid && multiline) { - if (dense) { - if (label) { - result += scale < 1 ? Math.min(offset, (refFontHeight / 2) * scale) : 0; - } else { - result += 0; - } - } - if (!dense) { - if (label) { - result += - scale < 1 - ? Math.min(offset, refFontHeight * scale) - : Math.min(offset / 2, refFontHeight * scale); - } else { - result += scale < 1 ? Math.min(offset / 2, refFontHeight * scale) : 0; - } - } - result = Math.floor(result); - } - return { paddingTop: result, paddingBottom: result }; -}; - -export const adjustPaddingFlat = ({ - pad, - scale, - multiline, - label, - height, - offset, - dense, - fontSize, - isAndroid, - styles, -}: AdjProps): Padding => { - let result = pad; - let topResult = result; - let bottomResult = result; - const { paddingTop, paddingBottom } = styles; - const refFontSize = scale * fontSize; - - if (!multiline) { - // do not modify padding if input is not multiline - if (label) { - // return const style for flat input with label - return { paddingTop, paddingBottom }; - } - // return pad for flat input without label - return { paddingTop: result, paddingBottom: result }; - } - - if (label) { - // add paddings passed from styles - topResult = paddingTop; - bottomResult = paddingBottom; - - // adjust top padding for iOS - if (!isAndroid) { - if (dense) { - topResult += - scale < 1 - ? Math.min(result, refFontSize * scale) - result / 2 - : Math.min(result, refFontSize * scale) - result / 2; - } - if (!dense) { - topResult += - scale < 1 - ? Math.min(offset / 2, refFontSize * scale) - : Math.min(result, refFontSize * scale) - offset / 2; - } - } - topResult = Math.floor(topResult); - } else { - if (height) { - // center text when height is passed - return { - paddingTop: Math.max(0, (height - fontSize) / 2), - paddingBottom: Math.max(0, (height - fontSize) / 2), - }; - } - // adjust paddings for iOS if no label - if (!isAndroid) { - if (dense) { - result += - scale < 1 - ? Math.min(offset / 2, (fontSize / 2) * scale) - : Math.min(offset / 2, scale); - } - if (!dense) { - result += - scale < 1 - ? Math.min(offset, fontSize * scale) - : Math.min(fontSize, (offset / 2) * scale); - } - - result = Math.floor(result); - topResult = result; - bottomResult = result; - } - } - - return { - paddingTop: Math.max(0, topResult), - paddingBottom: Math.max(0, bottomResult), - }; -}; - -export function calculateFlatAffixTopPosition({ - height, - paddingTop, - paddingBottom, - affixHeight, -}: { - height: number; - paddingTop: number; - paddingBottom: number; - affixHeight: number; -}): number { - const inputHeightWithoutPadding = height - paddingTop - paddingBottom; - - const halfOfTheInputHeightDecreasedByAffixHeight = - (inputHeightWithoutPadding - affixHeight) / 2; - - return paddingTop + halfOfTheInputHeightDecreasedByAffixHeight; -} - -export function calculateOutlinedIconAndAffixTopPosition({ - height, - affixHeight, - labelYOffset, -}: { - height: number; - affixHeight: number; - labelYOffset: number; -}): number { - return (height - affixHeight + labelYOffset) / 2; -} - -export const calculateFlatInputHorizontalPadding = ({ - adornmentConfig, -}: { - adornmentConfig: AdornmentConfig[]; -}) => { - const { LABEL_PADDING_HORIZONTAL, ADORNMENT_OFFSET, FLAT_INPUT_OFFSET } = - getConstants(); - - let paddingLeft = LABEL_PADDING_HORIZONTAL; - let paddingRight = LABEL_PADDING_HORIZONTAL; - - adornmentConfig.forEach(({ type, side }) => { - if (type === AdornmentType.Icon && side === AdornmentSide.Left) { - paddingLeft = ADORNMENT_SIZE + ADORNMENT_OFFSET + FLAT_INPUT_OFFSET; - } else if (side === AdornmentSide.Right) { - if (type === AdornmentType.Affix) { - paddingRight = ADORNMENT_SIZE + ADORNMENT_OFFSET + FLAT_INPUT_OFFSET; - } else if (type === AdornmentType.Icon) { - paddingRight = ADORNMENT_SIZE + ADORNMENT_OFFSET + FLAT_INPUT_OFFSET; - } - } - }); - - return { paddingLeft, paddingRight }; -}; - -type BaseProps = { - theme: InternalTheme; - disabled?: boolean; -}; - -type Mode = 'flat' | 'outlined'; - -const getInputTextColor = ({ - theme, - textColor, -}: BaseProps & { textColor?: ColorValue }) => { - if (textColor) { - return textColor; - } - - return theme.colors.onSurface; -}; - -const getActiveColor = ({ - theme, - error, - activeUnderlineColor, - activeOutlineColor, - mode, -}: BaseProps & { - error?: boolean; - activeUnderlineColor?: ColorValue; - activeOutlineColor?: ColorValue; - mode?: Mode; -}) => { - const isFlat = mode === 'flat'; - const modeColor = isFlat ? activeUnderlineColor : activeOutlineColor; - - if (error) { - return theme.colors.error; - } - - if (modeColor) { - return modeColor; - } - - return theme.colors.primary; -}; - -const getPlaceholderColor = ({ theme }: BaseProps) => { - return theme.colors.onSurfaceVariant; -}; - -const getSelectionColor = ({ - activeColor, - customSelectionColor, -}: { - activeColor: ColorValue; - customSelectionColor?: ColorValue; -}) => { - if (typeof customSelectionColor !== 'undefined') { - return customSelectionColor; - } - return activeColor; -}; - -const getFlatBackgroundColor = ({ theme, disabled }: BaseProps) => { - if (disabled) { - return theme.colors.surfaceContainerHighest; - } - - return theme.colors.surfaceVariant; -}; - -const getFlatUnderlineColor = ({ - theme, - disabled, - underlineColor, -}: BaseProps & { underlineColor?: ColorValue }) => { - if (!disabled && underlineColor) { - return underlineColor; - } - - return theme.colors.onSurfaceVariant; -}; - -const getOutlinedOutlineInputColor = ({ - theme, - disabled, - customOutlineColor, -}: BaseProps & { customOutlineColor?: ColorValue }) => { - if (!disabled && customOutlineColor) { - return customOutlineColor; - } - - if (disabled) { - if (theme.dark) { - return 'transparent'; - } - return theme.colors.outlineVariant; - } - - return theme.colors.outline; -}; - -export const getFlatInputColors = ({ - underlineColor, - activeUnderlineColor, - customSelectionColor, - textColor, - disabled, - error, - theme, -}: { - underlineColor?: ColorValue; - activeUnderlineColor?: ColorValue; - customSelectionColor?: ColorValue; - textColor?: ColorValue; - disabled?: boolean; - error?: boolean; - theme: InternalTheme; -}) => { - const baseFlatColorProps = { theme, disabled }; - const activeColor = getActiveColor({ - ...baseFlatColorProps, - error, - activeUnderlineColor, - mode: 'flat', - }); - - const disabledOpacity = disabled - ? stateOpacity.disabled - : stateOpacity.enabled; - - return { - inputTextColor: getInputTextColor({ - ...baseFlatColorProps, - textColor, - }), - activeColor, - disabledOpacity, - underlineColorCustom: getFlatUnderlineColor({ - ...baseFlatColorProps, - underlineColor, - }), - placeholderColor: getPlaceholderColor(baseFlatColorProps), - selectionColor: getSelectionColor({ activeColor, customSelectionColor }), - errorColor: theme.colors.error, - backgroundColor: getFlatBackgroundColor(baseFlatColorProps), - }; -}; - -export const getOutlinedInputColors = ({ - activeOutlineColor, - customOutlineColor, - customSelectionColor, - textColor, - disabled, - error, - theme, -}: { - activeOutlineColor?: ColorValue; - customOutlineColor?: ColorValue; - customSelectionColor?: ColorValue; - textColor?: ColorValue; - disabled?: boolean; - error?: boolean; - theme: InternalTheme; -}) => { - const baseOutlinedColorProps = { theme, disabled }; - const activeColor = getActiveColor({ - ...baseOutlinedColorProps, - error, - activeOutlineColor, - mode: 'outlined', - }); - - const disabledOpacity = disabled - ? stateOpacity.disabled - : stateOpacity.enabled; - - return { - inputTextColor: getInputTextColor({ - ...baseOutlinedColorProps, - textColor, - }), - activeColor, - disabledOpacity, - outlineColor: getOutlinedOutlineInputColor({ - ...baseOutlinedColorProps, - customOutlineColor, - }), - placeholderColor: getPlaceholderColor(baseOutlinedColorProps), - selectionColor: getSelectionColor({ activeColor, customSelectionColor }), - errorColor: theme.colors.error, - }; -}; - -export const getConstants = () => { - return { - AFFIX_OFFSET: MD3_AFFIX_OFFSET, - ICON_OFFSET: MD3_ICON_OFFSET, - LABEL_PADDING_TOP: MD3_LABEL_PADDING_TOP, - LABEL_PADDING_HORIZONTAL: MD3_LABEL_PADDING_HORIZONTAL, - FLAT_INPUT_OFFSET: MD3_FLAT_INPUT_OFFSET, - MIN_HEIGHT: MD3_MIN_HEIGHT, - INPUT_PADDING_HORIZONTAL: MD3_INPUT_PADDING_HORIZONTAL, - ADORNMENT_OFFSET: MD3_ADORNMENT_OFFSET, - OUTLINED_INPUT_OFFSET: MD3_OUTLINED_INPUT_OFFSET, - MIN_WIDTH, - }; -}; diff --git a/src/components/TextInput/hooks.ts b/src/components/TextInput/hooks.ts new file mode 100644 index 0000000000..968cde91f0 --- /dev/null +++ b/src/components/TextInput/hooks.ts @@ -0,0 +1,470 @@ +import { + useEffect, + useImperativeHandle, + useRef, + useState, + type RefObject, +} from 'react'; +import { + BlurEvent, + FocusEvent, + TextInput as NativeTextInput, +} from 'react-native'; + +import { + interpolate, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; + +import { + ACTIVE_LABEL_FONT_SIZE, + INACTIVE_LABEL_FONT_SIZE, + TIMING_CONFIG, +} from './constants'; +import type { + TextInputAnimationState, + TextInputFlags, + TextInputAnimationHandlers, + TextInputHookReturn, + TextInputLayoutState, + TextInputProps, + TextInputVariant, +} from './TextInput'; +import { + getAccentColors, + getAccessibilityData, + getFilledTextInputData, + getOutlinedTextInputData, + getTextInputAnimationLayout, +} from './utils'; +import { useLocale } from '../../core/locale'; +import { useInternalTheme } from '../../core/theming'; +import type { InternalTheme } from '../../types'; + +const useTextInputAnimation = ({ + variant, + isRTL, + hasAccessory, + value, + defaultValue, +}: { + variant: TextInputVariant; + isRTL: boolean; + hasAccessory: boolean; + value: string | undefined; + defaultValue: string | undefined; +}): TextInputAnimationState & TextInputAnimationHandlers => { + const initialText = value ?? defaultValue ?? ''; + + const focusSV = useSharedValue(0); + const floatSV = useSharedValue(initialText.length > 0 ? 1 : 0); + + const { activeTop, inactiveTop, translateXEnd } = getTextInputAnimationLayout( + { + variant, + hasAccessory, + isRTL, + } + ); + + const runFocusAnimation = (hasText: boolean) => { + focusSV.value = withTiming(1, TIMING_CONFIG); + + if (!hasText) { + floatSV.value = withTiming(1, TIMING_CONFIG); + } + }; + + const runBlurAnimation = (hasText: boolean) => { + focusSV.value = withTiming(0, TIMING_CONFIG); + + floatSV.value = withTiming(hasText ? 1 : 0, TIMING_CONFIG); + }; + + const syncFloatToValue = (hasText: boolean) => { + floatSV.value = withTiming(hasText ? 1 : 0, TIMING_CONFIG); + }; + + const animatedLabelWrapperStyle = useAnimatedStyle(() => { + const top = interpolate(floatSV.value, [0, 1], [inactiveTop, activeTop]); + + if (variant === 'filled') { + return { top }; + } + + return { + top, + transform: [ + { translateX: interpolate(floatSV.value, [0, 1], [0, translateXEnd]) }, + ], + }; + }); + + const animatedLabelTextStyle = useAnimatedStyle(() => ({ + fontSize: interpolate( + floatSV.value, + [0, 1], + [INACTIVE_LABEL_FONT_SIZE, ACTIVE_LABEL_FONT_SIZE] + ), + })); + + const animatedContainerStyle = useAnimatedStyle(() => ({ + opacity: floatSV.value, + })); + + const animatedActiveOutlineStyle = useAnimatedStyle(() => ({ + transform: [{ scaleX: focusSV.value }], + })); + + return { + animatedLabelWrapperStyle, + animatedLabelTextStyle, + animatedContainerStyle, + animatedActiveOutlineStyle: + variant === 'filled' ? animatedActiveOutlineStyle : undefined, + runFocusAnimation, + runBlurAnimation, + syncFloatToValue, + }; +}; + +const useTextInputInput = ( + props: Pick< + TextInputProps, + 'value' | 'defaultValue' | 'onChangeText' | 'counter' | 'maxLength' + > +) => { + const isControlled = props.value !== undefined; + const hasCounter = !!(props.counter && props.maxLength); + + const initialText = isControlled ? props.value : props.defaultValue; + + const ref = useRef(initialText ?? ''); + + const [hasValue, setHasValue] = useState(!!initialText); + const [charCount, setCharCount] = useState(initialText?.length ?? 0); + + const inputLength = isControlled + ? props.value?.length ?? 0 + : hasCounter + ? charCount + : hasValue + ? 1 + : 0; + + const getHasText = () => { + if (isControlled) { + return !!props.value; + } + + return ref.current.length > 0; + }; + + const onChangeText = (text: string) => { + ref.current = text; + + if (!isControlled) { + const next = text.length > 0; + + if (hasCounter) { + setCharCount(text.length); + } + + if (next !== hasValue) { + setHasValue(next); + } + } + + props.onChangeText?.(text); + }; + + return { + hasValue: isControlled ? !!props.value : hasValue, + inputLength, + getHasText, + onChangeText, + }; +}; + +const useTextInputFocus = ( + props: Pick, + input: RefObject, + isDisabled: boolean, + { runFocusAnimation, runBlurAnimation }: TextInputAnimationHandlers, + getHasText: () => boolean +) => { + const [isFocused, setIsFocused] = useState(false); + + const onFocus = (e: FocusEvent) => { + props.onFocus?.(e); + + setIsFocused(true); + + runFocusAnimation(getHasText()); + }; + + const onBlur = (e: BlurEvent) => { + props.onBlur?.(e); + + setIsFocused(false); + + runBlurAnimation(getHasText()); + }; + + const focusInput = () => { + if (isDisabled) return; + + input.current?.focus(); + }; + + return { + isFocused, + onFocus, + onBlur, + focusInput, + }; +}; + +const useTextInputFlags = ( + props: TextInputProps, + isFocused: boolean, + hasValue: boolean, + isRTL: boolean, + hasAccessory: boolean +): TextInputFlags => { + const isFloating = isFocused || hasValue; + + return { + isRTL, + isFloating, + isDisabled: !!props.disabled, + isEditable: props.disabled ? false : props.editable, + hasError: !!props.error, + hasCounter: !!(props.counter && props.maxLength), + hasAccessory, + hasPrefix: !!props.prefix && isFloating, + hasSuffix: !!props.suffix && isFloating, + }; +}; + +const useTextInputLayout = ({ + variant, + props, + input, + theme, + flags, + isFocused, + animation, +}: { + variant: TextInputVariant; + props: TextInputProps; + input: RefObject; + theme: InternalTheme; + flags: TextInputFlags; + isFocused: boolean; + animation: TextInputAnimationState; +}): TextInputLayoutState => { + const { isRTL, isDisabled, hasError, hasAccessory, hasSuffix } = flags; + + const { + animatedLabelWrapperStyle, + animatedLabelTextStyle, + animatedActiveOutlineStyle, + } = animation; + + const { + input: _input, + isDisabled: _isDisabled, + hasError: _hasError, + hasSuffix: _hasSuffix, + ...layout + } = variant === 'filled' + ? getFilledTextInputData( + { + input, + theme, + isFocused, + isRTL, + isDisabled, + hasAccessory, + hasError, + hasSuffix, + animatedLabelWrapperStyle, + animatedLabelTextStyle, + animatedActiveOutlineStyle, + }, + props + ) + : getOutlinedTextInputData( + { + input, + theme, + isFocused, + isRTL, + isDisabled, + hasAccessory, + hasError, + hasSuffix, + animatedLabelWrapperStyle, + animatedLabelTextStyle, + animatedActiveOutlineStyle, + }, + props + ); + + void _input; + void _isDisabled; + void _hasError; + void _hasSuffix; + + return layout; +}; + +export const useTextInput = (props: TextInputProps): TextInputHookReturn => { + const { ref, variant = 'filled', theme: themeOverride } = props; + + const input = useRef(null); + const init = useRef(false); + + const theme = useInternalTheme(themeOverride); + + const { direction } = useLocale(); + + const isControlled = props.value !== undefined; + const isRTL = direction === 'rtl'; + const hasAccessory = isRTL ? !!props.endAccessory : !!props.startAccessory; + + const { hasValue, inputLength, getHasText, onChangeText } = + useTextInputInput(props); + + const animation = useTextInputAnimation({ + variant, + isRTL, + hasAccessory, + value: props.value, + defaultValue: props.defaultValue, + }); + + const { isFocused, onFocus, onBlur, focusInput } = useTextInputFocus( + props, + input, + !!props.disabled, + animation, + getHasText + ); + + const flags = useTextInputFlags( + props, + isFocused, + hasValue, + isRTL, + hasAccessory + ); + + /** + * Edge case: a controlled `value` can change programmatically. + * Reconcile the float animation with the new text here. + * While focused the label is always floated, so we skip to avoid fighting the + * focus animation, and depend only on `value` to avoid re-running on + * focus/blur transitions (those are already handled by their own handlers) + */ + useEffect(() => { + if (!init.current) { + init.current = true; + return; + } + + if (isControlled && !isFocused) { + animation.syncFloatToValue(!!props.value); + } + // eslint-disable-next-line react-hooks/exhaustive-deps -- [props.value] is the only dep that should act as a trigger + }, [props.value]); + + useImperativeHandle(ref, () => ({ + focus: () => { + if (flags.isDisabled) return; + + input.current?.focus(); + }, + clear: () => { + input.current?.clear(); + + onChangeText(''); + + if (!input.current?.isFocused()) { + animation.runBlurAnimation(false); + } + }, + blur: () => input.current?.blur(), + isFocused: () => input.current?.isFocused() || false, + setNativeProps: (args: Object) => input.current?.setNativeProps(args), + setSelection: (start: number, end: number) => + input.current?.setSelection(start, end), + })); + + const { selectionColor, cursorColor } = getAccentColors({ + theme, + hasError: flags.hasError, + }); + + const placeholderTextColor = + props.placeholderTextColor ?? theme.colors.onSurfaceVariant; + + const layout = useTextInputLayout({ + variant, + props, + input, + theme, + flags, + isFocused, + animation, + }); + + const accessibilityProps = getAccessibilityData({ + hasError: flags.hasError, + hasCounter: flags.hasCounter, + isDisabled: flags.isDisabled, + data: props, + inputLength, + }); + + const counterText = `${inputLength}/${props.maxLength}`; + + const renderLeadingAccessory = flags.isRTL + ? props.endAccessory + : props.startAccessory; + const renderTrailingAccessory = flags.isRTL + ? props.startAccessory + : props.endAccessory; + + // https://github.com/facebook/react-native/issues/31573 + const placeholder = + isFocused || !props.label || hasValue ? props.placeholder : ' '; + + return { + input, + isDisabled: flags.isDisabled, + isEditable: flags.isEditable, + hasPrefix: flags.hasPrefix, + hasCounter: flags.hasCounter, + hasSuffix: flags.hasSuffix, + hasError: flags.hasError, + placeholderTextColor, + selectionColor, + cursorColor, + animatedActiveOutlineStyles: undefined, + animatedContainerStyle: animation.animatedContainerStyle, + placeholder, + counterText, + accessibilityProps, + ...layout, + renderLeadingAccessory, + renderTrailingAccessory, + onChangeText, + onFocus, + onBlur, + focusInput, + }; +}; diff --git a/src/components/TextInput/index.ts b/src/components/TextInput/index.ts new file mode 100644 index 0000000000..b6d400c68c --- /dev/null +++ b/src/components/TextInput/index.ts @@ -0,0 +1,13 @@ +import TextInputComponent from './TextInput'; +import TextInputIcon from './TextInputIcon'; + +const TextInput = Object.assign( + // @component ./TextInput.tsx + TextInputComponent, + { + // @component ./TextInputIcon.tsx + Icon: TextInputIcon, + } +); + +export default TextInput; diff --git a/src/components/TextInput/styles.ts b/src/components/TextInput/styles.ts new file mode 100644 index 0000000000..0a65662a5a --- /dev/null +++ b/src/components/TextInput/styles.ts @@ -0,0 +1,128 @@ +import { StyleSheet } from 'react-native'; + +import { + ACCESSORY_SIZE, + FILLED_DISABLED_CONTAINER_OPACITY, + OUTLINED_LABEL_PADDING_HORIZONTAL, + SUPPORTING_TEXT_FONT_SIZE, + SUPPORTING_TEXT_MARGIN_TOP, + TEXT_INPUT_ACCESSORY_MARGIN_HORIZONTAL, + TEXT_INPUT_BORDER_RADIUS, + TEXT_INPUT_HEIGHT, + TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL, + TEXT_INPUT_PADDING_VERTICAL, +} from './constants'; +import { tokens } from '../../theme/tokens'; + +const { bodyLarge, bodySmall } = tokens.md.sys.typescale; + +export const styles = StyleSheet.create({ + input: { + paddingVertical: 0, + paddingHorizontal: 0, + includeFontPadding: false, + fontWeight: bodyLarge.fontWeight, + }, + field: { + flexDirection: 'row', + minHeight: TEXT_INPUT_HEIGHT, + paddingVertical: TEXT_INPUT_PADDING_VERTICAL, + }, + addendum: { + flexDirection: 'row', + }, + supportingText: { + flex: 1, + marginTop: SUPPORTING_TEXT_MARGIN_TOP, + paddingHorizontal: TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL, + fontSize: SUPPORTING_TEXT_FONT_SIZE, + fontWeight: bodySmall.fontWeight, + textAlign: 'left', + }, + counter: { + marginTop: SUPPORTING_TEXT_MARGIN_TOP, + marginStart: 'auto', + paddingHorizontal: TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL, + fontSize: SUPPORTING_TEXT_FONT_SIZE, + fontWeight: bodySmall.fontWeight, + textAlign: 'right', + }, + trailingAccessory: { + width: ACCESSORY_SIZE, + height: ACCESSORY_SIZE, + alignSelf: 'center', + justifyContent: 'center', + alignItems: 'center', + marginEnd: TEXT_INPUT_ACCESSORY_MARGIN_HORIZONTAL, + }, + leadingAccessory: { + width: ACCESSORY_SIZE, + height: ACCESSORY_SIZE, + alignSelf: 'center', + justifyContent: 'center', + alignItems: 'center', + marginStart: TEXT_INPUT_ACCESSORY_MARGIN_HORIZONTAL, + }, + disabled: { + opacity: tokens.md.sys.state.opacity.disabled, + }, + iconWrapper: { + justifyContent: 'center', + alignItems: 'center', + }, + icon: { + margin: 0, + }, +}); + +export const filledStyles = StyleSheet.create({ + outline: { + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + pointerEvents: 'none', + }, + container: { + flex: 1, + flexDirection: 'row', + alignItems: 'flex-end', + paddingHorizontal: TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL, + }, + labelWrapper: { + position: 'absolute', + pointerEvents: 'none', + }, + disabledBackground: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + opacity: FILLED_DISABLED_CONTAINER_OPACITY, + pointerEvents: 'none', + }, +}); + +export const outlinedStyles = StyleSheet.create({ + outline: { + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + borderRadius: TEXT_INPUT_BORDER_RADIUS, + pointerEvents: 'none', + }, + container: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: TEXT_INPUT_INPUT_WRAPPER_PADDING_HORIZONTAL, + }, + labelWrapper: { + position: 'absolute', + paddingHorizontal: OUTLINED_LABEL_PADDING_HORIZONTAL, + pointerEvents: 'none', + }, +}); diff --git a/src/components/TextInput/types.tsx b/src/components/TextInput/types.tsx deleted file mode 100644 index ffb1f0bda0..0000000000 --- a/src/components/TextInput/types.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import * as React from 'react'; -import type { - TextInput as NativeTextInput, - Animated, - TextStyle, - LayoutChangeEvent, - ColorValue, - StyleProp, - ViewProps, - ViewStyle, - NativeSyntheticEvent, - TextLayoutEventData, -} from 'react-native'; - -import type { $Omit, InternalTheme, ThemeProp } from './../../types'; - -export type TextInputLabelProp = string | React.ReactElement; - -type TextInputProps = React.ComponentPropsWithRef & { - mode?: 'flat' | 'outlined'; - left?: React.ReactNode; - right?: React.ReactNode; - disabled?: boolean; - label?: TextInputLabelProp; - placeholder?: string; - error?: boolean; - onChangeText?: Function; - selectionColor?: ColorValue; - cursorColor?: ColorValue; - underlineColor?: ColorValue; - activeUnderlineColor?: ColorValue; - outlineColor?: ColorValue; - activeOutlineColor?: ColorValue; - textColor?: ColorValue; - dense?: boolean; - multiline?: boolean; - numberOfLines?: number; - onFocus?: (args: any) => void; - onBlur?: (args: any) => void; - render?: (props: RenderProps) => React.ReactNode; - value?: string; - style?: StyleProp; - theme?: ThemeProp; - testID?: string; - contentStyle?: StyleProp; - outlineStyle?: StyleProp; - underlineStyle?: StyleProp; - scaledLabel?: boolean; -}; - -export type RenderProps = { - ref: (a?: NativeTextInput | null) => void; - onChangeText?: (a: string) => void; - placeholder?: string; - placeholderTextColor?: ColorValue; - editable?: boolean; - selectionColor?: ColorValue; - cursorColor?: ColorValue; - onFocus?: (args: any) => void; - onBlur?: (args: any) => void; - underlineColorAndroid?: ColorValue; - onLayout?: (args: any) => void; - style: any; - multiline?: boolean; - numberOfLines?: number; - value?: string; - adjustsFontSizeToFit?: boolean; - testID?: string; -}; -type TextInputTypesWithoutMode = $Omit; -export type State = { - labeled: Animated.Value; - error: Animated.Value; - focused: boolean; - displayPlaceholder: boolean; - value?: string; - labelTextLayout: { width: number }; - labelLayout: { measured: boolean; width: number; height: number }; - leftLayout: { height: number | null; width: number | null }; - rightLayout: { height: number | null; width: number | null }; - inputContainerLayout: { width: number }; - contentStyle?: StyleProp; -}; -export type ChildTextInputProps = { - parentState: State; - innerRef: (ref?: NativeTextInput | null) => void; - onFocus?: (args: any) => void; - onBlur?: (args: any) => void; - forceFocus: () => void; - onChangeText?: (value: string) => void; - onInputLayout: (event: LayoutChangeEvent) => void; - onLayoutAnimatedText: (args: any) => void; - onLabelTextLayout: (event: NativeSyntheticEvent) => void; - onLeftAffixLayoutChange: (event: LayoutChangeEvent) => void; - onRightAffixLayoutChange: (event: LayoutChangeEvent) => void; -} & $Omit & { theme: InternalTheme }; - -export type LabelProps = { - mode?: 'flat' | 'outlined'; - placeholderStyle: any; - placeholderOpacity: - | number - | Animated.Value - | Animated.AnimatedInterpolation; - baseLabelTranslateX: number; - baseLabelTranslateY: number; - wiggleOffsetX: number; - labelScale: number; - fontSize: number; - lineHeight?: number | undefined; - fontWeight: TextStyle['fontWeight']; - font: any; - topPosition: number; - paddingLeft?: number; - paddingRight?: number; - labelTranslationXOffset?: number; - placeholderColor: ColorValue | null; - disabledOpacity?: number; - backgroundColor?: ColorValue; - label?: TextInputLabelProp | null; - hasActiveOutline?: boolean | null; - activeColor: ColorValue; - errorColor?: ColorValue; - labelError?: boolean | null; - onLayoutAnimatedText: (args: any) => void; - onLabelTextLayout: (event: NativeSyntheticEvent) => void; - roundness: number; - maxFontSizeMultiplier?: number | undefined | null; - testID?: string; - contentStyle?: StyleProp; - theme?: ThemeProp; -}; -export type InputLabelProps = { - labeled: Animated.Value; - error: Animated.Value; - focused: boolean; - wiggle: boolean; - opacity: number; - labelLayoutMeasured: boolean; - labelLayoutWidth: number; - labelLayoutHeight: number; - inputContainerLayout: { width: number }; - labelBackground?: any; - maxFontSizeMultiplier?: number | undefined | null; - scaledLabel?: boolean; -} & LabelProps; - -export type LabelBackgroundProps = { - labelStyle: any; - labeled: Animated.Value; - labelLayoutWidth: number; - labelLayoutHeight: number; - maxFontSizeMultiplier?: number | undefined | null; - theme?: ThemeProp; -} & LabelProps; diff --git a/src/components/TextInput/utils.ts b/src/components/TextInput/utils.ts new file mode 100644 index 0000000000..6ca77029e2 --- /dev/null +++ b/src/components/TextInput/utils.ts @@ -0,0 +1,642 @@ +import { + ColorValue, + Platform, + StyleProp, + TextStyle, + ViewStyle, +} from 'react-native'; + +import { AnimatedStyle } from 'react-native-reanimated'; + +import { + ACTIVE_INDICATOR_SIZE, + FILLED_ACTIVE_LABEL_TOP_POSITION, + FILLED_INACTIVE_LABEL_TOP_POSITION, + FILLED_LABEL_START_OFFSET_WITH_ACCESSORY, + FILLED_MULTILINE_PADDING_TOP, + INACTIVE_INDICATOR_SIZE, + INPUT_FONT_SIZE, + LABEL_START_OFFSET_WITHOUT_ACCESSORY, + OUTLINED_ACTIVE_LABEL_TOP_POSITION, + OUTLINED_DISABLED_OUTLINE_OPACITY, + OUTLINED_INACTIVE_LABEL_TOP_POSITION, + OUTLINED_LABEL_START_OFFSET_WITH_ACCESSORY, + OUTLINED_LABEL_TRANSLATE_DISTANCE_WITHOUT_ACCESSORY, + OUTLINED_LABEL_TRANSLATE_DISTANCE_WITH_ACCESSORY, + OUTLINED_MULTILINE_PADDING_TOP, + PREFIX_END_PADDING, + SUFFIX_START_PADDING, + TEXT_INPUT_BORDER_RADIUS, + FILLED_PADDING_BOTTOM, +} from './constants'; +import { filledStyles, outlinedStyles, styles } from './styles'; +import type { + FilledTextInputHookData, + OutlinedTextInputHookData, + TextInputProps, + TextInputSharedApi, + TextInputVariant, + SharedTextInputStyleData, + GetAccessibilityDataProps, + GetAccessibilityDataReturn, +} from './TextInput'; +import type { InternalTheme } from '../../types'; + +export const getTextInputAnimationLayout = ({ + variant, + hasAccessory, + isRTL, +}: { + variant: TextInputVariant; + hasAccessory: boolean; + isRTL: boolean; +}) => { + const activeTop = + variant === 'filled' + ? FILLED_ACTIVE_LABEL_TOP_POSITION + : OUTLINED_ACTIVE_LABEL_TOP_POSITION; + + const inactiveTop = + variant === 'filled' + ? FILLED_INACTIVE_LABEL_TOP_POSITION + : OUTLINED_INACTIVE_LABEL_TOP_POSITION; + + const distance = hasAccessory + ? OUTLINED_LABEL_TRANSLATE_DISTANCE_WITH_ACCESSORY + : OUTLINED_LABEL_TRANSLATE_DISTANCE_WITHOUT_ACCESSORY; + + const translateXEnd = (isRTL ? 1 : -1) * distance; + + return { activeTop, inactiveTop, translateXEnd }; +}; + +export const getAccentColors = ({ + theme, + hasError, +}: { + theme: InternalTheme; + hasError: boolean; +}) => { + const color = hasError ? theme.colors.error : theme.colors.primary; + + return { + selectionColor: color, + cursorColor: color, + }; +}; + +export const getLabelColor = ({ + theme, + hasError, + isFocused, + isDisabled, +}: { + theme: InternalTheme; + isFocused: boolean; + hasError: boolean; + isDisabled: boolean; +}) => { + const { + colors: { error, primary, onSurface, onSurfaceVariant }, + } = theme; + + if (hasError) { + return error; + } + if (isDisabled) { + return onSurface; + } + if (isFocused) { + return primary; + } + return onSurfaceVariant; +}; + +export const getSupportingTextColor = ({ + theme, + hasError, + isDisabled, +}: { + theme: InternalTheme; + hasError: boolean; + isDisabled: boolean; +}) => { + const { + colors: { error, onSurface, onSurfaceVariant }, + } = theme; + + if (hasError) { + return error; + } + if (isDisabled) { + return onSurface; + } + return onSurfaceVariant; +}; + +/** + * Returns the solid background color for the filled field container, or + * `undefined` when disabled. The disabled tint is rendered + * as a separate overlay View whose alpha is applied via the `opacity` style; + * keeping the alpha out of the color string is what makes the component safe + * to use with `PlatformColor` values on Android. + */ +export const getFieldBackgroundColor = ({ + theme, + isDisabled, +}: { + theme: InternalTheme; + isDisabled: boolean; +}): ColorValue | undefined => { + if (isDisabled) { + return undefined; + } + + return theme.colors.surfaceContainerHighest; +}; + +export const getIconColor = ({ + theme, + iconColor, + hasError, + isDisabled, +}: { + theme: InternalTheme; + iconColor?: ColorValue; + hasError: boolean; + isDisabled: boolean; +}): ColorValue => { + if (iconColor) return iconColor; + if (hasError) return theme.colors.error; + if (isDisabled) return theme.colors.onSurface; + return theme.colors.onSurfaceVariant; +}; + +/** + * Returns the raw outline color for a filled field. The disabled state's + * alpha is intentionally NOT baked in here — it is applied via the `opacity` + * style on the (childless) outline View so the value can be a `PlatformColor` + * on Android, which the `color` library cannot parse at runtime. + */ +export const getOutlineColor = ({ + theme, + hasError, + isFocused, + isDisabled, +}: { + theme: InternalTheme; + isFocused: boolean; + hasError: boolean; + isDisabled: boolean; +}) => { + const { + colors: { error, onSurface, primary, outline }, + } = theme; + + if (hasError) { + return error; + } + if (isDisabled) { + return onSurface; + } + if (isFocused) { + return primary; + } + + return outline; +}; + +/** + * Computes the style arrays that are identical across the filled and outlined + * variants. Each variant logic function calls this and then only computes its + * own variant-specific styles on top. + * + * Returns `isRTL` as well so callers can use it when building `inputStyles`, + * which is variant-specific (filled adds `MULTILINE_PADDING_TOP`). + */ +export const getSharedTextInputStyleData = ( + api: TextInputSharedApi +): SharedTextInputStyleData => { + const { + theme, + isDisabled, + hasError, + isFocused, + isRTL, + animatedLabelTextStyle, + } = api; + + const labelColor = getLabelColor({ theme, hasError, isFocused, isDisabled }); + + const supportingTextColor = getSupportingTextColor({ + theme, + hasError, + isDisabled, + }); + const { + colors: { onSurfaceVariant }, + } = theme; + + const animatedLabelTextStyles: StyleProp< + AnimatedStyle> + > = [ + styles.input, + { color: labelColor }, + animatedLabelTextStyle, + isDisabled && styles.disabled, + ]; + + const supportingTextStyles: StyleProp = [ + styles.supportingText, + { + color: supportingTextColor, + writingDirection: isRTL ? 'rtl' : 'ltr', + }, + isDisabled && styles.disabled, + ]; + + const counterStyles: StyleProp = [ + styles.counter, + { + color: supportingTextColor, + writingDirection: isRTL ? 'rtl' : 'ltr', + }, + isDisabled && styles.disabled, + ]; + + const prefixStyles: StyleProp = [ + styles.input, + { + fontSize: INPUT_FONT_SIZE, + color: onSurfaceVariant, + paddingEnd: PREFIX_END_PADDING, + }, + isDisabled && styles.disabled, + ]; + + const suffixStyles: StyleProp = [ + styles.input, + { + fontSize: INPUT_FONT_SIZE, + color: onSurfaceVariant, + paddingStart: SUFFIX_START_PADDING, + }, + isDisabled && styles.disabled, + ]; + + const leadingAccessoryStyles: StyleProp = [ + styles.leadingAccessory, + isDisabled && styles.disabled, + ]; + + const trailingAccessoryStyles: StyleProp = [ + styles.trailingAccessory, + isDisabled && styles.disabled, + ]; + + return { + isRTL, + animatedLabelTextStyles, + supportingTextStyles, + counterStyles, + prefixStyles, + suffixStyles, + leadingAccessoryStyles, + trailingAccessoryStyles, + }; +}; + +export const getFilledTextInputData = ( + api: TextInputSharedApi, + props: TextInputProps +): FilledTextInputHookData => { + const { style: inputStyleOverride, ...textInputProps } = props; + + const { + input, + theme, + hasSuffix, + isDisabled, + hasAccessory, + hasError, + animatedLabelWrapperStyle, + animatedActiveOutlineStyle, + } = api; + + /** + * Theme tokens + */ + const { + colors: { onSurface }, + } = theme; + + const outlineColor = getOutlineColor({ + theme, + hasError, + isFocused: false, + isDisabled, + }); + + const activeOutlineColor = getOutlineColor({ + theme, + hasError, + isFocused: true, + isDisabled, + }); + + const fieldBackgroundColor = getFieldBackgroundColor({ theme, isDisabled }); + + /** + * Shared styles + */ + + const shared = getSharedTextInputStyleData(api); + + /** + * Variant-specific styles + */ + + const animatedLabelWrapperStyles: StyleProp< + AnimatedStyle> + > = [ + filledStyles.labelWrapper, + { + left: hasAccessory + ? FILLED_LABEL_START_OFFSET_WITH_ACCESSORY + : LABEL_START_OFFSET_WITHOUT_ACCESSORY, + }, + animatedLabelWrapperStyle, + ]; + + const containerStyles: StyleProp = [ + filledStyles.container, + isDisabled && styles.disabled, + ]; + + const fieldStyles: StyleProp = [ + styles.field, + { + paddingBottom: FILLED_PADDING_BOTTOM, + backgroundColor: fieldBackgroundColor, + borderTopStartRadius: TEXT_INPUT_BORDER_RADIUS, + borderTopEndRadius: TEXT_INPUT_BORDER_RADIUS, + overflow: 'hidden', + }, + ]; + + /* Disabled tint (DISABLED_CONTAINER_OPACITY) is rendered as a childless overlay so its + alpha can be applied via the `opacity` style without leaking onto the label + and input. The View accepts `PlatformColor` directly. */ + const disabledBackgroundStyles: StyleProp | undefined = isDisabled + ? [ + filledStyles.disabledBackground, + { + backgroundColor: onSurface, + }, + ] + : undefined; + + const outlineStyles: StyleProp = [ + filledStyles.outline, + { + height: INACTIVE_INDICATOR_SIZE, + backgroundColor: outlineColor, + }, + isDisabled && styles.disabled, + ]; + + const animatedActiveOutlineStyles: StyleProp< + AnimatedStyle> + > = [ + filledStyles.outline, + { + height: ACTIVE_INDICATOR_SIZE, + backgroundColor: activeOutlineColor, + }, + isDisabled && styles.disabled, + animatedActiveOutlineStyle, + ]; + + const inputStyles: StyleProp = [ + styles.input, + { + flex: 1, + color: onSurface, + fontSize: INPUT_FONT_SIZE, + textAlign: hasSuffix === shared.isRTL ? 'left' : 'right', + writingDirection: shared.isRTL ? 'rtl' : 'ltr', + }, + textInputProps.multiline && { + height: 'auto', + paddingTop: FILLED_MULTILINE_PADDING_TOP, + }, + //@ts-expect-error - RN’s defs are narrower than CSS and RNW does not ship TS extensions that fix that + Platform.OS === 'web' && { + outlineStyle: 'none' as const, + }, + isDisabled && styles.disabled, + inputStyleOverride, + ]; + + return { + input, + isDisabled, + hasError, + hasSuffix, + animatedLabelWrapperStyles, + containerStyles, + fieldStyles, + disabledBackgroundStyles, + outlineStyles, + animatedActiveOutlineStyles, + inputStyles, + ...shared, + }; +}; + +export const getOutlinedTextInputData = ( + api: TextInputSharedApi, + props: TextInputProps +): OutlinedTextInputHookData => { + const { style: inputStyleOverride, ...textInputProps } = props; + + const { + input, + theme, + isFocused, + isDisabled, + hasAccessory, + hasError, + hasSuffix, + animatedLabelWrapperStyle, + } = api; + + /** + * Theme tokens + */ + + const { + colors: { background: labelBackgroundColor, onSurface }, + } = theme; + + const outlineColor = getOutlineColor({ + theme, + isDisabled, + isFocused, + hasError, + }); + + /** + * Shared styles + */ + + const shared = getSharedTextInputStyleData(api); + + /** + * Variant-specific styles + */ + + const containerStyles: StyleProp = [ + outlinedStyles.container, + isDisabled && styles.disabled, + ]; + + const fieldStyles: StyleProp = [ + styles.field, + { + borderRadius: TEXT_INPUT_BORDER_RADIUS, + }, + textInputProps.multiline && { alignItems: 'flex-start' }, + ]; + + /* The outline is a childless absolutely-positioned View, so applying + `opacity` here is safe and lets us pass `outlineColor` through unchanged + (including PlatformColor values on Android). */ + const outlineStyles: StyleProp = [ + outlinedStyles.outline, + { + borderWidth: isFocused ? 2 : 1, + borderColor: outlineColor, + }, + isDisabled && { opacity: OUTLINED_DISABLED_OUTLINE_OPACITY }, + ]; + + const animatedLabelWrapperStyles: StyleProp< + AnimatedStyle> + > = [ + outlinedStyles.labelWrapper, + { + left: hasAccessory + ? OUTLINED_LABEL_START_OFFSET_WITH_ACCESSORY + : LABEL_START_OFFSET_WITHOUT_ACCESSORY, + backgroundColor: labelBackgroundColor, + }, + animatedLabelWrapperStyle, + ]; + + const inputStyles: StyleProp = [ + styles.input, + { + flex: 1, + color: onSurface, + fontSize: INPUT_FONT_SIZE, + textAlign: hasSuffix === shared.isRTL ? 'left' : 'right', + writingDirection: shared.isRTL ? 'rtl' : 'ltr', + }, + textInputProps.multiline && { + height: 'auto', + textAlignVertical: 'top', + paddingTop: OUTLINED_MULTILINE_PADDING_TOP, + }, + //@ts-expect-error - RN’s defs are narrower than CSS and RNW does not ship TS extensions that fix that + Platform.OS === 'web' && { + outlineStyle: 'none' as const, + }, + isDisabled && styles.disabled, + inputStyleOverride, + ]; + + return { + input, + isDisabled, + hasError, + hasSuffix, + animatedLabelWrapperStyles, + containerStyles, + fieldStyles, + disabledBackgroundStyles: undefined, + outlineStyles, + inputStyles, + ...shared, + }; +}; + +export const getAccessibilityData = ({ + data, + hasError, + hasCounter, + isDisabled, + inputLength, +}: GetAccessibilityDataProps): GetAccessibilityDataReturn => { + const { label, supportingText, ...props } = data; + + const maxLength = props.maxLength; + const shouldEvaluateCounter = !!maxLength && hasCounter; + const isEmptyString = inputLength === 0; + const isCounterExceeded = shouldEvaluateCounter && inputLength > maxLength; + const isCounterReached = shouldEvaluateCounter && inputLength === maxLength; + const isInvalid = hasError || isCounterExceeded; + const isSupportingTextHidden = !!(supportingText && !hasError); + + const chunks: string[] = []; + + if (label) { + chunks.push(label); + } + + if (isSupportingTextHidden) { + chunks.push(supportingText); + } + + if (isEmptyString && props.placeholder && !hasError) { + chunks.push(props.placeholder); + } + + const ariaLabel = chunks.length > 0 ? chunks.join(', ') : label; + + let hint: string | undefined; + + if (isCounterExceeded && !(hasError && supportingText)) { + hint = `Character limit exceeded ${inputLength} of ${maxLength}`; + } + + const counterAccessibilityLabel = shouldEvaluateCounter + ? isCounterExceeded + ? `Character limit exceeded ${inputLength} of ${maxLength}` + : `Characters entered ${inputLength} of ${maxLength}` + : undefined; + + const accessibilityState = { + disabled: isDisabled, + invalid: isInvalid, + ...props.accessibilityState, + } as const; + + return { + input: { + 'aria-label': ariaLabel, + 'aria-valuemax': isCounterReached ? maxLength : undefined, + 'aria-valuenow': isCounterReached ? inputLength : undefined, + accessibilityHint: hint, + accessibilityState, + }, + supportingText: { + 'aria-hidden': isSupportingTextHidden, + 'aria-live': hasError && supportingText ? 'polite' : undefined, + }, + counter: { + 'aria-label': counterAccessibilityLabel, + 'aria-live': 'polite', + }, + }; +}; diff --git a/src/components/__tests__/HelperText.test.tsx b/src/components/__tests__/HelperText.test.tsx deleted file mode 100644 index 22ffc025b9..0000000000 --- a/src/components/__tests__/HelperText.test.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; - -import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; -import HelperText from '../HelperText/HelperText'; - -describe('HelperText', () => { - it('should have correct text color for info type', () => { - const { getByTestId } = render( - - Info: Maximum length is 100 characters - - ); - - expect(getByTestId('helper-text')).toHaveStyle({ - color: getTheme().colors.onSurfaceVariant, - }); - }); - - it('should have correct text color for error type', () => { - const { getByTestId } = render( - - Error: Only letters are allowed - - ); - - expect(getByTestId('helper-text')).toHaveStyle({ - color: getTheme().colors.error, - }); - }); -}); diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index 97f77833aa..c1df23b5fa 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -1,990 +1,1287 @@ -/* eslint-disable react-native/no-inline-styles */ import * as React from 'react'; -import { Platform, StyleSheet, Text, View } from 'react-native'; - -import { fireEvent } from '@testing-library/react-native'; +import { + I18nManager, + StyleSheet, + TextInput as NativeTextInput, + View, +} from 'react-native'; -import PaperProvider from '../../core/PaperProvider'; -import { getTheme, ThemeProvider } from '../../core/theming'; -import { render } from '../../test-utils'; -import { red500 } from '../../theme/colors'; -import { LightTheme } from '../../theme/schemes'; +import { act, fireEvent, render } from '../../test-utils'; import { tokens } from '../../theme/tokens'; -import { - getFlatInputColors, - getOutlinedInputColors, -} from '../TextInput/helpers'; -import TextInput, { Props } from '../TextInput/TextInput'; +import TextInput from '../TextInput'; +import type { + TextInputRenderProps, + TextInputHandles, +} from '../TextInput/TextInput'; +import type { TextInputAccessoryProps } from '../TextInput/TextInputIcon'; const stateOpacity = tokens.md.sys.state.opacity; -const style = StyleSheet.create({ - inputStyle: { - color: red500, - }, - centered: { - textAlign: 'center', - }, - height: { - height: 100, - }, - lineHeight: { - lineHeight: 22, - }, - contentStyle: { - paddingLeft: 20, - }, -}); - -// Revert changes to Platform.OS automatically -const defaultPlatform = Platform.OS; -beforeEach(() => { - Platform.OS = defaultPlatform; -}); - -const affixTextValue = '/100'; -it('correctly renders left-side icon adornment, and right-side affix adornment', () => { - const { getByText, getByTestId, toJSON } = render( - { - console.log('!@# press left'); - }} - /> - } - right={ - - } +const defaultI18nIsRTL = I18nManager.isRTL; + +const getConstantsOriginal = I18nManager.getConstants.bind(I18nManager); + +beforeAll(() => { + jest.spyOn(I18nManager, 'getConstants').mockImplementation(() => ({ + ...getConstantsOriginal(), + isRTL: I18nManager.isRTL, + })); +}); + +afterAll(() => { + jest.restoreAllMocks(); +}); + +afterEach(() => { + I18nManager.isRTL = defaultI18nIsRTL; +}); + +function firstIndexOfTestIdInTree(tree: unknown, testID: string): number { + const serialized = JSON.stringify(tree); + const match = new RegExp(`"testID":\\s*"${testID}"`).exec(serialized); + return match ? match.index : -1; +} + +/** Locates a Text node whose children are serialized as a one-element JSON string array. */ +function firstIndexOfTextChildArrayInTree(tree: unknown, text: string): number { + return JSON.stringify(tree).indexOf(JSON.stringify([text])); +} + +it('renders filled TextInput with label and value', () => { + const tree = render( + {}} /> + ).toJSON(); + + expect(tree).toMatchSnapshot(); +}); + +it('renders outlined TextInput with label and value', () => { + const tree = render( + {}} /> - ); - expect(getByText(affixTextValue)).toBeTruthy(); - expect(getByTestId('left-icon-adornment')).toBeTruthy(); - expect(getByTestId('right-affix-adornment')).toBeTruthy(); - expect(toJSON()).toMatchSnapshot(); + ).toJSON(); + + expect(tree).toMatchSnapshot(); +}); + +it('renders filled TextInput with TextInput.Icon accessories', () => { + const tree = render( + {}} + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + + )} + /> + ).toJSON(); + + expect(tree).toMatchSnapshot(); +}); + +it('renders outlined TextInput with TextInput.Icon accessories', () => { + const tree = render( + {}} + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + + )} + /> + ).toJSON(); + + expect(tree).toMatchSnapshot(); }); -it('correctly renders left-side affix adornment, and right-side icon adornment', () => { - const { getByText, getByTestId, toJSON } = render( +it('renders filled TextInput with TextInput.Icon accessories when error is true', () => { + const tree = render( - } - right={ + label="Search" + value="q" + onChangeText={() => {}} + error + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} + /> + ).toJSON(); + + expect(tree).toMatchSnapshot(); +}); + +it('renders outlined TextInput with TextInput.Icon accessories when error is true', () => { + const tree = render( + {}} + error + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} + /> + ).toJSON(); + + expect(tree).toMatchSnapshot(); +}); + +it('fires onPress on TextInput.Icon end accessory', () => { + const onClear = jest.fn(); + const { getAllByTestId } = render( + {}} + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( { - console.log('!@# press left'); - }} + {...props} + icon="close" + onPress={onClear} + accessibilityLabel="Clear" /> - } + )} /> ); - expect(getByText(affixTextValue)).toBeTruthy(); - expect(getByTestId('right-icon-adornment')).toBeTruthy(); - expect(getByTestId('left-affix-adornment')).toBeTruthy(); - expect(toJSON()).toMatchSnapshot(); + + fireEvent.press(getAllByTestId('icon-button')[1]); + + expect(onClear).toHaveBeenCalledTimes(1); }); -it('correctly applies default textAlign based on default RTL', () => { - const { toJSON } = render( +it('disables TextInput.Icon when the field is disabled', () => { + const { getAllByTestId } = render( {}} + disabled + startAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} + endAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} /> ); - expect(toJSON()).toMatchSnapshot(); + const buttons = getAllByTestId('icon-button'); + expect(buttons[0].props.accessibilityState?.disabled).toBe(true); + expect(buttons[1].props.accessibilityState?.disabled).toBe(true); }); -it('correctly applies textAlign center', () => { - const { toJSON } = render( +it('does not disable TextInput.Icon when the field is read-only (editable false)', () => { + const { getAllByTestId } = render( {}} + editable={false} + startAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} + endAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} /> ); - expect(toJSON()).toMatchSnapshot(); + const buttons = getAllByTestId('icon-button'); + expect(buttons[0].props.accessibilityState?.disabled).not.toBe(true); + expect(buttons[1].props.accessibilityState?.disabled).not.toBe(true); }); -it('correctly applies cursorColor prop', () => { - const { toJSON } = render( +it('renders supporting text below the field', () => { + const { getByText } = render( {}} + supportingText="Use a valid address" /> ); - expect(toJSON()).toMatchSnapshot(); + expect(getByText('Use a valid address')).toBeTruthy(); }); -it('correctly applies height to multiline Outline TextInput', () => { - const { toJSON } = render( +it('uses polite aria-live on error supporting text', () => { + const { getByText, getByTestId } = render( {}} + supportingText="Invalid" + error + testID="tf-input" /> ); - expect(toJSON()).toMatchSnapshot(); + expect(getByText('Invalid').props['aria-live']).toBe('polite'); + expect(getByTestId('tf-input').props.accessibilityState?.invalid).toBe(true); }); -it('correctly applies error state Outline TextInput', () => { +it('marks the input invalid when error is true without supporting text', () => { const { getByTestId } = render( {}} error + testID="tf-input" + /> + ); + + expect(getByTestId('tf-input').props.accessibilityState?.invalid).toBe(true); + expect(getByTestId('tf-input').props.accessibilityHint).toBeUndefined(); +}); + +it('hides helper supporting text from the accessibility tree and omits aria-live', () => { + const { getByText, getByTestId } = render( + {}} + supportingText="Optional" + testID="tf-input" /> ); - const outline = getByTestId('text-input-outline'); - expect(outline).toHaveStyle({ borderWidth: 2 }); + expect(getByText('Optional').props['aria-hidden']).toBe(true); + expect(getByText('Optional').props['aria-live']).toBeUndefined(); + expect(getByTestId('tf-input').props['aria-label']).toBe('Email, Optional'); }); -it('correctly applies focused state Outline TextInput', () => { +it('includes supporting text in aria-label when label is omitted', () => { const { getByTestId } = render( {}} + supportingText="Helper only" + testID="tf-input" /> ); - const outline = getByTestId('text-input-outline'); - expect(outline).toHaveStyle({ borderWidth: 2 }); + expect(getByTestId('tf-input').props['aria-label']).toBe('Helper only'); +}); - fireEvent(getByTestId('text-input-outlined'), 'focus'); +it('does not mark the input as aria-disabled when editable is false (read-only)', () => { + const { getByTestId } = render( + {}} + editable={false} + testID="tf-input" + /> + ); - expect(outline).toHaveStyle({ borderWidth: 2 }); + expect(getByTestId('tf-input').props.accessibilityState?.disabled).not.toBe( + true + ); }); -it('contains patch spacing for flat input when ios, multiline and disabled', () => { - Platform.OS = 'ios'; +it('marks the input as disabled in accessibilityState when disabled is true', () => { const { getByTestId } = render( {}} + disabled + testID="tf-input" /> ); - expect(getByTestId('patch-container')).toBeTruthy(); + + expect(getByTestId('tf-input').props.accessibilityState?.disabled).toBe(true); }); -it('correctly applies a component as the text label', () => { - const { toJSON } = render( +it('renders the input via render with merged props', () => { + const renderInput = jest.fn((props: TextInputRenderProps) => ( + + )); + + const { getByTestId } = render( Flat input} - placeholder="Type something" - value={'Some test value'} + label="Pin" + value="12" + onChangeText={() => {}} + render={renderInput} /> ); - expect(toJSON()).toMatchSnapshot(); + expect(getByTestId('custom-input')).toBeTruthy(); + expect(renderInput).toHaveBeenCalled(); + const merged = renderInput.mock.calls[0]?.[0] as TextInputRenderProps; + expect(merged['aria-label']).toBe('Pin'); + expect(merged.value).toBe('12'); }); -it('correctly applies paddingLeft from contentStyleProp', () => { - const { toJSON } = render( +it('does not apply disabled opacity to the TextInput when editable is false (filled)', () => { + const { getByTestId } = render( {}} + editable={false} + testID="tf-input-ro" /> ); - expect(toJSON()).toMatchSnapshot(); + expect( + StyleSheet.flatten(getByTestId('tf-input-ro').props.style) + ).not.toMatchObject({ opacity: stateOpacity.disabled }); }); -it('renders label with correct color when active', () => { +it('does not apply disabled opacity to the TextInput when editable is false (outlined)', () => { const { getByTestId } = render( {}} + editable={false} + testID="tf-input-ro-out" /> ); - fireEvent(getByTestId('text-input-flat'), 'focus'); + expect( + StyleSheet.flatten(getByTestId('tf-input-ro-out').props.style) + ).not.toMatchObject({ opacity: stateOpacity.disabled }); +}); - expect(getByTestId('text-input-flat-label-active')).toHaveStyle({ - color: getTheme().colors.primary, - }); +it('applies disabled opacity to the TextInput when disabled is true (filled)', () => { + const { getByTestId } = render( + {}} + disabled + testID="tf-input-dis" + /> + ); + + expect( + StyleSheet.flatten(getByTestId('tf-input-dis').props.style) + ).toMatchObject({ opacity: stateOpacity.disabled }); }); -it('renders label with correct color when inactive', () => { +it('applies disabled opacity to the TextInput when disabled is true (outlined)', () => { const { getByTestId } = render( {}} + disabled + testID="tf-input-dis-out" /> ); - expect(getByTestId('text-input-label-inactive')).toHaveStyle({ - color: getTheme().colors.onSurfaceVariant, - }); + expect( + StyleSheet.flatten(getByTestId('tf-input-dis-out').props.style) + ).toMatchObject({ opacity: stateOpacity.disabled }); }); -it('renders input placeholder initially with transparent placeholderTextColor', () => { +it('forwards TextInput props such as testID', () => { const { getByTestId } = render( - + {}} + testID="email-input" + /> + ); + + expect(getByTestId('email-input')).toBeTruthy(); +}); + +/* TextInput peels these before spreading onto TextInput (see TextInput.tsx). + * Custom layout / sub-component styling props are intentionally not supported. */ +it('does not pass TextInput-only props through to TextInput', () => { + const { getByTestId } = render( + {}} + error + disabled + testID="tf-native" + /> + ); + + const input = getByTestId('tf-native'); + expect(input.props.variant).toBeUndefined(); + expect(input.props.theme).toBeUndefined(); + expect(input.props.startAccessory).toBeUndefined(); + expect(input.props.endAccessory).toBeUndefined(); + expect(input.props.label).toBeUndefined(); + expect(input.props.supportingText).toBeUndefined(); + expect(input.props.prefix).toBeUndefined(); + expect(input.props.suffix).toBeUndefined(); + expect(input.props.counter).toBeUndefined(); + expect(input.props.error).toBeUndefined(); + expect(input.props.disabled).toBeUndefined(); +}); + +it('shows a character counter when counter is true and maxLength is set (filled)', () => { + const { getByText, queryByText } = render( + {}} + counter + maxLength={100} + /> + ); + + expect(getByText('5/100')).toBeTruthy(); + expect(queryByText('0/100')).toBeNull(); +}); + +it('shows a character counter when counter is true and maxLength is set (outlined)', () => { + const { getByText } = render( + {}} + counter + maxLength={50} + /> ); - expect(getByTestId('text-input').props.placeholderTextColor).toBe( - 'transparent' + expect(getByText('0/50')).toBeTruthy(); +}); + +it('updates the character counter when the value changes', () => { + const { getByText, rerender } = render( + {}} + counter + maxLength={10} + /> + ); + + expect(getByText('1/10')).toBeTruthy(); + + rerender( + {}} + counter + maxLength={10} + /> + ); + + expect(getByText('4/10')).toBeTruthy(); +}); + +it('does not show a character counter when counter is false', () => { + const { queryByText } = render( + {}} + maxLength={100} + /> ); + + expect(queryByText('5/100')).toBeNull(); }); -it('correctly applies padding offset to input label on Android when RTL', () => { - Platform.OS = 'android'; +it('does not show a character counter when maxLength is missing', () => { + const { queryByText } = render( + {}} counter /> + ); + + expect(queryByText('5/100')).toBeNull(); + expect(queryByText(/\//)).toBeNull(); +}); +it('invokes onFocus and onBlur on the TextInput', () => { + const onFocus = jest.fn(); + const onBlur = jest.fn(); const { getByTestId } = render( - - - } - right={ - - } - /> - - ); - - expect(getByTestId('text-input-flat-label-active')).toHaveStyle({ - paddingLeft: 56, - paddingRight: 16, - }); + {}} + onFocus={onFocus} + onBlur={onBlur} + testID="tf-input" + /> + ); + + const input = getByTestId('tf-input'); + fireEvent(input, 'focus'); + fireEvent(input, 'blur'); + + expect(onFocus).toHaveBeenCalledTimes(1); + expect(onBlur).toHaveBeenCalledTimes(1); +}); + +it('focuses the TextInput when the outer Pressable is pressed', () => { + const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); + + const { UNSAFE_getByProps, getByTestId } = render( + {}} + testID="tf-input" + /> + ); + + expect(getByTestId('tf-input')).toBeTruthy(); + + /* Pressable is not exposed as a distinct type in the test renderer; match its props. */ + const pressable = UNSAFE_getByProps({ role: 'none', accessible: false }); + fireEvent.press(pressable); + + expect(focusSpy).toHaveBeenCalled(); + focusSpy.mockRestore(); +}); + +it('does not focus the TextInput when disabled and the Pressable is pressed', () => { + const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); + + const { UNSAFE_getByProps } = render( + {}} disabled /> + ); + + const pressable = UNSAFE_getByProps({ role: 'none', accessible: false }); + fireEvent.press(pressable); + + expect(focusSpy).not.toHaveBeenCalled(); + focusSpy.mockRestore(); +}); + +it('focuses the TextInput when read-only and the Pressable is pressed', () => { + const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); + + const { UNSAFE_getByProps } = render( + {}} + editable={false} + /> + ); + + const pressable = UNSAFE_getByProps({ role: 'none', accessible: false }); + fireEvent.press(pressable); + + expect(focusSpy).toHaveBeenCalled(); + focusSpy.mockRestore(); }); -it('correctly applies padding offset to input label on Android when LTR', () => { - Platform.OS = 'android'; +it('exposes the TextInput instance via ref prop', () => { + const ref = React.createRef(); + + render( + {}} + testID="tf-input" + /> + ); + + expect(ref.current).toBeTruthy(); + expect(typeof ref.current?.focus).toBe('function'); + expect(typeof ref.current?.clear).toBe('function'); + expect(typeof ref.current?.blur).toBe('function'); + expect(typeof ref.current?.isFocused).toBe('function'); + expect(typeof ref.current?.setNativeProps).toBe('function'); + expect(typeof ref.current?.setSelection).toBe('function'); +}); + +it('passes error, disabled, and multiline to accessories', () => { + const startAccessoryProps: TextInputAccessoryProps[] = []; + const endAccessoryProps: TextInputAccessoryProps[] = []; + + function StartAccessory(props: TextInputAccessoryProps) { + startAccessoryProps.push(props); + return ; + } + + function EndAccessory(props: TextInputAccessoryProps) { + endAccessoryProps.push(props); + return ; + } const { getByTestId } = render( - } - right={ - - } + label="Search" + value="" + onChangeText={() => {}} + multiline + error + disabled + startAccessory={StartAccessory} + endAccessory={EndAccessory} /> ); - expect(getByTestId('text-input-flat-label-active')).toHaveStyle({ - paddingLeft: 16, - paddingRight: 56, + expect(getByTestId('start-accessory')).toBeTruthy(); + expect(getByTestId('end-accessory')).toBeTruthy(); + expect(startAccessoryProps[0]).toMatchObject({ + error: true, + disabled: true, + multiline: true, + }); + expect(endAccessoryProps[0]).toMatchObject({ + error: true, + disabled: true, + multiline: true, }); }); -it('calls onLayout on right-side affix adornment', () => { - const onLayoutMock = jest.fn(); - const nativeEventMock = { - nativeEvent: { layout: { height: 100 } }, - }; +it('passes error to accessories when the field is disabled', () => { + const startAccessoryProps: TextInputAccessoryProps[] = []; + + function StartAccessory(props: TextInputAccessoryProps) { + startAccessoryProps.push(props); + return ; + } const { getByTestId } = render( } - /> - ); - fireEvent( - getByTestId('right-affix-adornment-text'), - 'onLayout', - nativeEventMock - ); - expect(onLayoutMock).toHaveBeenCalledWith(nativeEventMock); -}); - -(['outlined', 'flat'] as const).forEach((mode) => - it(`renders ${mode} input with correct line height`, () => { - const input = render( - - ); - - expect(input.getByTestId(`text-input-${mode}`)).toHaveStyle({ - lineHeight: 22, - }); - }) -); - -(['outlined', 'flat'] as const).forEach((mode) => - it(`renders ${mode} input with passed textColor`, () => { - const input = render( - - ); - - expect(input.getByTestId(`text-input-${mode}`)).toHaveStyle({ - color: 'purple', - }); - }) -); - -it("correctly applies theme background to label when input's background is transparent", () => { - const backgroundColor = 'transparent'; - const theme = { - ...LightTheme, - colors: { - ...LightTheme.colors, - background: 'pink', - }, - }; + label="Search" + value="" + onChangeText={() => {}} + error + disabled + startAccessory={StartAccessory} + /> + ); + + expect(getByTestId('start-acc-error-disabled')).toBeTruthy(); + expect(startAccessoryProps[0].error).toBe(true); + expect(startAccessoryProps[0].disabled).toBe(true); +}); + +it('renders supporting text as a Text child', () => { + const { getByText } = render( + {}} + supportingText="Hint" + /> + ); + + expect(getByText('Hint')).toBeTruthy(); +}); + +it('renders the counter as a Text child', () => { + const { getByText } = render( + {}} + counter + maxLength={80} + /> + ); + + expect(getByText('2/80')).toBeTruthy(); +}); + +it('renders supporting text and counter separately when both are shown', () => { + const { getByText } = render( + {}} + supportingText="Help text" + counter + maxLength={10} + /> + ); + + expect(getByText('Help text')).toBeTruthy(); + expect(getByText('1/10')).toBeTruthy(); +}); + +it('applies RTL text alignment and writing direction to the TextInput (filled)', () => { + I18nManager.isRTL = true; const { getByTestId } = render( - - - - ); - - expect(getByTestId('transparent-example-label-background')).toHaveStyle({ - backgroundColor: 'pink', - }); + {}} + testID="tf-input-rtl" + /> + ); + + expect(StyleSheet.flatten(getByTestId('tf-input-rtl').props.style)).toEqual( + expect.objectContaining({ + textAlign: 'right', + writingDirection: 'rtl', + }) + ); }); -it('always applies line height for web, even if not specified', () => { - Platform.OS = 'web'; +it('applies RTL text alignment and writing direction to the TextInput (outlined)', () => { + I18nManager.isRTL = true; + const { getByTestId } = render( - - - - - - - - - ); - - expect(getByTestId('default-font')).toHaveStyle({ lineHeight: 16 * 1.2 }); - expect(getByTestId('default-font-flat')).toHaveStyle({ - lineHeight: 16 * 1.2, - }); + {}} + testID="tf-input-rtl-outlined" + /> + ); + + expect( + StyleSheet.flatten(getByTestId('tf-input-rtl-outlined').props.style) + ).toEqual( + expect.objectContaining({ + textAlign: 'right', + writingDirection: 'rtl', + }) + ); +}); - expect(getByTestId('large-font')).toHaveStyle({ lineHeight: 30 * 1.2 }); - expect(getByTestId('large-font-flat')).toHaveStyle({ lineHeight: 30 * 1.2 }); +it('applies RTL writing direction to supporting text', () => { + I18nManager.isRTL = true; - expect(getByTestId('custom-line-height')).toHaveStyle({ - lineHeight: 29, - }); - expect(getByTestId('custom-line-height-flat')).toHaveStyle({ - lineHeight: 29, - }); + const { getByText } = render( + {}} + supportingText="Hint" + /> + ); + + expect(StyleSheet.flatten(getByText('Hint').props.style)).toEqual( + expect.objectContaining({ + writingDirection: 'rtl', + }) + ); }); -it('call onPress when affix adornment pressed', () => { - const affixOnPress = jest.fn(); - const affixTextValue = '+39'; - const { getByText, toJSON } = render( +it('places EndAccessory before StartAccessory in the tree when RTL', () => { + I18nManager.isRTL = true; + + function StartAccessory() { + return ; + } + + function EndAccessory() { + return ; + } + + const { toJSON } = render( } + label="Email" + value="" + onChangeText={() => {}} + startAccessory={StartAccessory} + endAccessory={EndAccessory} + testID="tf-input-rtl-order" /> ); - fireEvent.press(getByText(affixTextValue)); + const tree = toJSON(); + expect(firstIndexOfTestIdInTree(tree, 'rtl-acc-from-end-prop')).toBeLessThan( + firstIndexOfTestIdInTree(tree, 'rtl-acc-from-start-prop') + ); +}); + +it('places StartAccessory before EndAccessory in the tree when LTR', () => { + I18nManager.isRTL = false; + + function StartAccessory() { + return ; + } + + function EndAccessory() { + return ; + } - expect(getByText(affixTextValue)).toBeTruthy(); - expect(toJSON()).toMatchSnapshot(); - expect(affixOnPress).toHaveBeenCalledTimes(1); + const { toJSON } = render( + {}} + startAccessory={StartAccessory} + endAccessory={EndAccessory} + testID="tf-input-ltr-order" + /> + ); + + const tree = toJSON(); + expect( + firstIndexOfTestIdInTree(tree, 'ltr-acc-from-start-prop') + ).toBeLessThan(firstIndexOfTestIdInTree(tree, 'ltr-acc-from-end-prop')); }); -describe('maxFontSizeMultiplier', () => { - const createInput = ( - type: Exclude, - maxFontSizeMultiplier?: Props['maxFontSizeMultiplier'] - ) => { - return ( - - ); - }; +it('does not expose the placeholder string when the TextInput is not focused', () => { + const { getByTestId } = render( + {}} + placeholder="e.g. user@example.com" + testID="tf-input" + /> + ); - it('should have default value in flat input', () => { - const { getByTestId } = render(createInput('flat')); + /* Sentinel space avoids iOS multiline UITextView not updating placeholder from nil (react-native#31573). */ + expect(getByTestId('tf-input').props.placeholder).toBe(' '); +}); - expect(getByTestId('text-input-flat').props.maxFontSizeMultiplier).toBe( - 1.5 - ); - }); +it('shows placeholder when unfocused and no label is given', () => { + const { getByTestId } = render( + {}} + placeholder="Search" + testID="tf-input-no-label" + /> + ); - it('should have default value in outlined input', () => { - const { getByTestId } = render(createInput('outlined')); + expect(getByTestId('tf-input-no-label').props.placeholder).toBe('Search'); +}); - expect(getByTestId('text-input-outlined').props.maxFontSizeMultiplier).toBe( - 1.5 - ); - }); +it('shows placeholder when the TextInput is focused', () => { + const { getByTestId } = render( + {}} + placeholder="e.g. user@example.com" + testID="tf-input" + /> + ); - it('should have correct passed value in flat input', () => { - const { getByTestId } = render(createInput('flat', 2)); + fireEvent(getByTestId('tf-input'), 'focus'); - expect(getByTestId('text-input-flat').props.maxFontSizeMultiplier).toBe(2); - }); + expect(getByTestId('tf-input').props.placeholder).toBe( + 'e.g. user@example.com' + ); +}); - it('should have correct passed value in outlined input', () => { - const { getByTestId } = render(createInput('outlined', 2)); +it('shows placeholder on multiline TextInput when focused', () => { + const { getByTestId } = render( + {}} + placeholder="Add a note…" + multiline + testID="tf-multiline" + /> + ); - expect(getByTestId('text-input-outlined').props.maxFontSizeMultiplier).toBe( - 2 - ); - }); + expect(getByTestId('tf-multiline').props.placeholder).toBe(' '); - it('should have passed null value in flat input', () => { - const { getByTestId } = render(createInput('flat', null)); + fireEvent(getByTestId('tf-multiline'), 'focus'); - expect(getByTestId('text-input-flat').props.maxFontSizeMultiplier).toBe( - null - ); - }); + expect(getByTestId('tf-multiline').props.placeholder).toBe('Add a note…'); +}); - it('should have passed null value in outlined input', () => { - const { getByTestId } = render(createInput('outlined', null)); +it('does not expose the placeholder string again after the TextInput loses focus', () => { + const { getByTestId } = render( + {}} + placeholder="e.g. user@example.com" + testID="tf-input" + /> + ); - expect(getByTestId('text-input-outlined').props.maxFontSizeMultiplier).toBe( - null - ); - }); + fireEvent(getByTestId('tf-input'), 'focus'); + fireEvent(getByTestId('tf-input'), 'blur'); + + expect(getByTestId('tf-input').props.placeholder).toBe(' '); }); -describe('getFlatInputColor - underline color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - underlineColorCustom: getTheme().colors.onSurfaceVariant, - }); - }); +it('maps a lone StartAccessory to leading in LTR and trailing in RTL (tree order)', () => { + function LoneStartAccessory() { + return ; + } - it('should return correct theme color, for theme version 3', () => { - expect( - getFlatInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - underlineColorCustom: getTheme().colors.onSurfaceVariant, - }); - }); + I18nManager.isRTL = false; - it('should return custom color, no matter what the theme is', () => { - expect( - getFlatInputColors({ - underlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - underlineColorCustom: 'beige', - }); - - expect( - getFlatInputColors({ - underlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - underlineColorCustom: 'beige', - }); - }); + const { toJSON: toJsonLtr } = render( + {}} + startAccessory={LoneStartAccessory} + testID="tf-lone-ltr" + /> + ); + + I18nManager.isRTL = true; + + const { toJSON: toJsonRtl } = render( + {}} + startAccessory={LoneStartAccessory} + testID="tf-lone-rtl" + /> + ); + + const ltrTree = toJsonLtr(); + expect(firstIndexOfTestIdInTree(ltrTree, 'lone-start-acc')).toBeLessThan( + firstIndexOfTestIdInTree(ltrTree, 'tf-lone-ltr') + ); + + const rtlTree = toJsonRtl(); + expect(firstIndexOfTestIdInTree(rtlTree, 'tf-lone-rtl')).toBeLessThan( + firstIndexOfTestIdInTree(rtlTree, 'lone-start-acc') + ); }); -describe('getFlatInputColor - input text color', () => { - it('should return custom color, if not disabled, no matter what the theme is', () => { - expect( - getOutlinedInputColors({ - textColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - inputTextColor: 'beige', - }); - - expect( - getOutlinedInputColors({ - textColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - inputTextColor: 'beige', - }); - }); +it('shows prefix and suffix when the field is floating and hides them after value is cleared while blurred', () => { + const { getByTestId, getByText, queryByText, rerender } = render( + {}} + prefix="$" + suffix="/100" + testID="tf-ps" + /> + ); - it('should return correct disabled color, for theme version 3', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - inputTextColor: getTheme().colors.onSurface, - disabledOpacity: stateOpacity.disabled, - }); - }); + expect(getByText('$')).toBeTruthy(); + expect(getByText('/100')).toBeTruthy(); - it('should return correct theme color, for theme version 3', () => { - expect( - getFlatInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - inputTextColor: getTheme().colors.onSurface, - }); - }); + rerender( + {}} + prefix="$" + suffix="/100" + testID="tf-ps" + /> + ); + + expect(queryByText('$')).toBeNull(); + expect(queryByText('/100')).toBeNull(); + expect(getByTestId('tf-ps')).toBeTruthy(); }); -describe('getFlatInputColor - placeholder color', () => { - it('should return correct disabled color', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - placeholderColor: getTheme().colors.onSurfaceVariant, - disabledOpacity: stateOpacity.disabled, - }); - }); +it('renders prefix and suffix while focused even when value is empty', () => { + const { getByTestId, getByText, queryByText } = render( + {}} + prefix="$" + suffix=" kg" + testID="tf-ps-focus" + /> + ); - it('should return correct theme color', () => { - expect( - getFlatInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - placeholderColor: getTheme().colors.onSurfaceVariant, - }); - }); + expect(queryByText('$')).toBeNull(); + expect(queryByText(' kg')).toBeNull(); + + fireEvent(getByTestId('tf-ps-focus'), 'focus'); + + expect(getByText('$')).toBeTruthy(); + expect(getByText(' kg')).toBeTruthy(); }); -describe('getFlatInputColor - background color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceContainerHighest, - }); - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(true), - }) - ).toMatchObject({ - backgroundColor: getTheme(true).colors.surfaceContainerHighest, - }); - }); +it('places prefix Text before the TextInput and suffix Text after it', () => { + const { toJSON } = render( + {}} + prefix="$" + suffix="/100" + testID="tf-order" + /> + ); - it('should return correct theme color, for theme version 3', () => { - expect( - getFlatInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceVariant, - }); - }); + const tree = toJSON(); + expect(firstIndexOfTextChildArrayInTree(tree, '$')).toBeLessThan( + firstIndexOfTestIdInTree(tree, 'tf-order') + ); + expect(firstIndexOfTestIdInTree(tree, 'tf-order')).toBeLessThan( + firstIndexOfTextChildArrayInTree(tree, '/100') + ); }); -describe('getFlatInputColor - error color', () => { - it('should return correct error color, no matter what the theme is', () => { - expect( - getFlatInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - errorColor: getTheme().colors.error, - }); - - expect( - getFlatInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - errorColor: getTheme().colors.error, - }); - }); +it('aligns input text toward the suffix when suffix is active (LTR)', () => { + const { getByTestId } = render( + {}} + suffix="/100" + testID="tf-suffix-align-ltr" + /> + ); + + expect( + StyleSheet.flatten(getByTestId('tf-suffix-align-ltr').props.style) + ).toEqual( + expect.objectContaining({ + textAlign: 'right', + writingDirection: 'ltr', + }) + ); }); -describe('getFlatInputColor - active color', () => { - it('should return disabled color, for theme version 3', () => { - expect( - getFlatInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.primary, - disabledOpacity: stateOpacity.disabled, - }); - }); +it('aligns input text toward the suffix when suffix is active (RTL)', () => { + I18nManager.isRTL = true; - it('should return correct active color, if error, no matter what the theme is', () => { - expect( - getFlatInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.error, - }); - - expect( - getFlatInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.error, - }); - }); + const { getByTestId } = render( + {}} + suffix="/100" + testID="tf-suffix-align-rtl" + /> + ); - it('should return custom active color, no matter what the theme is', () => { - expect( - getFlatInputColors({ - activeUnderlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: 'beige', - }); - - expect( - getFlatInputColors({ - activeUnderlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: 'beige', - }); - }); + expect( + StyleSheet.flatten(getByTestId('tf-suffix-align-rtl').props.style) + ).toEqual( + expect.objectContaining({ + textAlign: 'left', + writingDirection: 'rtl', + }) + ); +}); - it('should return theme active color, for theme version 3', () => { - expect( - getFlatInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.primary, - }); - }); +it('uses default horizontal alignment when suffix prop exists but suffix is not shown yet (LTR)', () => { + const { getByTestId } = render( + {}} + suffix="/100" + testID="tf-no-suffix-yet" + /> + ); + + expect( + StyleSheet.flatten(getByTestId('tf-no-suffix-yet').props.style) + ).toEqual( + expect.objectContaining({ + textAlign: 'left', + writingDirection: 'ltr', + }) + ); }); -describe('getOutlinedInputColors - outline color', () => { - it('should return correct disabled color, for theme version 3, light theme', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - outlineColor: getTheme().colors.outlineVariant, - }); - }); +it('does not apply the TextInput style prop to prefix or suffix Text', () => { + const { getByTestId, getByText } = render( + {}} + prefix="$" + suffix="]" + style={{ fontSize: 40, letterSpacing: 9 }} + testID="tf-input-style" + /> + ); - it('should return correct disabled color, for theme version 3, dark theme', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(true), - }) - ).toMatchObject({ - outlineColor: 'transparent', - }); - }); + const inputFlat = StyleSheet.flatten( + getByTestId('tf-input-style').props.style + ); + expect(inputFlat).toEqual( + expect.objectContaining({ fontSize: 40, letterSpacing: 9 }) + ); - it('should return custom color, if not disabled, no matter what the theme is', () => { - expect( - getOutlinedInputColors({ - customOutlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - outlineColor: 'beige', - }); - - expect( - getOutlinedInputColors({ - customOutlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - outlineColor: 'beige', - }); - }); + const prefixFlat = StyleSheet.flatten(getByText('$').props.style); + const suffixFlat = StyleSheet.flatten(getByText(']').props.style); - it('should return theme color, for theme version 3', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - outlineColor: getTheme().colors.outline, - }); - }); + expect(prefixFlat.fontSize).not.toBe(40); + expect(prefixFlat.letterSpacing).toBeUndefined(); + expect(suffixFlat.fontSize).not.toBe(40); + expect(suffixFlat.letterSpacing).toBeUndefined(); }); -describe('getOutlinedInputColors - input text color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - inputTextColor: getTheme().colors.onSurface, - disabledOpacity: stateOpacity.disabled, - }); - }); +it('passes defaultValue to the native input when uncontrolled without counter', () => { + const { getByTestId } = render( + {}} + testID="tf-uncontrolled" + /> + ); - it('should return correct theme color, for theme version 3', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - inputTextColor: getTheme().colors.onSurface, - }); - }); + const input = getByTestId('tf-uncontrolled'); + expect(input.props.defaultValue).toBe('hello'); + expect(input.props.value).toBeUndefined(); }); -describe('getOutlinedInputColors - placeholder color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - placeholderColor: getTheme().colors.onSurfaceVariant, - disabledOpacity: stateOpacity.disabled, - }); - }); +it('updates the character counter for an uncontrolled field with counter enabled', () => { + const onChangeText = jest.fn(); + const { getByTestId, getByText } = render( + + ); - it('should return correct theme color, for theme version 3', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - placeholderColor: getTheme().colors.onSurfaceVariant, - }); - }); + expect(getByText('1/10')).toBeTruthy(); + + fireEvent.changeText(getByTestId('tf-uncontrolled-counter'), 'abcd'); + + expect(onChangeText).toHaveBeenCalledWith('abcd'); + expect(getByText('4/10')).toBeTruthy(); }); -describe('getOutlinedInputColors - error color', () => { - it('should return correct error color, no matter what the theme is', () => { - expect( - getOutlinedInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - errorColor: getTheme().colors.error, - }); - - expect( - getOutlinedInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - errorColor: getTheme().colors.error, - }); +it('resets counter and hides prefix/suffix when clear() is called on uncontrolled field while blurred', () => { + const ref = React.createRef(); + const { getByText, queryByText } = render( + + ); + + expect(getByText('3/200')).toBeTruthy(); + expect(getByText('$')).toBeTruthy(); + expect(getByText('/100')).toBeTruthy(); + + act(() => { + ref.current?.clear(); }); + + expect(getByText('0/200')).toBeTruthy(); + expect(queryByText('$')).toBeNull(); + expect(queryByText('/100')).toBeNull(); }); -describe('getOutlinedInputColors - active color', () => { - it('should return disabled color, for theme version 3', () => { - expect( - getOutlinedInputColors({ - disabled: true, - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.primary, - disabledOpacity: stateOpacity.disabled, - }); - }); +it('resets counter but keeps prefix/suffix visible when clear() is called on uncontrolled field while focused', () => { + const ref = React.createRef(); + const { getByTestId, getByText } = render( + + ); - it('should return correct active color, if error, no matter what the theme is', () => { - expect( - getOutlinedInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.error, - }); - - expect( - getOutlinedInputColors({ - error: true, - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.error, - }); - }); + expect(getByText('2/100')).toBeTruthy(); + expect(getByText('$')).toBeTruthy(); + expect(getByText(' kg')).toBeTruthy(); - it('should return custom active color, no matter what the theme is', () => { - expect( - getOutlinedInputColors({ - activeOutlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: 'beige', - }); - - expect( - getOutlinedInputColors({ - activeOutlineColor: 'beige', - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: 'beige', - }); - }); + fireEvent(getByTestId('tf-clear-focused'), 'focus'); - it('should return theme active color, for theme version 3', () => { - expect( - getOutlinedInputColors({ - theme: getTheme(), - }) - ).toMatchObject({ - activeColor: getTheme().colors.primary, - }); + act(() => { + ref.current?.clear(); }); + + expect(getByText('0/100')).toBeTruthy(); + expect(getByText('$')).toBeTruthy(); + expect(getByText(' kg')).toBeTruthy(); }); -describe('outlineStyle - underlineStyle', () => { - it('correctly applies outline style', () => { - const { getByTestId } = render( - - ); +it('notifies the parent via onChangeText when clear() is called on a controlled field', () => { + const ref = React.createRef(); + const onChangeText = jest.fn(); + const { getByTestId } = render( + + ); + + const input = getByTestId('tf-controlled'); + expect(input.props.value).toBe('test@example.com'); - expect(getByTestId('text-input-outline')).toHaveStyle({ - borderRadius: 16, - borderWidth: 6, - }); + act(() => { + ref.current?.clear(); }); - it('correctly applies underline style', () => { - const { getByTestId } = render( - - ); - - expect(getByTestId('text-input-underline')).toHaveStyle({ - borderRadius: 16, - borderWidth: 6, - }); + expect(onChangeText).toHaveBeenCalledWith(''); + expect(onChangeText).toHaveBeenCalledTimes(1); +}); + +it('hides prefix/suffix when blurring after clear() was called while focused', () => { + const ref = React.createRef(); + const { getByTestId, getByText, queryByText } = render( + + ); + + expect(getByText('$')).toBeTruthy(); + expect(getByText('/100')).toBeTruthy(); + + fireEvent(getByTestId('tf-clear-then-blur'), 'focus'); + + act(() => { + ref.current?.clear(); }); + + // While focused, prefix/suffix stay visible + expect(getByText('$')).toBeTruthy(); + expect(getByText('/100')).toBeTruthy(); + + fireEvent(getByTestId('tf-clear-then-blur'), 'blur'); + + // After blur with no text, prefix/suffix should be hidden + expect(queryByText('$')).toBeNull(); + expect(queryByText('/100')).toBeNull(); }); diff --git a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap index facd344c1e..0bbe16ebc3 100644 --- a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap @@ -1,2067 +1,2059 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`call onPress when affix adornment pressed 1`] = ` +exports[`renders filled TextInput with TextInput.Icon accessories 1`] = ` - + } + accessible={false} + collapsable={false} + focusable={true} + onBlur={[Function]} + onClick={[Function]} + onFocus={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + role="none" +> - + + + + Search + + + + - - Flat input - - - Flat input - + + + magnify + + + - - - - - + + + - +39 - + + + + + close + + + + + - -`; - -exports[`correctly applies a component as the text label 1`] = ` - + +`; + +exports[`renders filled TextInput with TextInput.Icon accessories when error is true 1`] = ` + + - - - - - Flat input - - - - - Flat input - - - - - - - - -`; - -exports[`correctly applies cursorColor prop 1`] = ` - - - - - - - Flat input - - - Flat input - - - + Search + - - - -`; - -exports[`correctly applies default textAlign based on default RTL 1`] = ` - - - - - Flat input - - - Flat input - + + + magnify + + + - - - -`; - -exports[`correctly applies height to multiline Outline TextInput 1`] = ` - - - + > + + - - - Outline Input - - - Outline Input - + + + close + + + - + - + } + /> `; -exports[`correctly applies paddingLeft from contentStyleProp 1`] = ` +exports[`renders filled TextInput with label and value 1`] = ` - + } + accessible={false} + collapsable={false} + focusable={true} + onBlur={[Function]} + onClick={[Function]} + onFocus={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + role="none" +> - + - - - With padding - - - With padding - - - - - + + + Email + + + + > + + - -`; - -exports[`correctly applies textAlign center 1`] = ` - + +`; + +exports[`renders outlined TextInput with TextInput.Icon accessories 1`] = ` + + + + Search + + + + + + + + + magnify + + + + + + + + + + - - Flat input - - - Flat input - + + + close + + + - - -`; - -exports[`correctly renders left-side affix adornment, and right-side icon adornment 1`] = ` - + +`; + +exports[`renders outlined TextInput with TextInput.Icon accessories when error is true 1`] = ` + + + + Search + + + - - Flat input - - - Flat input - + + + magnify + + + - - - - - /100 - - - + + - + + - heart - + [ + { + "lineHeight": 24, + "transform": [ + { + "scaleX": 1, + }, + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + + - -`; - -exports[`correctly renders left-side icon adornment, and right-side affix adornment 1`] = ` - + +`; + +exports[`renders outlined TextInput with label and value 1`] = ` + + - + - - - Flat input - - - Flat input - - - + Password + - - - - - - - - - heart - - - - + "color": "rgba(29, 27, 32, 1)", + "flex": 1, + "fontSize": 16, + "textAlign": "left", + "writingDirection": "ltr", + }, + undefined, + false, + false, + undefined, + ] + } + value="secret" + /> - - /100 - - + /> `; diff --git a/src/index.tsx b/src/index.tsx index 71f105295e..fdbf0b9be8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -36,7 +36,6 @@ export { default as Dialog } from './components/Dialog/Dialog'; export { default as Divider } from './components/Divider'; export { default as FAB } from './components/FAB'; export { default as AnimatedFAB } from './components/FAB/AnimatedFAB'; -export { default as HelperText } from './components/HelperText/HelperText'; export { default as Icon } from './components/Icon'; export { default as IconButton } from './components/IconButton/IconButton'; export { default as Menu } from './components/Menu/Menu'; @@ -50,7 +49,7 @@ export { default as Surface } from './components/Surface'; export { default as Switch } from './components/Switch/Switch'; export { default as Appbar } from './components/Appbar'; export { default as TouchableRipple } from './components/TouchableRipple/TouchableRipple'; -export { default as TextInput } from './components/TextInput/TextInput'; +export { default as TextInput } from './components/TextInput'; export { default as ToggleButton } from './components/ToggleButton'; export { default as SegmentedButtons } from './components/SegmentedButtons/SegmentedButtons'; export { default as Tooltip } from './components/Tooltip/Tooltip'; @@ -101,7 +100,6 @@ export type { Props as DrawerItemProps } from './components/Drawer/DrawerItem'; export type { Props as DrawerSectionProps } from './components/Drawer/DrawerSection'; export type { Props as FABProps } from './components/FAB/FAB'; export type { Props as FABGroupProps } from './components/FAB/FABGroup'; -export type { Props as HelperTextProps } from './components/HelperText/HelperText'; export type { Props as IconButtonProps } from './components/IconButton/IconButton'; export type { Props as ListAccordionProps } from './components/List/ListAccordion'; export type { Props as ListAccordionGroupProps } from './components/List/ListAccordionGroup'; @@ -125,9 +123,16 @@ export type { Props as SearchbarProps } from './components/Searchbar'; export type { Props as SnackbarProps } from './components/Snackbar'; export type { Props as SurfaceProps } from './components/Surface'; export type { Props as SwitchProps } from './components/Switch/Switch'; -export type { Props as TextInputProps } from './components/TextInput/TextInput'; -export type { Props as TextInputAffixProps } from './components/TextInput/Adornment/TextInputAffix'; -export type { Props as TextInputIconProps } from './components/TextInput/Adornment/TextInputIcon'; +export type { + TextInputProps, + TextInputRenderProps, + TextInputVariant, + TextInputHandles, +} from './components/TextInput/TextInput'; +export type { + TextInputAccessoryProps, + TextInputIconProps, +} from './components/TextInput/TextInputIcon'; export type { Props as ToggleButtonProps } from './components/ToggleButton/ToggleButton'; export type { Props as ToggleButtonGroupProps } from './components/ToggleButton/ToggleButtonGroup'; export type { Props as ToggleButtonRowProps } from './components/ToggleButton/ToggleButtonRow'; diff --git a/yarn.lock b/yarn.lock index 63b967951e..07931713e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10824,6 +10824,7 @@ __metadata: marked: "npm:^4.1.1" patch-package: "npm:^6.5.0" prism-react-renderer: "npm:^1.3.5" + process: "npm:^0.11.10" react: "npm:17.0.2" react-color: "npm:^2.19.3" react-dom: "npm:17.0.2" @@ -19950,6 +19951,13 @@ __metadata: languageName: node linkType: hard +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 + languageName: node + linkType: hard + "progress@npm:^2.0.3": version: 2.0.3 resolution: "progress@npm:2.0.3" From 64058953989fb4db96f038ec615dec7b397651ef Mon Sep 17 00:00:00 2001 From: Fabrizio Cucci Date: Fri, 22 May 2026 08:43:13 +0100 Subject: [PATCH 37/94] feat(checkbox): modernize for md3 spec compliance Rewrites the Checkbox renderer to match the Material Design 3 spec (https://m3.material.io/components/checkbox/specs): - 18dp container with 2dp outline (unselected) / 0dp outline + theme primary fill (selected), inside a 40dp state-layer tap target. - State-layer overlay renders hover (8%), focus (10%) and pressed (10%) layers in the color the spec defines for each (selected pressed flips to onSurface; error always wins). - Focus indicator: 3dp ring at theme.colors.secondary with the 2dp outer-offset from md.sys.state.focusIndicator. Gated on :focus-visible via the useFocusVisible hook added in #4952. - Animations approximate Compose Material3 Checkbox.kt: 100ms fill transition and 150ms checkmark draw, sequenced short-leg then long-leg to suggest the stroke fraction. Indeterminate uses a scaleX-animated dash. - No new peer-deps: the checkmark is built from two rotated rectangles (View-based), not an SVG path. utils.ts: - New getSelectionVisualState helper returns the full color + opacity + outline-width picture for a given state combo. - Legacy getSelectionControlColor kept as a compatibility export for RadioButtonAndroid (radio button modernization is out of scope for this PR). 9 snapshots auto-updated to reflect the new render tree. --- example/src/Examples/CheckboxExample.tsx | 248 +++- src/components/Checkbox/Checkbox.tsx | 431 ++++-- src/components/Checkbox/CheckboxItem.tsx | 7 +- src/components/Checkbox/tokens.ts | 29 + src/components/Checkbox/utils.ts | 143 +- .../RadioButton/RadioButtonAndroid.tsx | 3 +- src/components/RadioButton/utils.ts | 34 + .../__tests__/Checkbox/CheckboxItem.test.tsx | 11 +- .../__snapshots__/Checkbox.test.tsx.snap | 1213 +++++++++++------ .../__snapshots__/CheckboxItem.test.tsx.snap | 364 +++-- .../__tests__/Checkbox/utils.test.tsx | 222 ++- 11 files changed, 1841 insertions(+), 864 deletions(-) create mode 100644 src/components/Checkbox/tokens.ts diff --git a/example/src/Examples/CheckboxExample.tsx b/example/src/Examples/CheckboxExample.tsx index 10ac45e11d..4d89d3ef9b 100644 --- a/example/src/Examples/CheckboxExample.tsx +++ b/example/src/Examples/CheckboxExample.tsx @@ -1,59 +1,185 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; -import { Checkbox, Palette, Text, TouchableRipple } from 'react-native-paper'; +import { Checkbox, Text, useTheme } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; -const CheckboxExample = () => { - const [checkedNormal, setCheckedNormal] = React.useState(true); - const [checkedCustom, setCheckedCustom] = React.useState(true); - const [indeterminate, setIndeterminate] = React.useState(true); +type Status = 'unchecked' | 'checked' | 'indeterminate'; + +const STATUSES: Status[] = ['unchecked', 'checked', 'indeterminate']; + +type Row = { + label: string; + render: (status: Status) => React.ReactNode; +}; +const ROWS: Row[] = [ + { + label: 'Default', + render: (status) => {}} />, + }, + { + label: 'Disabled', + render: (status) => , + }, + { + label: 'Custom color (tertiary)', + render: (status) => , + }, + { + label: 'Error', + render: (status) => {}} />, + }, +]; + +const CustomColorCheckbox = ({ status }: { status: Status }) => { + const theme = useTheme(); return ( - - setCheckedNormal(!checkedNormal)}> - - Normal - - - - - + {}} + /> + ); +}; - setCheckedCustom(!checkedCustom)}> - - Custom - - - +const Interactive = () => { + const theme = useTheme(); + const [aChecked, setAChecked] = React.useState(false); + const [bIndeterminate, setBIndeterminate] = React.useState(false); + return ( + + + Tap to toggle + + + + setAChecked((v) => !v)} + /> - - - setIndeterminate(!indeterminate)}> - - Indeterminate - - - + + unchecked ↔ checked + + + + + setBIndeterminate((v) => !v)} + /> - + + unchecked ↔ indeterminate + + + + ); +}; - - Checked (Disabled) - +const IndeterminateParent = () => { + const theme = useTheme(); + const [child1, setChild1] = React.useState(true); + const [child2, setChild2] = React.useState(false); + const [child3, setChild3] = React.useState(true); + const allChecked = child1 && child2 && child3; + const noneChecked = !child1 && !child2 && !child3; + const parentStatus: Status = allChecked + ? 'checked' + : noneChecked + ? 'unchecked' + : 'indeterminate'; + const toggleAll = () => { + const next = !allChecked; + setChild1(next); + setChild2(next); + setChild3(next); + }; + return ( + + + Parent / children (indeterminate) + + + + + Select all + + + + setChild1((v) => !v)} + /> + + Apples + - - Unchecked (Disabled) - + + setChild2((v) => !v)} + /> + + Bananas + - - Indeterminate (Disabled) - + + setChild3((v) => !v)} + /> + + Cherries + + + ); +}; + +const CheckboxExample = () => { + const theme = useTheme(); + return ( + + + {ROWS.map((row) => ( + + + {row.label} + + + {STATUSES.map((status) => ( + + {row.render(status)} + + ))} + + + ))} + ); }; @@ -61,15 +187,43 @@ const CheckboxExample = () => { CheckboxExample.title = 'Checkbox'; const styles = StyleSheet.create({ - container: { - paddingVertical: 8, + scrollContent: { paddingHorizontal: 16, paddingTop: 16, paddingBottom: 32 }, + row: { marginBottom: 20 }, + rowLabel: { + fontSize: 14, + fontWeight: '500', + marginBottom: 6, + opacity: 0.7, + }, + cells: { + flexDirection: 'row', + alignItems: 'center', + }, + cell: { + width: 60, + alignItems: 'center', + }, + interactive: { marginBottom: 24 }, + interactiveRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + }, + interactiveRowSpacing: { marginTop: 8 }, + interactiveLabel: { + fontSize: 14, + opacity: 0.7, + }, + parentRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, }, - row: { + childRow: { flexDirection: 'row', alignItems: 'center', - justifyContent: 'space-between', - paddingVertical: 8, - paddingHorizontal: 16, + gap: 12, + paddingLeft: 32, }, }); diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 69087a861e..7173e64501 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,16 +1,30 @@ import * as React from 'react'; -import { - Animated, +import { Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent, - StyleSheet, - View, + NativeSyntheticEvent, + StyleProp, + TargetedEvent, + ViewStyle, } from 'react-native'; -import { getSelectionControlColor } from './utils'; +import Animated, { + Easing, + ReduceMotion, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; + +import { CheckboxTokens } from './tokens'; +import { getSelectionVisualState } from './utils'; +import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; +import { tokens } from '../../theme/tokens'; import type { $RemoveChildren, ThemeProp } from '../../types'; -import MaterialCommunityIcon from '../MaterialCommunityIcon'; +import { isKeyboardFocusEvent } from '../../utils/isKeyboardFocusEvent'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; export type Props = $RemoveChildren & { @@ -36,9 +50,9 @@ export type Props = $RemoveChildren & { color?: ColorValue; /** * Whether the checkbox is in an error state. When true, the outline - * (unchecked) and container (checked / indeterminate) use - * `theme.colors.error`. `disabled` and explicit `color`/`uncheckedColor` - * overrides take precedence. + * (unchecked) and container (selected) use `theme.colors.error`. + * `disabled` and explicit `color`/`uncheckedColor` overrides take + * precedence. */ error?: boolean; /** @@ -49,9 +63,28 @@ export type Props = $RemoveChildren & { * testID to be used on tests. */ testID?: string; + /** + * Custom style to override the default tap target. Passed through to + * the underlying `TouchableRipple`. + */ + style?: StyleProp; }; -const ANIMATION_DURATION = 100; +// Spec dimensions (https://m3.material.io/components/checkbox/specs). +const { + containerSize: CONTAINER_SIZE, + containerRadius: CONTAINER_RADIUS, + outlineWidth: OUTLINE_WIDTH, + stateLayerSize: STATE_LAYER_SIZE, +} = CheckboxTokens; + +const FOCUS_THICKNESS = tokens.md.sys.state.focusIndicator.thickness; +// Focus indicator is a circular ring at the 40dp state-layer boundary. +// We don't apply `focusIndicator.outerOffset` here because the surrounding +// `TouchableRipple borderless` clips overflow to the tap-target shape, +// so a ring drawn outside the 40dp circle would be cropped. +const FOCUS_RING_SIZE = STATE_LAYER_SIZE; +const FOCUS_RING_RADIUS = STATE_LAYER_SIZE / 2; /** * Checkboxes allow the selection of multiple options from a set. @@ -84,121 +117,343 @@ const Checkbox = ({ onPress, testID, error, + color, + uncheckedColor, + style, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { current: scaleAnim } = React.useRef( - new Animated.Value(1) + const reduceMotion = useReduceMotion(); + const { direction } = useLocale(); + // Web (react-native-web) doesn't auto-mirror layout, so flip the mask + // anchor manually for RTL. Native handles it via `I18nManager`. + const flipMaskForWebRTL = Platform.OS === 'web' && direction === 'rtl'; + const [focused, setFocused] = React.useState(false); + + const selected = status === 'checked' || status === 'indeterminate'; + + const reanimatedReduceMotion = reduceMotion + ? ReduceMotion.Always + : ReduceMotion.Never; + + const fillTimingConfig = React.useMemo( + () => ({ + duration: theme.motion.duration.short2, + easing: Easing.bezier(...theme.motion.easing.standard), + reduceMotion: reanimatedReduceMotion, + }), + [ + theme.motion.duration.short2, + theme.motion.easing.standard, + reanimatedReduceMotion, + ] + ); + const checkTimingConfig = React.useMemo( + () => ({ + duration: theme.motion.duration.short3, + easing: Easing.bezier(...theme.motion.easing.standard), + reduceMotion: reanimatedReduceMotion, + }), + [ + theme.motion.duration.short3, + theme.motion.easing.standard, + reanimatedReduceMotion, + ] ); - const isFirstRendering = React.useRef(true); - const { - animation: { scale }, - } = theme; + // 0 = unselected (outline only), 1 = selected (filled + drawn icon). + const fillProgress = useSharedValue(selected ? 1 : 0); + const checkProgress = useSharedValue(selected ? 1 : 0); + const firstRender = React.useRef(true); React.useEffect(() => { - // Do not run animation on very first rendering - if (isFirstRendering.current) { - isFirstRendering.current = false; + if (firstRender.current) { + firstRender.current = false; return; } + const target = selected ? 1 : 0; + fillProgress.value = withTiming(target, fillTimingConfig); + checkProgress.value = withTiming(target, checkTimingConfig); + }, [ + selected, + fillProgress, + checkProgress, + fillTimingConfig, + checkTimingConfig, + ]); - const checked = status === 'checked'; - - Animated.sequence([ - Animated.timing(scaleAnim, { - toValue: 0.85, - duration: checked ? ANIMATION_DURATION * scale : 0, - useNativeDriver: false, - }), - Animated.timing(scaleAnim, { - toValue: 1, - duration: checked - ? ANIMATION_DURATION * scale - : ANIMATION_DURATION * scale * 1.75, - useNativeDriver: false, - }), - ]).start(); - }, [status, scaleAnim, scale]); - - const checked = status === 'checked'; - const indeterminate = status === 'indeterminate'; - - const { selectionControlColor, selectionControlOpacity } = - getSelectionControlColor({ - theme, - disabled, - checked, - customColor: rest.color, - customUncheckedColor: rest.uncheckedColor, - error, - }); - - const borderWidth = scaleAnim.interpolate({ - inputRange: [0.8, 1], - outputRange: [7, 0], + // Visual state (colors + opacity) for the static layers. `hovered` / + // `pressed` aren't tracked here — `TouchableRipple` owns the press ripple + // and hover overlay. + const visual = getSelectionVisualState({ + theme, + selected, + disabled, + error, + customColor: color, + customUncheckedColor: uncheckedColor, }); - const icon = indeterminate - ? 'minus-box' - : checked - ? 'checkbox-marked' - : 'checkbox-blank-outline'; + // Outline fades out as fill fades in (and vice versa). + const outlineAnimatedStyle = useAnimatedStyle(() => ({ + opacity: 1 - fillProgress.value, + })); + + const fillAnimatedStyle = useAnimatedStyle(() => ({ + opacity: fillProgress.value, + })); + + const maskAnimatedStyle = useAnimatedStyle(() => ({ + width: checkProgress.value * CONTAINER_SIZE, + opacity: checkProgress.value, + })); + + // Remember the last drawn glyph so the reveal-mask can finish collapsing + // when `selected` flips back to false. Computed via the "derive state + // during render" pattern (https://react.dev/reference/react/useState#storing-information-from-previous-renders) + // so we don't mutate a ref during render. + const [lastGlyph, setLastGlyph] = React.useState<'check' | 'indeterminate'>( + status === 'indeterminate' ? 'indeterminate' : 'check' + ); + const nextGlyph: 'check' | 'indeterminate' = + status === 'checked' + ? 'check' + : status === 'indeterminate' + ? 'indeterminate' + : lastGlyph; + if (nextGlyph !== lastGlyph) { + setLastGlyph(nextGlyph); + } + const showIndeterminate = nextGlyph === 'indeterminate'; + + const handleFocus = React.useCallback( + (e: NativeSyntheticEvent) => { + if (disabled) return; + if (!isKeyboardFocusEvent(e)) return; + setFocused(true); + }, + [disabled] + ); + + const handleBlur = React.useCallback(() => { + setFocused(false); + }, []); + + // When `accessible={false}` is passed (typically by `CheckboxItem`, which + // owns the a11y tree for the wrapped row), suppress our own a11y role + // and state so the same logical control doesn't expose two `checked` + // states to assistive tech. + const accessibilityProps = + rest.accessible === false + ? {} + : { + accessibilityRole: 'checkbox' as const, + accessibilityState: { + disabled: !!disabled, + checked: (status === 'indeterminate' + ? 'mixed' + : status === 'checked') as boolean | 'mixed', + }, + accessibilityLiveRegion: 'polite' as const, + }; return ( - - - + + {focused && !disabled ? ( + + ) : null} + + + {showIndeterminate ? ( + + + + + + ) : ( + + )} - + ); }; +/** + * Reveal-mask checkmark: an L-shape (borderLeftWidth + borderBottomWidth + * rotated -45deg) inside a directional-anchored View whose width animates + * 0 → CONTAINER_SIZE so the stroke "draws in" along the writing direction. + */ +const Checkmark = ({ + color, + maskAnimatedStyle, + flipMaskForWebRTL, + isRTL, + autoSwapsBorders, +}: { + color: ColorValue; + maskAnimatedStyle: ReturnType; + flipMaskForWebRTL: boolean; + isRTL: boolean; + // True on platforms whose layout engine auto-swaps `borderLeftWidth` + // ↔ `borderRightWidth` in RTL (Android, iOS); false on web (RNW). + autoSwapsBorders: boolean; +}) => { + return ( + + + + + + ); +}; + +// Web-only style; not in StyleSheet because `outline` is outside ViewStyle. +const webNoOutline = { outline: 'none' } as unknown as ViewStyle; + const styles = StyleSheet.create({ - container: { - borderRadius: 18, - width: 36, - height: 36, - padding: 6, + tapTarget: { + width: STATE_LAYER_SIZE, + height: STATE_LAYER_SIZE, + borderRadius: STATE_LAYER_SIZE / 2, + alignItems: 'center', + justifyContent: 'center', + }, + tapTargetInner: { + width: STATE_LAYER_SIZE, + height: STATE_LAYER_SIZE, + alignItems: 'center', + justifyContent: 'center', + }, + focusRing: { + position: 'absolute', + width: FOCUS_RING_SIZE, + height: FOCUS_RING_SIZE, + borderRadius: FOCUS_RING_RADIUS, + borderWidth: FOCUS_THICKNESS, }, - fillContainer: { + container: { + width: CONTAINER_SIZE, + height: CONTAINER_SIZE, + borderRadius: CONTAINER_RADIUS, alignItems: 'center', justifyContent: 'center', + overflow: 'hidden', }, fill: { - height: 14, - width: 14, + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + borderRadius: CONTAINER_RADIUS, + }, + outline: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + borderWidth: OUTLINE_WIDTH, + borderRadius: CONTAINER_RADIUS, + }, + dash: { + width: 10, + height: 2, + borderRadius: 1, + }, + checkmarkMask: { + position: 'absolute', + left: 0, + top: 0, + height: CONTAINER_SIZE, + overflow: 'hidden', + }, + checkmarkMaskWebRTL: { + position: 'absolute', + right: 0, + top: 0, + height: CONTAINER_SIZE, + overflow: 'hidden', + }, + checkmarkContent: { + width: CONTAINER_SIZE, + height: CONTAINER_SIZE, + alignItems: 'center', + justifyContent: 'center', + }, + checkmarkGlyph: { + width: 11, + height: 6, + borderLeftWidth: 2, + borderBottomWidth: 2, + transform: [{ rotate: '-45deg' }, { translateY: -1 }, { translateX: 1 }], + }, + checkmarkGlyphRTL: { + borderLeftWidth: 0, + borderRightWidth: 2, }, }); diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index f74d8dc9d5..70c7ec737e 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -143,7 +143,10 @@ const CheckboxItem = ({ const theme = useInternalTheme(themeOverrides); const checkboxProps = { ...props, status, theme, disabled }; const isLeading = position === 'leading'; - const checkbox = ; + // The outer TouchableRipple below is the interactable element + a11y + // checkbox; the inner Checkbox is purely visual, so we exclude it from + // the accessibility tree to avoid duplicate `checked` states. + const checkbox = ; const textAlign = isLeading ? 'right' : 'left'; @@ -157,7 +160,7 @@ const CheckboxItem = ({ accessibilityLabel={accessibilityLabel} accessibilityRole="checkbox" accessibilityState={{ - checked: status === 'checked', + checked: status === 'indeterminate' ? 'mixed' : status === 'checked', disabled, }} onPress={onPress} diff --git a/src/components/Checkbox/tokens.ts b/src/components/Checkbox/tokens.ts new file mode 100644 index 0000000000..6406f25c47 --- /dev/null +++ b/src/components/Checkbox/tokens.ts @@ -0,0 +1,29 @@ +import type { ColorRole } from '../../theme/types'; + +/** + * MD3 Checkbox spec dimensions and color-role tokens. + * @see https://m3.material.io/components/checkbox/specs + */ +const sizes = { + containerSize: 18, + containerRadius: 2, + outlineWidth: 2, + stateLayerSize: 40, +} as const; + +const colors = { + containerColor: 'primary', + disabledContainerColor: 'onSurface', + errorContainerColor: 'error', + outlineColor: 'onSurfaceVariant', + disabledOutlineColor: 'onSurface', + errorOutlineColor: 'error', + iconColor: 'onPrimary', + disabledIconColor: 'surface', + errorIconColor: 'onError', + selectedStateLayerColor: 'primary', + unselectedStateLayerColor: 'onSurface', + errorStateLayerColor: 'error', +} as const satisfies Record; + +export const CheckboxTokens = { ...sizes, ...colors }; diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index 10f6d4eb1e..acd3c61126 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -1,106 +1,121 @@ import type { ColorValue } from 'react-native'; +import { CheckboxTokens } from './tokens'; import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; +// MD3 Checkbox spec: https://m3.material.io/components/checkbox/specs + const stateOpacity = tokens.md.sys.state.opacity; -const getCheckedColor = ({ - theme, - customColor, - error, -}: { +type SelectionState = { theme: InternalTheme; - customColor?: ColorValue; + selected: boolean; + disabled?: boolean; error?: boolean; -}) => { + customColor?: ColorValue; + customUncheckedColor?: ColorValue; +}; + +type SelectionVisualState = { + containerColor: ColorValue; + outlineColor: ColorValue; + containerOpacity: number; + iconColor: ColorValue; +}; + +const getContainerColor = ({ + theme, + disabled, + error, + customColor, +}: SelectionState): ColorValue => { + if (disabled) { + return theme.colors[CheckboxTokens.disabledContainerColor]; + } if (customColor) { return customColor; } - if (error) { - return theme.colors.error; + return theme.colors[CheckboxTokens.errorContainerColor]; } - - return theme.colors.primary; + return theme.colors[CheckboxTokens.containerColor]; }; -const getUncheckedColor = ({ +const getOutlineColor = ({ theme, - customUncheckedColor, + disabled, error, -}: { - theme: InternalTheme; - customUncheckedColor?: ColorValue; - error?: boolean; -}) => { + customUncheckedColor, +}: SelectionState): ColorValue => { + if (disabled) { + return theme.colors[CheckboxTokens.disabledOutlineColor]; + } if (customUncheckedColor) { return customUncheckedColor; } - if (error) { - return theme.colors.error; + return theme.colors[CheckboxTokens.errorOutlineColor]; } - - return theme.colors.onSurfaceVariant; + return theme.colors[CheckboxTokens.outlineColor]; }; -const getControlColor = ({ +const getIconColor = ({ theme, - checked, + selected, disabled, - checkedColor, - uncheckedColor, -}: { - theme: InternalTheme; - checked: boolean; - checkedColor: ColorValue; - uncheckedColor: ColorValue; - disabled?: boolean; -}) => { + error, +}: SelectionState): ColorValue => { + if (!selected) { + return 'transparent'; + } if (disabled) { - return theme.colors.onSurface; + return theme.colors[CheckboxTokens.disabledIconColor]; } - - if (checked) { - return checkedColor; + if (error) { + return theme.colors[CheckboxTokens.errorIconColor]; } - return uncheckedColor; + return theme.colors[CheckboxTokens.iconColor]; }; -export const getSelectionControlColor = ({ +/** + * Resolve the static (non-interactive) colors + opacity for the Checkbox + * renderer. Hover / pressed / focused visuals are owned by `TouchableRipple` + * and the focus-ring outline, so they don't appear here. + */ +export const getSelectionVisualState = ({ theme, + selected, disabled, - checked, + error, customColor, customUncheckedColor, - error, -}: { - theme: InternalTheme; - checked: boolean; - disabled?: boolean; - customColor?: ColorValue; - customUncheckedColor?: ColorValue; - error?: boolean; -}) => { - const checkedColor = getCheckedColor({ theme, customColor, error }); - const uncheckedColor = getUncheckedColor({ - theme, - customUncheckedColor, - error, - }); - const selectionControlOpacity = disabled - ? stateOpacity.disabled - : stateOpacity.enabled; - +}: SelectionState): SelectionVisualState => { return { - selectionControlColor: getControlColor({ + containerColor: getContainerColor({ + theme, + selected, + disabled, + error, + customColor, + customUncheckedColor, + }), + outlineColor: getOutlineColor({ + theme, + selected, + disabled, + error, + customColor, + customUncheckedColor, + }), + containerOpacity: disabled ? stateOpacity.disabled : stateOpacity.enabled, + iconColor: getIconColor({ theme, + selected, disabled, - checked, - checkedColor, - uncheckedColor, + error, + customColor, + customUncheckedColor, }), - selectionControlOpacity, }; }; diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index cb1757726b..eeb42c218e 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -2,10 +2,9 @@ import * as React from 'react'; import { Animated, StyleSheet, View } from 'react-native'; import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; -import { handlePress, isChecked } from './utils'; +import { getSelectionControlColor, handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, ThemeProp } from '../../types'; -import { getSelectionControlColor } from '../Checkbox/utils'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; export type Props = $RemoveChildren & { diff --git a/src/components/RadioButton/utils.ts b/src/components/RadioButton/utils.ts index 96adafaf47..a4a3904d8a 100644 --- a/src/components/RadioButton/utils.ts +++ b/src/components/RadioButton/utils.ts @@ -93,3 +93,37 @@ export const getSelectionControlIOSColor = ({ checkedColorOpacity, }; }; + +export const getSelectionControlColor = ({ + theme, + disabled, + checked, + customColor, + customUncheckedColor, + error, +}: { + theme: InternalTheme; + checked: boolean; + disabled?: boolean; + customColor?: ColorValue; + customUncheckedColor?: ColorValue; + error?: boolean; +}): { selectionControlColor: ColorValue; selectionControlOpacity: number } => { + const opacity = disabled ? stateOpacity.disabled : stateOpacity.enabled; + const checkedColor = customColor + ? customColor + : error + ? theme.colors.error + : theme.colors.primary; + const uncheckedColor = customUncheckedColor + ? customUncheckedColor + : error + ? theme.colors.error + : theme.colors.onSurfaceVariant; + const color = disabled + ? theme.colors.onSurface + : checked + ? checkedColor + : uncheckedColor; + return { selectionControlColor: color, selectionControlOpacity: opacity }; +}; diff --git a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx index 7018d01c38..d66de5fef7 100644 --- a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx +++ b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx @@ -29,8 +29,7 @@ it('should have `accessibilityState={ checked: true }` when `status="checked"`', ); - const elements = getAllByA11yState({ checked: true }); - expect(elements).toHaveLength(2); + expect(getAllByA11yState({ checked: true })).toHaveLength(1); }); it('should have `accessibilityState={ checked: false }` when `status="unchecked"', () => { @@ -38,17 +37,15 @@ it('should have `accessibilityState={ checked: false }` when `status="unchecked" ); - const elements = getAllByA11yState({ checked: false }); - expect(elements).toHaveLength(2); + expect(getAllByA11yState({ checked: false })).toHaveLength(1); }); -it('should have `accessibilityState={ checked: false }` when `status="indeterminate"', () => { +it('should have `accessibilityState={ checked: "mixed" }` when `status="indeterminate"`', () => { const { getAllByA11yState } = render( ); - const elements = getAllByA11yState({ checked: false }); - expect(elements).toHaveLength(2); + expect(getAllByA11yState({ checked: 'mixed' })).toHaveLength(1); }); it('disables the row when the prop disabled is true', () => { diff --git a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap index f1a7834ac6..1b21c11b68 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap @@ -22,6 +22,7 @@ exports[`renders Checkbox with custom testID 1`] = ` } } accessible={true} + centered={true} collapsable={false} focusable={true} onBlur={[Function]} @@ -38,87 +39,148 @@ exports[`renders Checkbox with custom testID 1`] = ` { "overflow": "hidden", }, - { - "borderRadius": 18, - "height": 36, - "padding": 6, - "width": 36, - }, + [ + { + "alignItems": "center", + "borderRadius": 20, + "height": 40, + "justifyContent": "center", + "width": 40, + }, + undefined, + undefined, + ], ] } testID="custom:testID" > - - checkbox-marked - + + + + + + @@ -146,8 +208,8 @@ exports[`renders checked Checkbox with color 1`] = ` } } accessible={true} + centered={true} collapsable={false} - color="red" focusable={true} onBlur={[Function]} onClick={[Function]} @@ -163,86 +225,147 @@ exports[`renders checked Checkbox with color 1`] = ` { "overflow": "hidden", }, - { - "borderRadius": 18, - "height": 36, - "padding": 6, - "width": 36, - }, + [ + { + "alignItems": "center", + "borderRadius": 20, + "height": 40, + "justifyContent": "center", + "width": 40, + }, + undefined, + undefined, + ], ] } > - - checkbox-marked - + + + + + + @@ -270,6 +393,7 @@ exports[`renders checked Checkbox with onPress 1`] = ` } } accessible={true} + centered={true} collapsable={false} focusable={true} onBlur={[Function]} @@ -286,86 +410,147 @@ exports[`renders checked Checkbox with onPress 1`] = ` { "overflow": "hidden", }, - { - "borderRadius": 18, - "height": 36, - "padding": 6, - "width": 36, - }, + [ + { + "alignItems": "center", + "borderRadius": 20, + "height": 40, + "justifyContent": "center", + "width": 40, + }, + undefined, + undefined, + ], ] } > - - checkbox-marked - + + + + + + @@ -378,7 +563,7 @@ exports[`renders indeterminate Checkbox 1`] = ` accessibilityState={ { "busy": undefined, - "checked": false, + "checked": "mixed", "disabled": false, "expanded": undefined, "selected": undefined, @@ -393,6 +578,7 @@ exports[`renders indeterminate Checkbox 1`] = ` } } accessible={true} + centered={true} collapsable={false} focusable={true} onBlur={[Function]} @@ -409,86 +595,134 @@ exports[`renders indeterminate Checkbox 1`] = ` { "overflow": "hidden", }, - { - "borderRadius": 18, - "height": 36, - "padding": 6, - "width": 36, - }, + [ + { + "alignItems": "center", + "borderRadius": 20, + "height": 40, + "justifyContent": "center", + "width": 40, + }, + undefined, + undefined, + ], ] } > - - minus-box - + + + + + + @@ -501,7 +735,7 @@ exports[`renders indeterminate Checkbox with color 1`] = ` accessibilityState={ { "busy": undefined, - "checked": false, + "checked": "mixed", "disabled": true, "expanded": undefined, "selected": undefined, @@ -516,8 +750,8 @@ exports[`renders indeterminate Checkbox with color 1`] = ` } } accessible={true} + centered={true} collapsable={false} - color="red" focusable={true} onBlur={[Function]} onClick={[Function]} @@ -533,86 +767,134 @@ exports[`renders indeterminate Checkbox with color 1`] = ` { "overflow": "hidden", }, - { - "borderRadius": 18, - "height": 36, - "padding": 6, - "width": 36, - }, + [ + { + "alignItems": "center", + "borderRadius": 20, + "height": 40, + "justifyContent": "center", + "width": 40, + }, + undefined, + undefined, + ], ] } > - - minus-box - + + + + + + @@ -640,8 +922,8 @@ exports[`renders unchecked Checkbox with color 1`] = ` } } accessible={true} + centered={true} collapsable={false} - color="red" focusable={true} onBlur={[Function]} onClick={[Function]} @@ -657,86 +939,147 @@ exports[`renders unchecked Checkbox with color 1`] = ` { "overflow": "hidden", }, - { - "borderRadius": 18, - "height": 36, - "padding": 6, - "width": 36, - }, + [ + { + "alignItems": "center", + "borderRadius": 20, + "height": 40, + "justifyContent": "center", + "width": 40, + }, + undefined, + undefined, + ], ] } > - - checkbox-marked - + + + + + + @@ -764,6 +1107,7 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` } } accessible={true} + centered={true} collapsable={false} focusable={true} onBlur={[Function]} @@ -780,86 +1124,147 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` { "overflow": "hidden", }, - { - "borderRadius": 18, - "height": 36, - "padding": 6, - "width": 36, - }, + [ + { + "alignItems": "center", + "borderRadius": 20, + "height": 40, + "justifyContent": "center", + "width": 40, + }, + undefined, + undefined, + ], ] } > - - checkbox-blank-outline - + + + + + + diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index 8f8faef29b..a511c78821 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -57,12 +57,10 @@ exports[`can render leading checkbox control 1`] = ` } > - - checkbox-blank-outline - + + + + + + @@ -313,12 +373,10 @@ exports[`renders unchecked 1`] = ` Unchecked Button - - checkbox-blank-outline - + + + + + + diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index 78af32e8ea..f2476c2cc5 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,163 +1,129 @@ import { getTheme } from '../../../core/theming'; import { tokens } from '../../../theme/tokens'; -import { getSelectionControlColor } from '../../Checkbox/utils'; +import { getSelectionVisualState } from '../../Checkbox/utils'; + const stateOpacity = tokens.md.sys.state.opacity; +const theme = getTheme(); +const darkTheme = getTheme(true); -describe('getSelectionControlColor - checkbox color', () => { - it('should return correct disabled color, for theme version 3', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - disabled: true, - checked: false, - }) - ).toMatchObject({ - selectionControlColor: getTheme().colors.onSurface, - selectionControlOpacity: stateOpacity.disabled, +describe('getSelectionVisualState', () => { + describe('containerColor (the fill)', () => { + it('uses theme.colors.primary when selected (default)', () => { + expect(getSelectionVisualState({ theme, selected: true })).toMatchObject({ + containerColor: theme.colors.primary, + }); }); - }); - it('should return custom color, checked', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: true, - customColor: 'purple', - }) - ).toMatchObject({ - selectionControlColor: 'purple', + it('uses theme.colors.error when selected + error', () => { + expect( + getSelectionVisualState({ theme, selected: true, error: true }) + ).toMatchObject({ containerColor: theme.colors.error }); }); - }); - it('should return theme color, for theme version 3, checked', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: true, - }) - ).toMatchObject({ - selectionControlColor: getTheme().colors.primary, + it('uses customColor when provided (overrides error)', () => { + expect( + getSelectionVisualState({ + theme, + selected: true, + error: true, + customColor: 'purple', + }) + ).toMatchObject({ containerColor: 'purple' }); }); - }); - it('should return custom color, unchecked', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: false, - customUncheckedColor: 'purple', - }) - ).toMatchObject({ - selectionControlColor: 'purple', + it('falls back to onSurface when disabled (overrides customColor)', () => { + expect( + getSelectionVisualState({ + theme, + selected: true, + disabled: true, + customColor: 'purple', + }) + ).toMatchObject({ containerColor: theme.colors.onSurface }); }); }); - it('should return theme color, for theme version 3, unchecked', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: false, - }) - ).toMatchObject({ - selectionControlColor: getTheme().colors.onSurfaceVariant, + describe('outlineColor (unselected border)', () => { + it('uses theme.colors.onSurfaceVariant by default', () => { + expect(getSelectionVisualState({ theme, selected: false })).toMatchObject( + { outlineColor: theme.colors.onSurfaceVariant } + ); }); - }); - it('should return theme color, unchecked, dark mode', () => { - expect( - getSelectionControlColor({ - theme: getTheme(true), - checked: false, - }) - ).toMatchObject({ - selectionControlColor: getTheme(true).colors.onSurfaceVariant, + it('uses theme.colors.error when error', () => { + expect( + getSelectionVisualState({ theme, selected: false, error: true }) + ).toMatchObject({ outlineColor: theme.colors.error }); }); - }); - it('should return theme color, unchecked, light mode', () => { - expect( - getSelectionControlColor({ - theme: getTheme(false), - checked: false, - }) - ).toMatchObject({ - selectionControlColor: getTheme(false).colors.onSurfaceVariant, + it('uses customUncheckedColor when provided (overrides error)', () => { + expect( + getSelectionVisualState({ + theme, + selected: false, + error: true, + customUncheckedColor: 'orange', + }) + ).toMatchObject({ outlineColor: 'orange' }); }); - }); - it('should return error color, checked, when error is true', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: true, - error: true, - }) - ).toMatchObject({ - selectionControlColor: getTheme().colors.error, + it('falls back to onSurface when disabled', () => { + expect( + getSelectionVisualState({ + theme, + selected: false, + disabled: true, + customUncheckedColor: 'orange', + }) + ).toMatchObject({ outlineColor: theme.colors.onSurface }); }); }); - it('should return error color, unchecked, when error is true', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: false, - error: true, - }) - ).toMatchObject({ - selectionControlColor: getTheme().colors.error, + describe('iconColor (checkmark / dash glyph)', () => { + it('is transparent when unselected (no glyph drawn)', () => { + expect(getSelectionVisualState({ theme, selected: false })).toMatchObject( + { iconColor: 'transparent' } + ); }); - }); - it('should return error color, checked, dark mode, when error is true', () => { - expect( - getSelectionControlColor({ - theme: getTheme(true), - checked: true, - error: true, - }) - ).toMatchObject({ - selectionControlColor: getTheme(true).colors.error, + it('uses theme.colors.onPrimary when selected (default)', () => { + expect(getSelectionVisualState({ theme, selected: true })).toMatchObject({ + iconColor: theme.colors.onPrimary, + }); + }); + + it('uses theme.colors.onError when selected + error', () => { + expect( + getSelectionVisualState({ theme, selected: true, error: true }) + ).toMatchObject({ iconColor: theme.colors.onError }); }); - }); - it('should return disabled color when both disabled and error are true (disabled wins)', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - disabled: true, - checked: true, - error: true, - }) - ).toMatchObject({ - selectionControlColor: getTheme().colors.onSurface, - selectionControlOpacity: stateOpacity.disabled, + it('uses theme.colors.surface when selected + disabled', () => { + expect( + getSelectionVisualState({ theme, selected: true, disabled: true }) + ).toMatchObject({ iconColor: theme.colors.surface }); }); }); - it('should return custom color when both customColor and error are true, checked (customColor wins)', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: true, - customColor: 'purple', - error: true, - }) - ).toMatchObject({ - selectionControlColor: 'purple', + describe('containerOpacity', () => { + it('is the enabled state opacity by default', () => { + expect(getSelectionVisualState({ theme, selected: true })).toMatchObject({ + containerOpacity: stateOpacity.enabled, + }); + }); + + it('drops to the disabled state opacity when disabled', () => { + expect( + getSelectionVisualState({ theme, selected: true, disabled: true }) + ).toMatchObject({ containerOpacity: stateOpacity.disabled }); }); }); - it('should return custom unchecked color when both customUncheckedColor and error are true, unchecked (customUncheckedColor wins)', () => { - expect( - getSelectionControlColor({ - theme: getTheme(), - checked: false, - customUncheckedColor: 'purple', - error: true, - }) - ).toMatchObject({ - selectionControlColor: 'purple', + describe('dark theme', () => { + it('respects the dark theme palette', () => { + const v = getSelectionVisualState({ theme: darkTheme, selected: true }); + expect(v.containerColor).toBe(darkTheme.colors.primary); + expect(v.iconColor).toBe(darkTheme.colors.onPrimary); }); }); }); From 2f5e55d5a034d7cf6ddb8ef8587960791f461ed1 Mon Sep 17 00:00:00 2001 From: Adrian Cotfas Date: Fri, 22 May 2026 15:36:58 +0300 Subject: [PATCH 38/94] feat(fab): modernize FloatingActionButton to MD3 Rework the FAB family to follow Material Design 3, replacing the legacy FAB, FAB.Group, and AnimatedFAB with a token-driven implementation. - New `variant` prop (primary, secondary, tertiary, tonalPrimary, tonalSecondary, tonalTertiary) drives role-based coloring, defaulting to `tonalPrimary`. - New `size` prop (default, medium, large) replaces the old `small` / `customSize` sizing, with shape and metrics sourced from spec tokens. - `visible` toggling now animates the spec'd enter/exit (scale + alpha) on the FAB itself. - Add `FAB.Extended` for the extended (icon + label) FAB. - Add `FAB.Menu` (with Menu, MenuItem, MenuTrigger) to replace the old `FAB.Group` speed-dial pattern. - Add a keyboard focus ring (web) and shared visibility animation hook. - Split the implementation into Shell, Content, Extended, Menu, tokens, useFocusRing, and useVisibility for clarity. BREAKING CHANGE: The FAB API has been redesigned for MD3 with no deprecation aliases. - `AnimatedFAB` and `AnimatedFABProps` are removed. Use `FAB.Extended` for the icon + label FAB. - `FAB.Group` and `FABGroupProps` are removed. Use `FAB.Menu` instead. - The legacy FAB props (`mode`, `small`, `color`, `label`, `uppercase`, `customSize`, `animated`) are removed. Use `variant`, `size`, `containerColor`, and `contentColor` instead; for a labeled FAB switch to `FAB.Extended`. - New exported types: `FABExtendedProps`, `FABMenuProps`, `FABMenuItemProps`, `FABMenuTriggerProps`, `FABVariant`, `FABSize`. https://github.com/callstack/react-native-paper/issues/4959 - Lint, typescript, existing and new tests pass - manual visual inspection on all platforms --- docs/docusaurus.config.js | 4 +- docs/src/components/BannerExample.tsx | 2 +- example/src/ExampleList.tsx | 2 - .../src/Examples/ActivityIndicatorExample.tsx | 1 - .../AnimatedFABExample/AnimatedFABExample.tsx | 204 - .../Examples/AnimatedFABExample/CustomFAB.tsx | 61 - .../AnimatedFABExample/CustomFABControls.tsx | 136 - .../src/Examples/AnimatedFABExample/index.ts | 1 - example/src/Examples/AppbarExample.tsx | 2 - example/src/Examples/BannerExample.tsx | 7 +- example/src/Examples/FABExample.tsx | 412 +- example/src/Examples/TooltipExample.tsx | 2 +- src/components/FAB/AnimatedFAB.tsx | 594 -- src/components/FAB/Content.tsx | 118 + src/components/FAB/Extended.tsx | 288 + src/components/FAB/FAB.tsx | 317 +- src/components/FAB/FABGroup.tsx | 533 -- src/components/FAB/Menu.tsx | 728 +++ src/components/FAB/Shell.tsx | 372 ++ src/components/FAB/index.ts | 13 - src/components/FAB/index.tsx | 7 + src/components/FAB/tokens.ts | 128 + src/components/FAB/useFocusRing.ts | 41 + src/components/FAB/useVisibility.ts | 103 + src/components/FAB/utils.ts | 421 +- src/components/__tests__/AnimatedFAB.test.tsx | 183 - src/components/__tests__/FAB.test.tsx | 342 +- src/components/__tests__/FABExtended.test.tsx | 108 + src/components/__tests__/FABGroup.test.tsx | 417 -- src/components/__tests__/FABMenu.test.tsx | 184 + src/components/__tests__/FABUtils.test.tsx | 157 + .../__snapshots__/AnimatedFAB.test.tsx.snap | 866 --- .../__tests__/__snapshots__/FAB.test.tsx.snap | 2836 +++++---- .../__snapshots__/FABExtended.test.tsx.snap | 1435 +++++ .../__snapshots__/FABMenu.test.tsx.snap | 5515 +++++++++++++++++ src/index.tsx | 15 +- src/theme/tokens/sys/elevation.ts | 20 +- src/theme/utils/shape.ts | 22 + tsconfig.json | 2 +- 39 files changed, 11060 insertions(+), 5539 deletions(-) delete mode 100644 example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx delete mode 100644 example/src/Examples/AnimatedFABExample/CustomFAB.tsx delete mode 100644 example/src/Examples/AnimatedFABExample/CustomFABControls.tsx delete mode 100644 example/src/Examples/AnimatedFABExample/index.ts delete mode 100644 src/components/FAB/AnimatedFAB.tsx create mode 100644 src/components/FAB/Content.tsx create mode 100644 src/components/FAB/Extended.tsx delete mode 100644 src/components/FAB/FABGroup.tsx create mode 100644 src/components/FAB/Menu.tsx create mode 100644 src/components/FAB/Shell.tsx delete mode 100644 src/components/FAB/index.ts create mode 100644 src/components/FAB/index.tsx create mode 100644 src/components/FAB/tokens.ts create mode 100644 src/components/FAB/useFocusRing.ts create mode 100644 src/components/FAB/useVisibility.ts delete mode 100644 src/components/__tests__/AnimatedFAB.test.tsx create mode 100644 src/components/__tests__/FABExtended.test.tsx delete mode 100644 src/components/__tests__/FABGroup.test.tsx create mode 100644 src/components/__tests__/FABMenu.test.tsx create mode 100644 src/components/__tests__/FABUtils.test.tsx delete mode 100644 src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap create mode 100644 src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap create mode 100644 src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap create mode 100644 src/theme/utils/shape.ts diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 99056b2426..a4b672640c 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -123,8 +123,8 @@ const config = { }, FAB: { FAB: 'FAB/FAB', - AnimatedFAB: 'FAB/AnimatedFAB', - FABGroup: 'FAB/FABGroup', + FABExtended: 'FAB/Extended', + FABMenu: 'FAB/Menu', }, IconButton: { IconButton: 'IconButton/IconButton', diff --git a/docs/src/components/BannerExample.tsx b/docs/src/components/BannerExample.tsx index 3e21a11ee4..fcb3e0a8e2 100644 --- a/docs/src/components/BannerExample.tsx +++ b/docs/src/components/BannerExample.tsx @@ -83,7 +83,7 @@ const BannerExample = () => { - {}} /> + {}} /> {}} /> {}} /> diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index bdf5af7428..18c958a4bb 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -6,7 +6,6 @@ import { Divider, List, useTheme } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import ActivityIndicatorExample from './Examples/ActivityIndicatorExample'; -import AnimatedFABExample from './Examples/AnimatedFABExample'; import AppbarExample from './Examples/AppbarExample'; import AvatarExample from './Examples/AvatarExample'; import BadgeExample from './Examples/BadgeExample'; @@ -51,7 +50,6 @@ import TooltipExample from './Examples/TooltipExample'; import TouchableRippleExample from './Examples/TouchableRippleExample'; export const mainExamples = { - AnimatedFAB: AnimatedFABExample, ActivityIndicator: ActivityIndicatorExample, Appbar: AppbarExample, Avatar: AvatarExample, diff --git a/example/src/Examples/ActivityIndicatorExample.tsx b/example/src/Examples/ActivityIndicatorExample.tsx index e3e5e72e3c..e5fc90f032 100644 --- a/example/src/Examples/ActivityIndicatorExample.tsx +++ b/example/src/Examples/ActivityIndicatorExample.tsx @@ -12,7 +12,6 @@ const ActivityIndicatorExample = () => { setAnimating(!animating)} /> diff --git a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx b/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx deleted file mode 100644 index 8215092b79..0000000000 --- a/example/src/Examples/AnimatedFABExample/AnimatedFABExample.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import * as React from 'react'; -import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'; -import { Animated, FlatList, Platform, StyleSheet, View } from 'react-native'; - -import Icon from '@react-native-vector-icons/material-design-icons'; -import { Avatar, Palette, Text, useTheme } from 'react-native-paper'; - -import CustomFAB from './CustomFAB'; -import CustomFABControls, { - Controls, - initialControls, -} from './CustomFABControls'; -import { animatedFABExampleData } from '../../../utils'; -type Item = { - id: string; - sender: string; - header: string; - message: string; - initials: string; - date: string; - read: boolean; - favorite: boolean; - bgColor: string; -}; - -const AnimatedFABExample = () => { - const { colors } = useTheme(); - - const isIOS = Platform.OS === 'ios'; - - const [extended, setExtended] = React.useState(true); - const [visible, setVisible] = React.useState(true); - - const [controls, setControls] = React.useState(initialControls); - - const { current: velocity } = React.useRef( - new Animated.Value(0) - ); - - const renderItem = React.useCallback( - ({ item }: { item: Item }) => { - return ( - - - - - - {item.sender} - - - {item.date} - - - - - - - {item.header} - - - {item.message} - - - - setVisible(!visible)} - style={styles.icon} - /> - - - - ); - }, - [visible] - ); - - const onScroll = ({ - nativeEvent, - }: NativeSyntheticEvent) => { - const currentScrollPosition = - Math.floor(nativeEvent?.contentOffset?.y) ?? 0; - - if (!isIOS) { - return velocity.setValue(currentScrollPosition); - } - - setExtended(currentScrollPosition <= 0); - }; - - const _keyExtractor = React.useCallback( - (item: { id: string }) => item.id, - [] - ); - - const { animateFrom, iconMode } = controls; - - return ( - <> - - - - - - ); -}; - -AnimatedFABExample.title = 'Animated Floating Action Button'; - -const styles = StyleSheet.create({ - container: { - padding: 16, - paddingBottom: 60, - }, - avatar: { - marginRight: 16, - marginTop: 8, - }, - flex: { - flex: 1, - }, - itemContainer: { - marginBottom: 16, - flexDirection: 'row', - }, - itemTextContentContainer: { - flexDirection: 'column', - flex: 1, - }, - itemHeaderContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - }, - itemMessageContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - flexGrow: 1, - }, - read: { - fontWeight: 'bold', - }, - icon: { - marginLeft: 16, - alignSelf: 'flex-end', - }, - date: { - fontSize: 12, - }, - header: { - fontSize: 14, - marginRight: 8, - flex: 1, - }, -}); - -export default AnimatedFABExample; diff --git a/example/src/Examples/AnimatedFABExample/CustomFAB.tsx b/example/src/Examples/AnimatedFABExample/CustomFAB.tsx deleted file mode 100644 index 9130debf33..0000000000 --- a/example/src/Examples/AnimatedFABExample/CustomFAB.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { Animated, Platform, StyleSheet, ViewStyle } from 'react-native'; - -import { AnimatedFAB } from 'react-native-paper'; - -type CustomFABProps = { - animatedValue: Animated.Value; - visible: boolean; - extended: boolean; - label: string; - animateFrom: 'left' | 'right'; - iconMode?: 'static' | 'dynamic'; - style?: ViewStyle; -}; - -const CustomFAB = ({ - animatedValue, - visible, - extended, - label, - animateFrom, - style, - iconMode, -}: CustomFABProps) => { - const [isExtended, setIsExtended] = React.useState(true); - - const isIOS = Platform.OS === 'ios'; - - React.useEffect(() => { - if (!isIOS) { - animatedValue.addListener(({ value }: { value: number }) => { - setIsExtended(value <= 0); - }); - } else setIsExtended(extended); - }, [animatedValue, extended, isIOS]); - - const fabStyle = { [animateFrom]: 16 }; - - return ( - console.log('Pressed')} - visible={visible} - animateFrom={animateFrom} - iconMode={iconMode} - style={[styles.fabStyle, style, fabStyle]} - /> - ); -}; - -export default CustomFAB; - -const styles = StyleSheet.create({ - fabStyle: { - bottom: 16, - position: 'absolute', - }, -}); diff --git a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx b/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx deleted file mode 100644 index 1cfaf1b84c..0000000000 --- a/example/src/Examples/AnimatedFABExample/CustomFABControls.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React from 'react'; -import { FlatList, ListRenderItemInfo, StyleSheet, View } from 'react-native'; - -import { TouchableOpacity } from 'react-native-gesture-handler'; -import type { - AnimatedFABAnimateFrom, - AnimatedFABIconMode, -} from 'react-native-paper'; -import { RadioButton, Text, useTheme } from 'react-native-paper'; -export type Controls = { - iconMode: AnimatedFABIconMode; - animateFrom: AnimatedFABAnimateFrom; -}; - -export const initialControls: Controls = { - iconMode: 'static', - animateFrom: 'right', -}; - -type Props = { - controls: Controls; - setControls(controls: React.SetStateAction): void; -}; - -type ControlValue = AnimatedFABIconMode | AnimatedFABAnimateFrom; - -type CustomControlProps = { - name: string; - options: ControlValue[]; - value: ControlValue; - onChange(newValue: ControlValue): void; -}; - -const CustomControl = ({ - name, - options, - value, - onChange, -}: CustomControlProps) => { - const _renderItem = React.useCallback( - ({ item }: ListRenderItemInfo<(typeof options)[number]>) => { - return ( - onChange(item)} - style={styles.controlItem} - > - {item} - - - - ); - }, - [value, onChange] - ); - - const _keyExtractor = React.useCallback( - (item: (typeof options)[number]) => item, - [] - ); - return ( - - {name} - - - - ); -}; - -const CustomFABControls = ({ - setControls, - controls: { animateFrom, iconMode }, -}: Props) => { - const { colors } = useTheme(); - - const setIconMode = (newIconMode: AnimatedFABIconMode) => - setControls((state) => ({ ...state, iconMode: newIconMode })); - - const setAnimateFrom = (newAnimateFrom: AnimatedFABAnimateFrom) => - setControls((state) => ({ ...state, animateFrom: newAnimateFrom })); - - return ( - - - - - - ); -}; - -export default CustomFABControls; - -const styles = StyleSheet.create({ - controlsWrapper: { - paddingHorizontal: 16, - }, - controlWrapper: { - flexDirection: 'row', - alignItems: 'center', - }, - controlItemsList: { - flex: 1, - justifyContent: 'flex-end', - }, - controlItem: { - marginLeft: 16, - flexDirection: 'row', - alignItems: 'center', - }, -}); diff --git a/example/src/Examples/AnimatedFABExample/index.ts b/example/src/Examples/AnimatedFABExample/index.ts deleted file mode 100644 index 7561c57b50..0000000000 --- a/example/src/Examples/AnimatedFABExample/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './AnimatedFABExample'; diff --git a/example/src/Examples/AppbarExample.tsx b/example/src/Examples/AppbarExample.tsx index 846dd61182..bdb47ad641 100644 --- a/example/src/Examples/AppbarExample.tsx +++ b/example/src/Examples/AppbarExample.tsx @@ -83,8 +83,6 @@ const AppbarExample = () => { const renderFAB = () => { return ( {}} style={[styles.fab, { top: (height - MEDIUM_FAB_HEIGHT) / 2 }]} diff --git a/example/src/Examples/BannerExample.tsx b/example/src/Examples/BannerExample.tsx index 85e7ec986d..78ef19fed4 100644 --- a/example/src/Examples/BannerExample.tsx +++ b/example/src/Examples/BannerExample.tsx @@ -57,12 +57,7 @@ const BannerExample = () => { ))} - setVisible(!visible)} - /> + setVisible(!visible)} /> ; + +const variants: FabColor[] = [ + 'primary', + 'secondary', + 'tertiary', + 'tonalPrimary', + 'tonalSecondary', + 'tonalTertiary', + 'custom', +]; + +const sizes: FABSize[] = ['default', 'medium', 'large']; + +const types: FabType[] = ['icon', 'extended', 'extendedTransforming', 'menu']; + +const positions: FabPosition[] = ['start', 'center', 'end']; + +const rows = Array.from({ length: 40 }, (_, i) => ({ + id: String(i + 1), + text: `Item ${i + 1}`, +})); + +type ChipRowProps = { + label: string; + options: readonly T[]; + value: T; + onChange: (value: T) => void; +}; + +const ChipRow = ({ + label, + options, + value, + onChange, +}: ChipRowProps) => ( + + + {label} + + + {options.map((option) => ( + onChange(option)} + > + {option} + + ))} + + +); const FABExample = () => { - const [visible, setVisible] = React.useState(true); - const [toggleStackOnLongPress, setToggleStackOnLongPress] = - React.useState(false); - const [open, setOpen] = React.useState(false); + const { colors } = useTheme(); + const insets = useSafeAreaInsets(); - const variants = ['primary', 'secondary', 'tertiary', 'surface']; - const sizes = ['small', 'medium', 'large']; - const modes = ['flat', 'elevated']; + const [variant, setVariant] = React.useState('tonalPrimary'); + const activeVariant = variant === 'custom' ? undefined : variant; + const activeContainerColor = + variant === 'custom' ? CUSTOM_CONTAINER_COLOR : undefined; + const [size, setSize] = React.useState('medium'); + const [type, setType] = React.useState('icon'); + const [position, setPosition] = React.useState('end'); + const [showFab, setShowFab] = React.useState(true); + const [transformingExpanded, setTransformingExpanded] = React.useState(true); + const [menuExpanded, setMenuExpanded] = React.useState(false); + const lastScrollY = React.useRef(0); - return ( - - - setVisible(!visible)} - /> - - - {variants.map((variant) => ( - - {}} - visible={visible} - variant={variant as FABVariant} - /> - {visible && {variant}} - - ))} - - - {sizes.map((size) => ( - - {}} - visible={visible} - size={size as FABSize} - /> - {visible && {size}} - - ))} - - - {modes.map((mode) => ( - - {}} - visible={visible} - mode={mode as FABMode} - /> - {visible && {mode}} - - ))} + const fabPadding = 16; + + const renderItem = React.useCallback( + ({ item }: { item: (typeof rows)[number] }) => ( + + {item.text} - - {}} - visible={visible} - /> - {}} - visible={visible} - /> - {}} - visible={visible} + ), + [] + ); + + const onScroll = React.useCallback( + ({ nativeEvent }: NativeSyntheticEvent) => { + const y = nativeEvent.contentOffset.y; + const delta = y - lastScrollY.current; + if (Math.abs(delta) < SCROLL_DELTA_THRESHOLD) { + return; + } + lastScrollY.current = y; + if (y <= 0) { + setTransformingExpanded(true); + } else if (delta > 0) { + setTransformingExpanded(false); + } else { + setTransformingExpanded(true); + } + }, + [] + ); + + return ( + + + - {}} - visible={visible} - uppercase + + + - {}} - visible={visible} - loading + ( + + + + )} + onPress={() => setShowFab((v) => !v)} /> - - {} }, - { icon: 'star', label: 'Star', onPress: () => {} }, - { icon: 'email', label: 'Email', onPress: () => {} }, - { - icon: 'bell', - label: 'Remind', - onPress: () => {}, - size: 'small', - }, - { - icon: toggleStackOnLongPress - ? 'gesture-tap' - : 'gesture-tap-hold', - label: toggleStackOnLongPress - ? 'Toggle on Press' - : 'Toggle on Long Press', - onPress: () => { - setToggleStackOnLongPress(!toggleStackOnLongPress); - }, - }, - ]} - enableLongPressWhenStackOpened - onStateChange={({ open }: { open: boolean }) => setOpen(open)} - onPress={() => { - if (toggleStackOnLongPress) { - isWeb ? alert('Fab is Pressed') : Alert.alert('Fab is Pressed'); - // do something on press when the speed dial is closed - } else if (open) { - isWeb ? alert('Fab is Pressed') : Alert.alert('Fab is Pressed'); - // do something if the speed dial is open - } - }} - onLongPress={() => { - if (!toggleStackOnLongPress || open) { - isWeb - ? alert('Fab is Long Pressed') - : Alert.alert('Fab is Long Pressed'); - // do something if the speed dial is open - } - }} - visible={visible} + + + item.id} + contentContainerStyle={[ + styles.listContent, + { paddingBottom: insets.bottom + fabPadding + 96 }, + ]} + onScroll={type === 'extendedTransforming' ? onScroll : undefined} + scrollEventThrottle={16} + /> + + {type === 'icon' && ( + {}} + /> + )} + {(type === 'extended' || type === 'extendedTransforming') && ( + {}} /> - + )} - + {type === 'menu' ? ( + setMenuExpanded(false)} + alignment={position} + trigger={{ + icon: 'pencil', + variant: activeVariant, + containerColor: activeContainerColor, + size, + visible: showFab, + onPress: () => setMenuExpanded(true), + }} + items={[ + { icon: 'email', label: 'Send', onPress: () => {} }, + { icon: 'bell', label: 'Remind me', onPress: () => {} }, + { icon: 'star', label: 'Favorite', onPress: () => {} }, + ]} + /> + ) : null} + ); }; @@ -170,26 +242,32 @@ FABExample.title = 'Floating Action Button'; const styles = StyleSheet.create({ container: { - padding: 4, + flex: 1, }, - row: { - marginBottom: 8, - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', + controls: { + paddingTop: 8, + paddingBottom: 8, }, - column: { - justifyContent: 'center', - alignItems: 'center', - marginBottom: 8, + chipRow: { + paddingVertical: 4, }, - fab: { - margin: 8, + chipRowLabel: { + paddingHorizontal: 16, + paddingBottom: 6, }, - fabVariant: { - flex: 1, - justifyContent: 'space-between', - alignItems: 'center', + chipRowContent: { + paddingHorizontal: 16, + gap: 8, + }, + listContent: { + paddingHorizontal: 16, + }, + listItem: { + paddingVertical: 12, + }, + fabContainer: { + position: 'absolute', + flexDirection: 'row', }, }); diff --git a/example/src/Examples/TooltipExample.tsx b/example/src/Examples/TooltipExample.tsx index ebe86e74f2..8e0802d4a4 100644 --- a/example/src/Examples/TooltipExample.tsx +++ b/example/src/Examples/TooltipExample.tsx @@ -149,7 +149,7 @@ const TooltipExample = () => { - {}} /> + {}} /> diff --git a/src/components/FAB/AnimatedFAB.tsx b/src/components/FAB/AnimatedFAB.tsx deleted file mode 100644 index 5ca2348614..0000000000 --- a/src/components/FAB/AnimatedFAB.tsx +++ /dev/null @@ -1,594 +0,0 @@ -import * as React from 'react'; -import type { - AccessibilityState, - NativeSyntheticEvent, - PressableAndroidRippleConfig, - TextLayoutEventData, -} from 'react-native'; -import { - Animated, - Easing, - GestureResponderEvent, - Platform, - ScrollView, - StyleProp, - StyleSheet, - View, - ViewStyle, - Text, -} from 'react-native'; - -import { getCombinedStyles, getFABColors, getLabelSizeWeb } from './utils'; -import { useLocale } from '../../core/locale'; -import { useInternalTheme } from '../../core/theming'; -import type { $Omit, $RemoveChildren, ThemeProp } from '../../types'; -import type { IconSource } from '../Icon'; -import Icon from '../Icon'; -import Surface from '../Surface'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; -import AnimatedText from '../Typography/AnimatedText'; - -export type AnimatedFABIconMode = 'static' | 'dynamic'; -export type AnimatedFABAnimateFrom = 'left' | 'right'; - -export type Props = $Omit<$RemoveChildren, 'mode'> & { - /** - * Icon to display for the `FAB`. - */ - icon: IconSource; - /** - * Label for extended `FAB`. - */ - label: string; - /** - * Make the label text uppercased. - */ - uppercase?: boolean; - /** - * Type of background drawabale to display the feedback (Android). - * https://reactnative.dev/docs/pressable#rippleconfig - */ - background?: PressableAndroidRippleConfig; - /** - * Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB. - * Uses `label` by default if specified. - */ - accessibilityLabel?: string; - /** - * Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB. - */ - accessibilityState?: AccessibilityState; - /** - * Custom color for the icon and label of the `FAB`. - */ - color?: string; - /** - * Whether `FAB` is currently visible. - */ - visible?: boolean; - /** - * Function to execute on press. - */ - onPress?: (e: GestureResponderEvent) => void; - /** - * Function to execute on long press. - */ - onLongPress?: (e: GestureResponderEvent) => void; - /** - * The number of milliseconds a user must touch the element before executing `onLongPress`. - */ - delayLongPress?: number; - /** - * Whether icon should be translated to the end of extended `FAB` or be static and stay in the same place. The default value is `dynamic`. - */ - iconMode?: AnimatedFABIconMode; - /** - * Indicates from which direction animation should be performed. The default value is `right`. - */ - animateFrom?: AnimatedFABAnimateFrom; - /** - * Whether `FAB` should start animation to extend. - */ - extended: boolean; - /** - * Specifies the largest possible scale a label font can reach. - */ - labelMaxFontSizeMultiplier?: number; - /** - * @supported Available in v5.x with theme version 3 - * - * Color mappings variant for combinations of container and icon colors. - */ - variant?: 'primary' | 'secondary' | 'tertiary' | 'surface'; - style?: Animated.WithAnimatedValue>; - /** - * Sets additional distance outside of element in which a press can be detected. - */ - hitSlop?: TouchableRippleProps['hitSlop']; - /** - * @optional - */ - theme?: ThemeProp; - /** - * TestID used for testing purposes - */ - testID?: string; -}; - -const SIZE = 56; -const SHADOW_LAYER_Z_INDEX = 1; -const CONTENT_LAYER_Z_INDEX = 2; - -/** - * An animated, extending horizontally floating action button represents the primary action in an application. - * - * ## Usage - * ```js - * import React from 'react'; - * import { - * StyleProp, - * ViewStyle, - * Animated, - * StyleSheet, - * Platform, - * ScrollView, - * Text, - * SafeAreaView, - * I18nManager, - * } from 'react-native'; - * import { AnimatedFAB } from 'react-native-paper'; - * - * const MyComponent = ({ - * animatedValue, - * visible, - * extended, - * label, - * animateFrom, - * style, - * iconMode, - * }) => { - * const [isExtended, setIsExtended] = React.useState(true); - * - * const isIOS = Platform.OS === 'ios'; - * - * const onScroll = ({ nativeEvent }) => { - * const currentScrollPosition = - * Math.floor(nativeEvent?.contentOffset?.y) ?? 0; - * - * setIsExtended(currentScrollPosition <= 0); - * }; - * - * const fabStyle = { [animateFrom]: 16 }; - * - * return ( - * - * - * {[...new Array(100).keys()].map((_, i) => ( - * {i} - * ))} - * - * console.log('Pressed')} - * visible={visible} - * animateFrom={'right'} - * iconMode={'static'} - * style={[styles.fabStyle, style, fabStyle]} - * /> - * - * ); - * }; - * - * export default MyComponent; - * - * const styles = StyleSheet.create({ - * container: { - * flexGrow: 1, - * }, - * fabStyle: { - * bottom: 16, - * right: 16, - * position: 'absolute', - * }, - * }); - * ``` - */ -const AnimatedFAB = ({ - icon, - label, - background, - accessibilityLabel = label, - accessibilityState, - color: customColor, - onPress, - onLongPress, - delayLongPress, - theme: themeOverrides, - style, - visible = true, - uppercase: uppercaseProp, - testID = 'animated-fab', - animateFrom = 'right', - extended = false, - iconMode = 'dynamic', - variant = 'primary', - labelMaxFontSizeMultiplier, - hitSlop, - ...rest -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const { direction } = useLocale(); - const uppercase: boolean = uppercaseProp ?? false; - const isIOS = Platform.OS === 'ios'; - const isWeb = Platform.OS === 'web'; - const isAnimatedFromRight = animateFrom === 'right'; - const isIconStatic = iconMode === 'static'; - const isRTL = direction === 'rtl'; - const labelRef = React.useRef(null); - const { current: visibility } = React.useRef( - new Animated.Value(visible ? 1 : 0) - ); - const { current: animFAB } = React.useRef( - new Animated.Value(0) - ); - const { animation } = theme; - const { scale } = animation; - - const labelSize = isWeb ? getLabelSizeWeb(labelRef) : null; - const [textWidth, setTextWidth] = React.useState( - labelSize?.width ?? 0 - ); - const [textHeight, setTextHeight] = React.useState( - labelSize?.height ?? 0 - ); - - const borderRadius = SIZE / 3.5; - - React.useEffect(() => { - if (!isWeb) { - return; - } - - const updateTextSize = () => { - if (labelRef.current) { - const labelSize = getLabelSizeWeb(labelRef); - - if (labelSize) { - setTextHeight(labelSize.height ?? 0); - setTextWidth(labelSize.width ?? 0); - } - } - }; - - updateTextSize(); - window.addEventListener('resize', updateTextSize); - - return () => { - if (!isWeb) { - return; - } - - window.removeEventListener('resize', updateTextSize); - }; - }, [isWeb]); - - React.useEffect(() => { - if (visible) { - Animated.timing(visibility, { - toValue: 1, - duration: 200 * scale, - useNativeDriver: true, - }).start(); - } else { - Animated.timing(visibility, { - toValue: 0, - duration: 150 * scale, - useNativeDriver: true, - }).start(); - } - }, [visible, scale, visibility]); - - const { backgroundColor: customBackgroundColor, ...restStyle } = - (StyleSheet.flatten(style) || {}) as ViewStyle; - - const { backgroundColor, foregroundColor } = getFABColors({ - theme, - variant, - customColor, - customBackgroundColor, - }); - - const extendedWidth = textWidth + SIZE + borderRadius; - - const distance = isAnimatedFromRight - ? -textWidth - borderRadius - : textWidth + borderRadius; - - React.useEffect(() => { - Animated.timing(animFAB, { - toValue: !extended ? 0 : distance, - duration: 150 * scale, - useNativeDriver: true, - easing: Easing.linear, - }).start(); - }, [animFAB, scale, distance, extended]); - - const onTextLayout = ({ - nativeEvent, - }: NativeSyntheticEvent) => { - const currentWidth = Math.ceil(nativeEvent.lines[0]?.width ?? 0); - const currentHeight = Math.ceil(nativeEvent.lines[0]?.height ?? 0); - - if (currentWidth !== textWidth || currentHeight !== textHeight) { - setTextHeight(currentHeight); - - if (isIOS) { - return setTextWidth(currentWidth - 12); - } - - setTextWidth(currentWidth); - } - }; - - const propForDirection = (right: T[]): T[] => { - if (isAnimatedFromRight) { - return right; - } - - return right.reverse(); - }; - - const combinedStyles = getCombinedStyles({ - isAnimatedFromRight, - isIconStatic, - isRTL, - distance, - animFAB, - }); - - const font = theme.fonts.labelLarge; - - const textStyle = { - color: foregroundColor, - ...font, - }; - - const md3Elevation = !isIOS ? 0 : 3; - - const shadowStyle = styles.shadow; - const baseStyle = [StyleSheet.absoluteFill, shadowStyle]; - - const newAccessibilityState = { ...accessibilityState }; - - return ( - - - - - - - - - - - - - - - - - - - - - - {label} - - - - {!isIOS && ( - // Method `onTextLayout` on Android returns sizes of text visible on the screen, - // however during render the text in `FAB` isn't fully visible. In order to get - // proper text measurements there is a need to additionaly render that text, but - // wrapped in absolutely positioned `ScrollView` which height is 0. - - - {label} - - - )} - - ); -}; - -const styles = StyleSheet.create({ - standard: { - height: SIZE, - }, - // eslint-disable-next-line react-native/no-color-literals - container: { - position: 'absolute', - backgroundColor: 'transparent', - }, - innerWrapper: { - flexDirection: 'row', - overflow: 'hidden', - ...Platform.select({ - android: { - zIndex: CONTENT_LAYER_Z_INDEX, - }, - }), - }, - shadowWrapper: { - elevation: 0, - ...Platform.select({ - android: { - zIndex: SHADOW_LAYER_Z_INDEX, - }, - }), - }, - shadow: { - elevation: 3, - }, - iconWrapper: { - alignItems: 'center', - justifyContent: 'center', - position: 'absolute', - height: SIZE, - width: SIZE, - }, - label: { - position: 'absolute', - }, - uppercaseLabel: { - textTransform: 'uppercase', - }, - textPlaceholderContainer: { - height: 0, - position: 'absolute', - }, -}); - -export default AnimatedFAB; diff --git a/src/components/FAB/Content.tsx b/src/components/FAB/Content.tsx new file mode 100644 index 0000000000..d7dc81a9cb --- /dev/null +++ b/src/components/FAB/Content.tsx @@ -0,0 +1,118 @@ +import * as React from 'react'; +import { + ColorValue, + StyleProp, + StyleSheet, + View, + ViewStyle, +} from 'react-native'; + +import Reanimated, { AnimatedStyle } from 'react-native-reanimated'; + +import type { TypescaleKey } from '../../theme/types'; +import Icon, { IconSource } from '../Icon'; +import AnimatedText from '../Typography/AnimatedText'; + +export type ContentProps = { + icon?: IconSource; + label?: string; + contentColor: ColorValue; + height: number; + iconSize: number; + leading: number; + trailing: number; + iconLabelGap: number; + labelTypescale?: TypescaleKey; + labelMaxFontSizeMultiplier?: number; + /** + * Reanimated style merged onto the label wrapper. Used by the Extended FAB + * to fade the label in and out as the FAB expands and collapses. + */ + labelAnimatedStyle?: StyleProp>; + labelNumberOfLines?: number; + labelEllipsisMode?: 'clip' | 'tail' | 'head' | 'middle'; + testID?: string; +}; + +/** + * Internal layout primitive: an icon-and-label row used by every FAB-flavored + * surface in this package (regular, Extended, Menu trigger, Menu item). + * + * No animation, no ripple, no shadow, no container shape. Just the content. + */ +const Content = ({ + icon, + label, + contentColor, + height, + iconSize, + leading, + trailing, + iconLabelGap, + labelTypescale = 'labelLarge', + labelMaxFontSizeMultiplier, + labelAnimatedStyle, + labelNumberOfLines, + labelEllipsisMode, + testID, +}: ContentProps) => { + const hasLabel = label !== undefined && label !== ''; + const colorStyle = { color: contentColor }; + + return ( + <> + + {icon ? ( + + ) : null} + {hasLabel ? ( + + + {label} + + + ) : null} + + + ); +}; + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + alignItems: 'center', + pointerEvents: 'none', + }, + rowIconOnly: { + justifyContent: 'center', + }, + labelNoPointerEvents: { + pointerEvents: 'none', + }, +}); + +export default Content; diff --git a/src/components/FAB/Extended.tsx b/src/components/FAB/Extended.tsx new file mode 100644 index 0000000000..d10d624596 --- /dev/null +++ b/src/components/FAB/Extended.tsx @@ -0,0 +1,288 @@ +import * as React from 'react'; +import { + AccessibilityState, + ColorValue, + GestureResponderEvent, + PressableAndroidRippleConfig, + StyleProp, + StyleSheet, + View, + ViewStyle, +} from 'react-native'; + +import Reanimated, { + measure, + useAnimatedRef, + useAnimatedStyle, + useSharedValue, + withSpring, +} from 'react-native-reanimated'; +import { scheduleOnUI } from 'react-native-worklets'; + +import Shell from './Shell'; +import { Size, Variant } from './tokens'; +import { getDimensions } from './utils'; +import { useInternalTheme } from '../../core/theming'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; +import { toRawSpring } from '../../theme/tokens/sys/motion'; +import type { ThemeProp } from '../../types'; +import { forwardRef } from '../../utils/forwardRef'; +import type { IconSource } from '../Icon'; +import AnimatedText from '../Typography/AnimatedText'; + +export type Props = { + /** + * Icon to display inside the FAB. + */ + icon: IconSource; + /** + * Label rendered next to the icon when expanded. + */ + label: string; + /** + * Role-color preset. Defaults to `tonalPrimary`. + */ + variant?: Variant; + /** + * Override the container (background) color. When set without `contentColor`, + * the icon and label colors are derived automatically via `contentColorFor`. + */ + containerColor?: ColorValue; + /** + * Override the content (icon + label) color. + */ + contentColor?: ColorValue; + /** + * Spec size. Defaults to `default`. + */ + size?: Size; + /** + * Whether the FAB is expanded (icon + label) or collapsed (icon only). The + * width and label opacity animate per the MD3 Expressive spec on change. + */ + expanded: boolean; + /** + * Whether the FAB is currently visible. Toggling animates the spec'd enter + * and exit (scale + alpha) on the FAB itself. + */ + visible?: boolean; + /** + * Function to execute on press. + */ + onPress?: (e: GestureResponderEvent) => void; + /** + * Accessibility label. Falls back to `label` if unset. + */ + accessibilityLabel?: string; + /** + * Accessibility state forwarded to the underlying button. + */ + accessibilityState?: AccessibilityState; + /** + * Specifies the largest possible scale a label font can reach. + */ + labelMaxFontSizeMultiplier?: number; + /** + * Type of background drawable to display the feedback (Android). + * https://reactnative.dev/docs/pressable#rippleconfig + */ + background?: PressableAndroidRippleConfig; + /** + * Style for positioning the FAB. The visual treatment (size, shape, color) + * is driven by `variant` and `size`. + */ + style?: StyleProp; + /** + * TestID used for testing purposes. + */ + testID?: string; + /** + * @optional + */ + theme?: ThemeProp; + ref?: React.RefObject; +}; + +/** + * An extended floating action button represents the primary action on a screen + * and shows a label next to the icon. Animates between expanded (icon + label) + * and collapsed (icon only) states. + * + * ## Usage + * ```js + * import * as React from 'react'; + * import { StyleSheet } from 'react-native'; + * import { FAB } from 'react-native-paper'; + * + * const MyComponent = () => { + * const [expanded, setExpanded] = React.useState(true); + * + * return ( + * setExpanded((v) => !v)} + * style={styles.fab} + * /> + * ); + * }; + * + * const styles = StyleSheet.create({ + * fab: { + * position: 'absolute', + * margin: 16, + * left: 0, + * bottom: 0, + * }, + * }); + * + * export default MyComponent; + * ``` + */ +const Extended = forwardRef( + ( + { + icon, + label, + variant = 'tonalPrimary', + containerColor, + contentColor, + size = 'default', + expanded, + visible = true, + onPress, + accessibilityLabel = label, + accessibilityState, + labelMaxFontSizeMultiplier, + background, + style, + testID = 'extended-floating-action-button', + theme: themeOverrides, + }, + ref + ) => { + const theme = useInternalTheme(themeOverrides); + const reduceMotion = useReduceMotion(); + + const dimensions = getDimensions({ theme, size }); + + const offscreenLabelRef = useAnimatedRef(); + + const widthValue = useSharedValue(dimensions.width); + const labelOpacity = useSharedValue(expanded ? 1 : 0); + + React.useEffect(() => { + const { + width: collapsedWidth, + leading, + iconSize, + iconLabelGap, + trailing, + } = dimensions; + const targetOpacity = expanded ? 1 : 0; + + if (reduceMotion) { + scheduleOnUI(() => { + 'worklet'; + const m = measure(offscreenLabelRef); + const lw = m?.width ?? 0; + widthValue.value = expanded + ? leading + iconSize + iconLabelGap + lw + trailing + : collapsedWidth; + labelOpacity.value = targetOpacity; + }); + return; + } + + const widthSpring = toRawSpring( + expanded + ? theme.motion.spring.fast.spatial + : theme.motion.spring.default.spatial + ); + const opacitySpring = toRawSpring( + expanded + ? theme.motion.spring.default.effects + : theme.motion.spring.fast.effects + ); + + scheduleOnUI(() => { + 'worklet'; + const m = measure(offscreenLabelRef); + const lw = m?.width ?? 0; + const expandedWidth = leading + iconSize + iconLabelGap + lw + trailing; + widthValue.value = withSpring( + expanded ? expandedWidth : collapsedWidth, + widthSpring + ); + labelOpacity.value = withSpring(targetOpacity, opacitySpring); + }); + }, [ + expanded, + label, + dimensions, + theme, + reduceMotion, + widthValue, + labelOpacity, + offscreenLabelRef, + ]); + + const labelAnimatedStyle = useAnimatedStyle(() => ({ + opacity: labelOpacity.value, + })); + + return ( + <> + + + + {label} + + + + ); + } +); + +const styles = StyleSheet.create({ + offscreenMeasure: { + position: 'absolute', + alignSelf: 'flex-start', + opacity: 0, + pointerEvents: 'none', + }, +}); + +export default Extended; + +// @component-docs ignore-next-line +export { Extended }; diff --git a/src/components/FAB/FAB.tsx b/src/components/FAB/FAB.tsx index 1926449f28..2a897ddbb9 100644 --- a/src/components/FAB/FAB.tsx +++ b/src/components/FAB/FAB.tsx @@ -1,143 +1,81 @@ import * as React from 'react'; import { AccessibilityState, - Animated, ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, View, ViewStyle, } from 'react-native'; -import { getExtendedFabStyle, getFABColors, getFabStyle } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { $Omit, $RemoveChildren, ThemeProp } from '../../types'; +import Shell from './Shell'; +import { Size, Variant } from './tokens'; +import type { ThemeProp } from '../../types'; import { forwardRef } from '../../utils/forwardRef'; -import ActivityIndicator from '../ActivityIndicator'; -import CrossFadeIcon from '../CrossFadeIcon'; -import Icon, { IconSource } from '../Icon'; -import Surface from '../Surface'; -import TouchableRipple from '../TouchableRipple/TouchableRipple'; -import Text from '../Typography/Text'; +import type { IconSource } from '../Icon'; -type FABSize = 'small' | 'medium' | 'large'; - -type FABMode = 'flat' | 'elevated'; - -type IconOrLabel = - | { - icon: IconSource; - label?: string; - } - | { - icon?: IconSource; - label: string; - }; - -export type Props = $Omit<$RemoveChildren, 'mode'> & { - // For `icon` and `label` props their types are duplicated due to the generation of documentation. - // Appropriate type for them is `IconOrLabel` contains the both union and intersection types. - /** - * Icon to display for the `FAB`. It's optional only if `label` is defined. - */ - icon?: IconSource; - /** - * Optional label for extended `FAB`. It's optional only if `icon` is defined. - */ - label?: string; +export type Props = { /** - * Make the label text uppercased. + * Icon to display inside the FAB. */ - uppercase?: boolean; + icon: IconSource; /** - * Type of background drawabale to display the feedback (Android). - * https://reactnative.dev/docs/pressable#rippleconfig + * Role-color preset. Defaults to `tonalPrimary`. */ - background?: PressableAndroidRippleConfig; + variant?: Variant; /** - * Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB. - * Uses `label` by default if specified. + * Override the container (background) color. */ - accessibilityLabel?: string; + containerColor?: ColorValue; /** - * Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB. + * Override the content (icon) color. */ - accessibilityState?: AccessibilityState; + contentColor?: ColorValue; /** - * Whether an icon change is animated. + * Spec size. Defaults to `default`. */ - animated?: boolean; + size?: Size; /** - * Custom color for the icon and label of the `FAB`. - */ - color?: ColorValue; - /** - * Whether `FAB` is currently visible. + * Whether the FAB is currently visible. Toggling animates the spec'd enter + * and exit (scale + alpha) on the FAB itself. */ visible?: boolean; - /** - * Whether to show a loading indicator. - */ - loading?: boolean; /** * Function to execute on press. */ onPress?: (e: GestureResponderEvent) => void; /** - * Function to execute on long press. + * Accessibility label. Falls back to nothing if unset. */ - onLongPress?: (e: GestureResponderEvent) => void; - /** - * The number of milliseconds a user must touch the element before executing `onLongPress`. - */ - delayLongPress?: number; - /** - * @supported Available in v5.x with theme version 3 - * - * Size of the `FAB`. - * - `small` - FAB with small height (40). - * - `medium` - FAB with default medium height (56). - * - `large` - FAB with large height (96). - */ - size?: FABSize; + accessibilityLabel?: string; /** - * Custom size for the `FAB`. This prop takes precedence over size prop + * Accessibility state forwarded to the underlying button. */ - customSize?: number; + accessibilityState?: AccessibilityState; /** - * @supported Available in v5.x with theme version 3 - * - * Mode of the `FAB`. You can change the mode to adjust the the shadow: - * - `flat` - button without a shadow. - * - `elevated` - button with a shadow. + * Type of background drawable to display the feedback (Android). + * https://reactnative.dev/docs/pressable#rippleconfig */ - mode?: FABMode; + background?: PressableAndroidRippleConfig; /** - * @supported Available in v5.x with theme version 3 - * - * Color mappings variant for combinations of container and icon colors. + * Style for positioning the FAB. The visual treatment (size, shape, color) + * is driven by `variant` and `size`. */ - variant?: 'primary' | 'secondary' | 'tertiary' | 'surface'; + style?: StyleProp; /** - * Specifies the largest possible scale a label font can reach. + * TestID used for testing purposes. */ - labelMaxFontSizeMultiplier?: number; - style?: Animated.WithAnimatedValue>; + testID?: string; /** * @optional */ theme?: ThemeProp; - /** - * TestID used for testing purposes - */ - testID?: string; ref?: React.RefObject; -} & IconOrLabel; +}; /** - * A floating action button represents the primary action on a screen. It appears in front of all screen content. + * A floating action button represents the primary action on a screen. * * ## Usage * ```js @@ -160,7 +98,7 @@ export type Props = $Omit<$RemoveChildren, 'mode'> & { * right: 0, * bottom: 0, * }, - * }) + * }); * * export default MyComponent; * ``` @@ -169,173 +107,40 @@ const FAB = forwardRef( ( { icon, - label, - background, - accessibilityLabel = label, - accessibilityState, - animated = true, - color: customColor, + variant = 'tonalPrimary', + size = 'default', + visible = true, onPress, - onLongPress, - delayLongPress, - theme: themeOverrides, + containerColor, + contentColor, + accessibilityLabel, + accessibilityState, + background, style, - visible = true, - uppercase: uppercaseProp, - loading, - testID = 'fab', - size = 'medium', - customSize, - mode = 'elevated', - variant = 'primary', - labelMaxFontSizeMultiplier, - ...rest - }: Props, - ref - ) => { - const theme = useInternalTheme(themeOverrides); - const uppercase = uppercaseProp ?? false; - const { current: visibility } = React.useRef( - new Animated.Value(visible ? 1 : 0) - ); - const { animation } = theme; - const { scale } = animation; - - React.useEffect(() => { - if (visible) { - Animated.timing(visibility, { - toValue: 1, - duration: 200 * scale, - useNativeDriver: true, - }).start(); - } else { - Animated.timing(visibility, { - toValue: 0, - duration: 150 * scale, - useNativeDriver: true, - }).start(); - } - }, [visible, scale, visibility]); - - const IconComponent = animated ? CrossFadeIcon : Icon; - - const fabStyle = getFabStyle({ customSize, size, theme }); - - const { - borderRadius = fabStyle.borderRadius, - backgroundColor: customBackgroundColor, - } = (StyleSheet.flatten(style) || {}) as ViewStyle; - - const { backgroundColor, foregroundColor } = getFABColors({ + testID = 'floating-action-button', theme, - variant, - customColor, - customBackgroundColor, - }); - - const isLargeSize = size === 'large'; - const isFlatMode = mode === 'flat'; - const iconSize = isLargeSize ? 36 : 24; - const loadingIndicatorSize = isLargeSize ? 24 : 18; - const font = theme.fonts.labelLarge; - - const extendedStyle = getExtendedFabStyle({ customSize, theme }); - const textStyle = { - color: foregroundColor, - ...font, - }; - - const md3Elevation = isFlatMode ? 0 : 3; - - return ( - - - - {icon && loading !== true ? ( - - ) : null} - {loading ? ( - - ) : null} - {label ? ( - - {label} - - ) : null} - - - - ); - } + }, + ref + ) => ( + + ) ); -const styles = StyleSheet.create({ - content: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - label: { - marginHorizontal: 8, - }, - uppercaseLabel: { - textTransform: 'uppercase', - }, -}); - export default FAB; // @component-docs ignore-next-line diff --git a/src/components/FAB/FABGroup.tsx b/src/components/FAB/FABGroup.tsx deleted file mode 100644 index 1123da16ae..0000000000 --- a/src/components/FAB/FABGroup.tsx +++ /dev/null @@ -1,533 +0,0 @@ -import * as React from 'react'; -import { - Animated, - GestureResponderEvent, - Pressable, - StyleProp, - StyleSheet, - TextStyle, - View, - ViewStyle, -} from 'react-native'; - -import { useSafeAreaInsets } from 'react-native-safe-area-context'; - -import FAB from './FAB'; -import { getFABGroupColors } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; -import Card from '../Card/Card'; -import type { IconSource } from '../Icon'; -import Text from '../Typography/Text'; - -const AnimatedPressable = Animated.createAnimatedComponent(Pressable); - -export type Props = { - /** - * Action items to display in the form of a speed dial. - * An action item should contain the following properties: - * - `icon`: icon to display (required) - * - `label`: optional label text - * - `color`: custom icon color of the action item - * - `labelTextColor`: custom label text color of the action item - * - `accessibilityLabel`: accessibility label for the action, uses label by default if specified - * - `accessibilityHint`: accessibility hint for the action - * - `style`: pass additional styles for the fab item, for example, `backgroundColor` - * - `containerStyle`: pass additional styles for the fab item label container, for example, `backgroundColor` @supported Available in 5.x - * - `wrapperStyle`: pass additional styles for the wrapper of the action item. - * - `labelStyle`: pass additional styles for the fab item label, for example, `fontSize` - * - `labelMaxFontSizeMultiplier`: specifies the largest possible scale a title font can reach. - * - `onPress`: callback that is called when `FAB` is pressed (required) - * - `onLongPress`: callback that is called when `FAB` is long pressed - * - `toggleStackOnLongPress`: callback that is called when `FAB` is long pressed - * - `size`: size of action item. Defaults to `small`. @supported Available in v5.x - * - `testID`: testID to be used on tests - */ - actions: Array<{ - icon: IconSource; - label?: string; - color?: string; - labelTextColor?: string; - accessibilityLabel?: string; - accessibilityHint?: string; - style?: Animated.WithAnimatedValue>; - containerStyle?: Animated.WithAnimatedValue>; - wrapperStyle?: StyleProp; - labelStyle?: StyleProp; - labelMaxFontSizeMultiplier?: number; - onPress: (e: GestureResponderEvent) => void; - size?: 'small' | 'medium'; - testID?: string; - }>; - /** - * Icon to display for the `FAB`. - * You can toggle it based on whether the speed dial is open to display a different icon. - */ - icon: IconSource; - /** - * Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB. - */ - accessibilityLabel?: string; - /** - * Custom color for the `FAB`. - */ - color?: string; - /** - * Custom backdrop color for opened speed dial background. - */ - backdropColor?: string; - /** - * Function to execute on pressing the `FAB`. - */ - onPress?: (e: GestureResponderEvent) => void; - /** - * Function to execute on long pressing the `FAB`. - */ - onLongPress?: (e: GestureResponderEvent) => void; - /** - * Makes actions stack appear on long press instead of on press. - */ - toggleStackOnLongPress?: boolean; - /** - * Changes the delay for long press reaction. - */ - delayLongPress?: number; - /** - * Allows for onLongPress when stack is opened. - */ - enableLongPressWhenStackOpened?: boolean; - /** - * Whether the speed dial is open. - */ - open: boolean; - /** - * Callback which is called on opening and closing the speed dial. - * The open state needs to be updated when it's called, otherwise the change is dropped. - */ - onStateChange: (state: { open: boolean }) => void; - /** - * Whether `FAB` is currently visible. - */ - visible: boolean; - /** - * Style for the group. You can use it to pass additional styles if you need. - * For example, you can set an additional padding if you have a tab bar at the bottom. - */ - style?: StyleProp; - /** - * Style for the FAB. It allows to pass the FAB button styles, such as backgroundColor. - */ - fabStyle?: Animated.WithAnimatedValue>; - /** - * @supported Available in v5.x with theme version 3 - * - * Color mappings variant for combinations of container and icon colors. - */ - variant?: 'primary' | 'secondary' | 'tertiary' | 'surface'; - /** - * @optional - */ - theme?: ThemeProp; - /** - * Optional label for `FAB`. - */ - label?: string; - /** - * Pass down testID from Group props to FAB. - */ - testID?: string; -}; - -/** - * A component to display a stack of FABs with related actions in a speed dial. - * To render the group above other components, you'll need to wrap it with the [`Portal`](../Portal) component. - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { FAB, Portal, PaperProvider } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [state, setState] = React.useState({ open: false }); - * - * const onStateChange = ({ open }) => setState({ open }); - * - * const { open } = state; - * - * return ( - * - * - * console.log('Pressed add') }, - * { - * icon: 'star', - * label: 'Star', - * onPress: () => console.log('Pressed star'), - * }, - * { - * icon: 'email', - * label: 'Email', - * onPress: () => console.log('Pressed email'), - * }, - * { - * icon: 'bell', - * label: 'Remind', - * onPress: () => console.log('Pressed notifications'), - * }, - * ]} - * onStateChange={onStateChange} - * onPress={() => { - * if (open) { - * // do something if the speed dial is open - * } - * }} - * /> - * - * - * ); - * }; - * - * export default MyComponent; - * ``` - */ -const FABGroup = ({ - actions, - icon, - open, - onPress, - onLongPress, - toggleStackOnLongPress, - accessibilityLabel, - theme: themeOverrides, - style, - fabStyle, - visible, - label, - testID, - onStateChange, - color: colorProp, - delayLongPress = 200, - variant = 'primary', - enableLongPressWhenStackOpened = false, - backdropColor: customBackdropColor, -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const { top, bottom, right, left } = useSafeAreaInsets(); - - const { current: backdrop } = React.useRef( - new Animated.Value(0) - ); - const animations = React.useRef( - actions.map(() => new Animated.Value(open ? 1 : 0)) - ); - - const [isClosingAnimationFinished, setIsClosingAnimationFinished] = - React.useState(false); - - const [prevActions, setPrevActions] = React.useState< - | { - icon: IconSource; - label?: string; - color?: string; - accessibilityLabel?: string; - style?: Animated.WithAnimatedValue>; - onPress: (e: GestureResponderEvent) => void; - testID?: string; - }[] - | null - >(null); - - const { scale } = theme.animation; - - React.useEffect(() => { - if (open) { - setIsClosingAnimationFinished(false); - Animated.parallel([ - Animated.timing(backdrop, { - toValue: 1, - duration: 250 * scale, - useNativeDriver: true, - }), - Animated.stagger( - 15, - animations.current - .map((animation) => - Animated.timing(animation, { - toValue: 1, - duration: 150 * scale, - useNativeDriver: true, - }) - ) - .reverse() - ), - ]).start(); - } else { - Animated.parallel([ - Animated.timing(backdrop, { - toValue: 0, - duration: 200 * scale, - useNativeDriver: true, - }), - ...animations.current.map((animation) => - Animated.timing(animation, { - toValue: 0, - duration: 150 * scale, - useNativeDriver: true, - }) - ), - ]).start(({ finished }) => { - if (finished) { - setIsClosingAnimationFinished(true); - } - }); - } - }, [open, actions, backdrop, scale]); - - const close = () => onStateChange({ open: false }); - const toggle = () => onStateChange({ open: !open }); - - const handlePress = (e: GestureResponderEvent) => { - onPress?.(e); - if (!toggleStackOnLongPress || open) { - toggle(); - } - }; - - const handleLongPress = (e: GestureResponderEvent) => { - if (!open || enableLongPressWhenStackOpened) { - onLongPress?.(e); - if (toggleStackOnLongPress) { - toggle(); - } - } - }; - - const { - labelColor, - backdropColor, - backdropOpacity: backdropMaxOpacity, - stackedFABBackgroundColor, - } = getFABGroupColors({ theme, customBackdropColor }); - - const backdropOpacity = open - ? backdrop.interpolate({ - inputRange: [0, 0.5, 1], - outputRange: [0, backdropMaxOpacity, backdropMaxOpacity], - }) - : Animated.multiply(backdrop, backdropMaxOpacity); - - const opacities = animations.current; - - const translations = opacities.map((opacity) => - open - ? opacity.interpolate({ - inputRange: [0, 1], - outputRange: [24, -8], - }) - : -8 - ); - const labelTranslations = opacities.map((opacity) => - open - ? opacity.interpolate({ - inputRange: [0, 1], - outputRange: [8, -8], - }) - : -8 - ); - - const containerPaddings = { - paddingBottom: bottom, - paddingRight: right, - paddingLeft: left, - paddingTop: top, - }; - - const actionsContainerVisibility: ViewStyle = { - display: isClosingAnimationFinished ? 'none' : 'flex', - }; - - if (actions.length !== prevActions?.length) { - animations.current = actions.map( - (_, i) => animations.current[i] || new Animated.Value(open ? 1 : 0) - ); - setPrevActions(actions); - } - - return ( - - - - - {actions.map((it, i) => { - const labelTextStyle = { - color: it.labelTextColor ?? labelColor, - ...theme.fonts.titleMedium, - }; - const marginHorizontal = - typeof it.size === 'undefined' || it.size === 'small' ? 24 : 16; - const accessibilityLabel = - typeof it.accessibilityLabel !== 'undefined' - ? it.accessibilityLabel - : it.label; - const size = typeof it.size !== 'undefined' ? it.size : 'small'; - - const handleActionPress = (e: GestureResponderEvent) => { - it.onPress(e); - close(); - }; - - return ( - - {it.label && ( - - - - {it.label} - - - - )} - - - ); - })} - - - - - ); -}; - -FABGroup.displayName = 'FAB.Group'; - -export default FABGroup; - -// @component-docs ignore-next-line -export { FABGroup }; - -const styles = StyleSheet.create({ - safeArea: { - alignItems: 'flex-end', - }, - container: { - ...StyleSheet.absoluteFill, - justifyContent: 'flex-end', - }, - fab: { - marginHorizontal: 16, - marginBottom: 16, - marginTop: 0, - }, - backdrop: { - ...StyleSheet.absoluteFill, - }, - containerStyle: { - borderRadius: 5, - paddingHorizontal: 12, - paddingVertical: 6, - marginVertical: 8, - marginHorizontal: 16, - elevation: 2, - }, - item: { - marginBottom: 16, - flexDirection: 'row', - justifyContent: 'flex-end', - alignItems: 'center', - }, - // eslint-disable-next-line react-native/no-color-literals - v3ContainerStyle: { - backgroundColor: 'transparent', - elevation: 0, - }, -}); diff --git a/src/components/FAB/Menu.tsx b/src/components/FAB/Menu.tsx new file mode 100644 index 0000000000..0af09b5eb5 --- /dev/null +++ b/src/components/FAB/Menu.tsx @@ -0,0 +1,728 @@ +import * as React from 'react'; +import { + ColorValue, + GestureResponderEvent, + Platform, + StyleSheet, + View, +} from 'react-native'; + +import Animated, { + interpolate, + useAnimatedStyle, + useDerivedValue, + useSharedValue, + withDelay, + withSpring, +} from 'react-native-reanimated'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +import Content from './Content'; +import Shell from './Shell'; +import { + MenuTokens, + Size, + Tokens, + Variant, + FOCUS_RING_INSET, + FOCUS_RING_THICKNESS, + webNoOutline, +} from './tokens'; +import { useFocusRing } from './useFocusRing'; +import { resolveColors } from './utils'; +import { useLocale } from '../../core/locale'; +import { useInternalTheme } from '../../core/theming'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; +import { toRawSpring } from '../../theme/tokens/sys/motion'; +import { resolveCornerRadius } from '../../theme/utils/shape'; +import type { InternalTheme, ThemeProp } from '../../types'; +import Icon, { IconSource } from '../Icon'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; + +export type MenuItemProps = { + /** + * Optional icon for the item. + */ + icon?: IconSource; + /** + * Mandatory label. + */ + label: string; + /** + * Called when the item is pressed. The menu is dismissed automatically + * after `onPress` runs. + */ + onPress: (e: GestureResponderEvent) => void; + /** + * Accessibility label. Falls back to `label`. + */ + accessibilityLabel?: string; + testID?: string; +}; + +export type MenuTriggerProps = { + /** + * Icon displayed in the trigger FAB (and cross-faded to `closeIcon` when + * the menu is open). + */ + icon: IconSource; + variant?: Variant; + size?: Size; + containerColor?: ColorValue; + contentColor?: ColorValue; + visible?: boolean; + onPress?: (e: GestureResponderEvent) => void; + accessibilityLabel?: string; + testID?: string; +}; + +type TwoToSix = + | [T, T] + | [T, T, T] + | [T, T, T, T] + | [T, T, T, T, T] + | [T, T, T, T, T, T]; + +export type MenuProps = { + /** + * Whether the menu is open. + */ + expanded: boolean; + /** + * Called when the user taps the close button or taps an item. + */ + onDismiss: () => void; + /** + * Trigger FAB configuration. The menu renders a morphing FAB that + * animates between the trigger appearance and the spec'd close button. + */ + trigger: MenuTriggerProps; + /** + * Horizontal side the menu sits on. Default `'end'`. + */ + alignment?: 'start' | 'center' | 'end'; + /** + * Icon used by the close button when the menu is expanded. Default + * `'close'`. + */ + closeIcon?: IconSource; + /** + * Menu items. Spec calls for 2 to 6 items. + */ + items: TwoToSix; + testID?: string; + /** + * @optional + */ + theme?: ThemeProp; +}; + +/** + * Per the M3 FAB Menu spec, the menu picks one of three color sets (primary, + * secondary, tertiary) based on which family the trigger FAB belongs to. + * The close button is always the saturated role color; items are always the + * tonal (container) role color. + */ +const getCloseVariant = (triggerVariant: Variant): Variant => { + if (triggerVariant === 'primary' || triggerVariant === 'tonalPrimary') { + return 'primary'; + } + if (triggerVariant === 'secondary' || triggerVariant === 'tonalSecondary') { + return 'secondary'; + } + return 'tertiary'; +}; + +const getItemsVariant = (triggerVariant: Variant): Variant => { + if (triggerVariant === 'primary' || triggerVariant === 'tonalPrimary') { + return 'tonalPrimary'; + } + if (triggerVariant === 'secondary' || triggerVariant === 'tonalSecondary') { + return 'tonalSecondary'; + } + return 'tonalTertiary'; +}; + +// Per-item delay used by the stagger. Compose uses a single SlowEffects-driven +// integer count that crosses each item's threshold; we approximate with a +// fixed delay per index. +const STAGGER_MS = 30; + +type AnimatedItemProps = { + expanded: boolean; + index: number; + itemCount: number; + theme: InternalTheme; + transformOrigin: 'left' | 'center' | 'right'; + marginBottom: number; + children: React.ReactNode; +}; + +const AnimatedItem = ({ + expanded, + index, + itemCount, + theme, + transformOrigin, + marginBottom, + children, +}: AnimatedItemProps) => { + const reduceMotion = useReduceMotion(); + // Initial values match the resting state for the current `expanded` prop so + // first mount doesn't animate unexpectedly. + const scaleX = useSharedValue(expanded ? 1 : 0); + const alpha = useSharedValue(expanded ? 1 : 0); + + React.useEffect(() => { + const target = expanded ? 1 : 0; + // Bottom-up on open, top-down on close (matches Compose). + const delay = expanded + ? (itemCount - 1 - index) * STAGGER_MS + : index * STAGGER_MS; + + if (reduceMotion) { + scaleX.value = target; + alpha.value = target; + return; + } + scaleX.value = withDelay( + delay, + withSpring(target, toRawSpring(theme.motion.spring.fast.spatial)) + ); + alpha.value = withDelay( + delay, + withSpring(target, toRawSpring(theme.motion.spring.fast.effects)) + ); + }, [expanded, index, itemCount, theme, reduceMotion, scaleX, alpha]); + + // Only scaleX and opacity animate. Layout height stays at the item's + // natural size — the items container is absolutely positioned above the + // trigger, so this fixed height never affects the trigger's position. + const animStyle = useAnimatedStyle(() => ({ + transform: [{ scaleX: scaleX.value }], + opacity: alpha.value, + })); + + return ( + + {children} + + ); +}; + +type ItemProps = { + icon?: IconSource; + label: string; + variant: Variant; + theme: InternalTheme; + onPress: (e: GestureResponderEvent) => void; + accessibilityLabel?: string; + testID?: string; +}; + +/** + * A single FAB Menu item. Visually a tonal pill with an icon and a label, + * but it is not a floating action button: no shadow, no enter/exit scaling + * of its own (the surrounding `AnimatedItem` handles entrance), and its + * shape and dimensions come from the menu spec rather than FAB tokens. + */ +const MenuItem = ({ + icon, + label, + variant, + theme, + onPress, + accessibilityLabel, + testID, +}: ItemProps) => { + const colors = resolveColors({ theme, variant }); + const { height, iconSize, leading, trailing, iconLabelGap, shape } = + MenuTokens.listItem; + const borderRadius = resolveCornerRadius(theme, shape); + + const { focusedSV, onFocus, onBlur } = useFocusRing(); + const focusRingStyle = useAnimatedStyle(() => ({ + opacity: focusedSV.value ? 1 : 0, + })); + + return ( + + + + + + + + + ); +}; + +type MorphingTriggerProps = { + triggerVariant: Variant; + closeVariant: Variant; + triggerContainerColor?: ColorValue; + triggerContentColor?: ColorValue; + size: Size; + openIcon: IconSource; + closeIcon: IconSource; + expanded: boolean; + /** Whether the trigger FAB is visible; drives the scale/alpha enter/exit. */ + visible: boolean; + alignment: 'start' | 'center' | 'end'; + onPress?: (e: GestureResponderEvent) => void; + accessibilityLabel?: string; + theme: InternalTheme; + testID?: string; +}; + +const MorphingTrigger = ({ + triggerVariant, + closeVariant, + triggerContainerColor, + triggerContentColor, + size, + openIcon, + closeIcon, + expanded, + visible, + alignment, + onPress, + accessibilityLabel, + theme, + testID, +}: MorphingTriggerProps) => { + const reduceMotion = useReduceMotion(); + + const closedSpec = Tokens.sizes[size]; + const closedContainer = closedSpec.container; + const closedIconSize = closedSpec.icon; + const closedBorderRadius = resolveCornerRadius(theme, closedSpec.shape); + + const openContainer = MenuTokens.closeButton.container; + const openIconSize = MenuTokens.closeButton.iconSize; + // Use container/2 (instead of the cornerFull sentinel) as the open radius, + // so the interpolation produces a smooth round-corner morph rather than + // jumping past the visual "circle" threshold almost immediately. + const openBorderRadius = openContainer / 2; + + // Trigger color set (respects user overrides) and close color set (always + // the saturated role color per spec — no overrides). + const triggerColors = resolveColors({ + theme, + variant: triggerVariant, + containerColor: triggerContainerColor, + contentColor: triggerContentColor, + }); + const closeColors = resolveColors({ theme, variant: closeVariant }); + + const progress = useSharedValue(expanded ? 1 : 0); + + React.useEffect(() => { + if (reduceMotion) { + progress.value = expanded ? 1 : 0; + return; + } + // Compose's ToggleFloatingActionButton uses a single FastSpatial spring + // for the full open/close progress (size, corner, color, icon all share + // one timeline). + progress.value = withSpring( + expanded ? 1 : 0, + toRawSpring(theme.motion.spring.fast.spatial) + ); + }, [expanded, theme, reduceMotion, progress]); + + // Derived shared values for the morph shape. Passing them to Shell as + // individual shared values (rather than packing them into an animated + // style) means Shell can put each into a single `useAnimatedStyle` with + // no inter-style merge surprises. Explicit deps so toggling `size` while + // the menu is open re-derives immediately — e.g. closed-state values + // change to match the new size's resting shape, and the close-state values + // (always 56 / 28) keep the open shape circular. + const widthShared = useDerivedValue( + () => interpolate(progress.value, [0, 1], [closedContainer, openContainer]), + [closedContainer, openContainer] + ); + const heightShared = useDerivedValue( + () => interpolate(progress.value, [0, 1], [closedContainer, openContainer]), + [closedContainer, openContainer] + ); + const borderRadiusShared = useDerivedValue( + () => + interpolate( + progress.value, + [0, 1], + [closedBorderRadius, openBorderRadius] + ), + [closedBorderRadius, openBorderRadius] + ); + + const openPlaneStyle = useAnimatedStyle(() => ({ + opacity: 1 - progress.value, + })); + const closePlaneStyle = useAnimatedStyle(() => ({ + opacity: progress.value, + })); + const openIconAnimStyle = useAnimatedStyle(() => { + const offset = (widthShared.value - closedIconSize) / 2; + return { + opacity: 1 - progress.value, + transform: [{ translateX: offset }, { translateY: offset }], + }; + }); + const closeIconAnimStyle = useAnimatedStyle(() => { + const offset = (widthShared.value - openIconSize) / 2; + return { + opacity: progress.value, + transform: [{ translateX: offset }, { translateY: offset }], + }; + }); + + // Outer slot is fixed at the trigger's resting size; the FAB itself + // shrinks toward the top-{start|center|end} corner of that slot when + // expanded (only meaningful for medium / large sizes). + const slotAlign: 'flex-start' | 'center' | 'flex-end' = + alignment === 'start' + ? 'flex-start' + : alignment === 'center' + ? 'center' + : 'flex-end'; + + return ( + + + + + + } + theme={theme} + > + + + + + + + + + + + ); +}; + +/** + * Floating action button menu. Wraps a trigger FAB; when `expanded` is true, + * items appear stacked above and the trigger morphs into the spec'd close + * button (`shape: 'full'`, 56 dp, saturated role color). + * + * No visual backdrop and no outside-tap dismiss — that matches the MD3 spec + * and lets the user keep interacting with the content underneath. Dismiss + * via the close button or by tapping an item. + * + * ## Usage + * ```tsx + * const [open, setOpen] = React.useState(false); + * + * + * setOpen(false)} + * trigger={{ icon: 'plus', variant: 'primary', onPress: () => setOpen(true) }} + * items={[ + * { icon: 'email', label: 'Send', onPress: () => {} }, + * { icon: 'bell', label: 'Remind', onPress: () => {} }, + * ]} + * /> + * + * ``` + */ +const Menu = ({ + expanded, + onDismiss, + trigger, + alignment = 'end', + closeIcon = 'close', + items, + testID = 'floating-action-button-menu', + theme: themeOverrides, +}: MenuProps) => { + const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); + const isRTL = direction === 'rtl'; + const insets = useSafeAreaInsets(); + + const triggerVariant: Variant = trigger.variant ?? 'tonalPrimary'; + const size: Size = trigger.size ?? 'default'; + const openIcon: IconSource = trigger.icon; + const openOnPress = trigger.onPress; + const triggerVisible = trigger.visible ?? true; + const closeVariant = getCloseVariant(triggerVariant); + const itemsVariant = getItemsVariant(triggerVariant); + + const effectiveExpanded = triggerVisible && expanded; + + const handleItemPress = + (item: MenuItemProps) => (e: GestureResponderEvent) => { + item.onPress(e); + onDismiss(); + }; + + const flexAlign: 'flex-start' | 'center' | 'flex-end' = + alignment === 'start' + ? 'flex-start' + : alignment === 'center' + ? 'center' + : 'flex-end'; + const itemTransformOrigin: 'left' | 'center' | 'right' = + alignment === 'start' + ? isRTL + ? 'right' + : 'left' + : alignment === 'center' + ? 'center' + : isRTL + ? 'left' + : 'right'; + + const triggerSlotSize = Tokens.sizes[size].container; + + return ( + + + + {items.map((item, index) => { + const isLast = index === items.length - 1; + return ( + + + + ); + })} + + + + + ); +}; + +Menu.displayName = 'Menu'; + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFill, + justifyContent: 'flex-end', + pointerEvents: 'box-none', + }, + stack: { + flexDirection: 'column', + pointerEvents: 'box-none', + }, + items: { + position: 'absolute', + flexDirection: 'column', + pointerEvents: 'box-none', + }, + itemsStart: { + left: 0, + }, + itemsCenter: { + left: 0, + right: 0, + }, + itemsEnd: { + right: 0, + }, + menuItemWrapper: { + position: 'relative', + }, + menuItem: { + overflow: 'hidden', + }, + menuItemFocusRing: { + position: 'absolute', + top: -FOCUS_RING_INSET, + left: -FOCUS_RING_INSET, + right: -FOCUS_RING_INSET, + bottom: -FOCUS_RING_INSET, + borderWidth: FOCUS_RING_THICKNESS, + pointerEvents: 'none', + }, + triggerSlot: { + justifyContent: 'flex-start', + }, + colorPlane: { + ...StyleSheet.absoluteFill, + pointerEvents: 'none', + }, + iconOrigin: { + position: 'absolute', + top: 0, + left: 0, + pointerEvents: 'none', + }, + iconAbsolute: { + position: 'absolute', + top: 0, + left: 0, + pointerEvents: 'none', + }, + pointerEventsAuto: { + pointerEvents: 'auto', + }, + pointerEventsNone: { + pointerEvents: 'none', + }, + pointerEventsBoxNone: { + pointerEvents: 'box-none', + }, +}); + +export default Menu; + +// @component-docs ignore-next-line +export { Menu }; diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx new file mode 100644 index 0000000000..b748b11e40 --- /dev/null +++ b/src/components/FAB/Shell.tsx @@ -0,0 +1,372 @@ +import * as React from 'react'; +import { + AccessibilityState, + ColorValue, + GestureResponderEvent, + Platform, + PressableAndroidRippleConfig, + StyleProp, + StyleSheet, + View, + ViewStyle, +} from 'react-native'; + +import Reanimated, { + AnimatedStyle, + useAnimatedStyle, + useSharedValue, + type SharedValue, +} from 'react-native-reanimated'; + +import Content from './Content'; +import { + Size, + Tokens, + Variant, + FOCUS_RING_INSET, + FOCUS_RING_THICKNESS, + webNoOutline, +} from './tokens'; +import { useFocusRing } from './useFocusRing'; +import { useVisibility } from './useVisibility'; +import { getDimensions, resolveColors } from './utils'; +import { useInternalTheme } from '../../core/theming'; +import type { ShapeToken } from '../../theme/utils/shape'; +import type { Elevation, ThemeProp } from '../../types'; +import { forwardRef } from '../../utils/forwardRef'; +import type { IconSource } from '../Icon'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; + +export type ShellProps = { + /** + * Icon rendered inside the FAB when no custom `children` are provided. + */ + icon?: IconSource; + /** + * Label rendered next to the icon when no custom `children` are provided. + * When present, the FAB grows to fit. + */ + label?: string; + /** + * Role-color preset. Defaults to `tonalPrimary`. + */ + variant?: Variant; + /** + * Spec size. Defaults to `default`. + */ + size?: Size; + /** + * Container color override. Wins over `variant`. + */ + containerColor?: ColorValue; + /** + * Content color override. Wins over `variant`. + */ + contentColor?: ColorValue; + /** + * Shape override. Defaults to the size-driven shape token. + */ + shape?: ShapeToken; + /** + * Icon size override. + */ + iconSize?: number; + /** + * Leading-padding override. + */ + leading?: number; + /** + * Trailing-padding override. + */ + trailing?: number; + /** + * Resting elevation level. Defaults to the FAB's enabled-state elevation. + * Pass `0` to disable the shadow entirely. + */ + elevation?: Elevation; + /** + * When `false`, the shell animates out (scale + alpha) and stops accepting + * touches. + */ + visible?: boolean; + /** + * Function to execute on press. + */ + onPress?: (e: GestureResponderEvent) => void; + /** + * Accessibility label. Falls back to `label` if unset. + */ + accessibilityLabel?: string; + /** + * Accessibility state forwarded to the underlying button. + */ + accessibilityState?: AccessibilityState; + /** + * Largest scale the label font can reach (auto-built content only). + */ + labelMaxFontSizeMultiplier?: number; + /** + * Animated style merged onto the label wrapper. Used by the Extended FAB + * to fade the label in and out as the FAB expands and collapses. + */ + labelAnimatedStyle?: StyleProp>; + /** + * Type of background drawable to display the feedback (Android). + */ + background?: PressableAndroidRippleConfig; + /** + * Shared value driving the outer's animated width. When omitted, the + * outer is sized by its content (icon FAB) or the size token + * (`dimensions.width`). + */ + widthShared?: SharedValue; + /** + * Shared value driving the outer's animated height. When omitted, the + * outer is sized by its content. + */ + heightShared?: SharedValue; + /** + * Shared value driving the outer's animated borderRadius. The same value + * is applied to the inner clip so children are clipped to the same shape. + * When omitted, the static size-driven radius is used. + */ + borderRadiusShared?: SharedValue; + /** + * When `true`, both outer and clip render with `backgroundColor: transparent` + * so the consumer can paint the surface via the `overlay` slot (used by the + * morph trigger's cross-faded color planes). + */ + transparentBackground?: boolean; + /** + * Absolutely-positioned content rendered inside the shell, behind the icon + * and label row. Used by the morphing trigger to cross-fade color planes. + */ + overlay?: React.ReactNode; + /** + * Replaces the default icon + label content. Pass your own `` + * when you need custom typescale, label animation, or measurement. + */ + children?: React.ReactNode; + /** + * Outer-positioning style. Visual treatment (size, shape, color) comes from + * `variant` and `size`. + */ + style?: StyleProp; + /** + * TestID used for testing purposes. + */ + testID?: string; + /** + * @optional + */ + theme?: ThemeProp; + ref?: React.RefObject; +}; + +/** + * Internal shell used by every FAB-flavored component (regular, Extended, + * morphing menu trigger). Owns the outer container, ripple, clip, and the + * visibility animation (scale + alpha + shadow). Consumers that need to + * animate the outer's width/height/borderRadius pass shared values; the + * static size-driven defaults are used otherwise. + * + * Not exported from the package. + */ +const Shell = forwardRef( + ( + { + icon, + label, + variant = 'tonalPrimary', + size = 'default', + containerColor, + contentColor, + shape, + iconSize, + leading, + trailing, + elevation = Tokens.stateElevation.enabled, + visible = true, + onPress, + accessibilityLabel = label, + accessibilityState, + labelMaxFontSizeMultiplier, + labelAnimatedStyle, + background, + widthShared, + heightShared, + borderRadiusShared, + transparentBackground = false, + overlay, + children, + style, + testID = 'fab-shell', + theme: themeOverrides, + }, + ref + ) => { + const theme = useInternalTheme(themeOverrides); + + const dimensions = React.useMemo( + () => getDimensions({ theme, size, shape, iconSize, leading, trailing }), + [theme, size, shape, iconSize, leading, trailing] + ); + + const colors = React.useMemo( + () => resolveColors({ theme, variant, containerColor, contentColor }), + [theme, variant, containerColor, contentColor] + ); + + const { scale, alpha, shadowStyle } = useVisibility({ + visible, + theme, + elevation, + }); + + // Fallback shared values track the static size-driven dimensions. Consumers + // that don't supply their own animated shared values get these. Keeping + // everything as a shared value means there's exactly one animated style + // per view — no static-vs-animated merge surprises. + const fallbackWidth = useSharedValue(dimensions.width); + const fallbackHeight = useSharedValue(dimensions.height); + const fallbackBorderRadius = useSharedValue(dimensions.borderRadius); + React.useEffect(() => { + fallbackWidth.value = dimensions.width; + fallbackHeight.value = dimensions.height; + fallbackBorderRadius.value = dimensions.borderRadius; + }, [ + dimensions.width, + dimensions.height, + dimensions.borderRadius, + fallbackWidth, + fallbackHeight, + fallbackBorderRadius, + ]); + + const width = widthShared ?? fallbackWidth; + const height = heightShared ?? fallbackHeight; + const borderRadius = borderRadiusShared ?? fallbackBorderRadius; + const containerBg = transparentBackground + ? 'transparent' + : colors.container; + + const outerStyle = useAnimatedStyle( + () => ({ + transform: [{ scale: scale.value }], + opacity: alpha.value, + width: width.value, + height: height.value, + borderRadius: borderRadius.value, + backgroundColor: containerBg, + }), + [width, height, borderRadius, containerBg] + ); + + const clipStyle = useAnimatedStyle( + () => ({ + borderRadius: borderRadius.value, + backgroundColor: containerBg, + }), + [borderRadius, containerBg] + ); + + const { focusedSV, onFocus, onBlur } = useFocusRing(); + const focusRingStyle = useAnimatedStyle( + () => ({ + opacity: focusedSV.value ? 1 : 0, + borderRadius: borderRadius.value + FOCUS_RING_INSET, + }), + [borderRadius] + ); + + return ( + + + {overlay} + + {children ?? ( + + )} + + + + + ); + } +); + +const styles = StyleSheet.create({ + container: { + transformOrigin: 'center', + }, + clip: { + width: '100%', + height: '100%', + overflow: 'hidden', + }, + fill: { + flex: 1, + }, + pointerEventsAuto: { + pointerEvents: 'auto', + }, + pointerEventsNone: { + pointerEvents: 'none', + }, + focusRing: { + position: 'absolute', + top: -FOCUS_RING_INSET, + left: -FOCUS_RING_INSET, + right: -FOCUS_RING_INSET, + bottom: -FOCUS_RING_INSET, + borderWidth: FOCUS_RING_THICKNESS, + pointerEvents: 'none', + }, +}); + +export default Shell; diff --git a/src/components/FAB/index.ts b/src/components/FAB/index.ts deleted file mode 100644 index 4364bc7411..0000000000 --- a/src/components/FAB/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import FABComponent from './FAB'; -import FABGroup from './FABGroup'; - -const FAB = Object.assign( - // @component ./FAB.tsx - FABComponent, - { - // @component ./FABGroup.tsx - Group: FABGroup, - } -); - -export default FAB; diff --git a/src/components/FAB/index.tsx b/src/components/FAB/index.tsx new file mode 100644 index 0000000000..3224fcf60f --- /dev/null +++ b/src/components/FAB/index.tsx @@ -0,0 +1,7 @@ +import Extended from './Extended'; +import FABBase from './FAB'; +import Menu from './Menu'; + +const FAB = Object.assign(FABBase, { Extended, Menu }); + +export default FAB; diff --git a/src/components/FAB/tokens.ts b/src/components/FAB/tokens.ts new file mode 100644 index 0000000000..73ef1e9ff8 --- /dev/null +++ b/src/components/FAB/tokens.ts @@ -0,0 +1,128 @@ +import type { ViewStyle } from 'react-native'; + +import { tokens } from '../../theme/tokens'; +import type { + ColorRole, + Elevation, + ThemeShapeCorners, + TypescaleKey, +} from '../../theme/types'; + +export type Variant = + | 'primary' + | 'secondary' + | 'tertiary' + | 'tonalPrimary' + | 'tonalSecondary' + | 'tonalTertiary'; + +export type Size = 'default' | 'medium' | 'large'; + +type SizeSpec = { + container: number; + icon: number; + shape: keyof ThemeShapeCorners; + leading: number; + trailing: number; + iconLabelGap: number; + labelTypescale: TypescaleKey; +}; + +const sizes = { + default: { + container: 56, + icon: 24, + shape: 'large', + leading: 16, + trailing: 16, + iconLabelGap: 8, + labelTypescale: 'titleMedium', + }, + medium: { + container: 80, + icon: 28, + shape: 'largeIncreased', + leading: 26, + trailing: 26, + iconLabelGap: 12, + labelTypescale: 'titleLarge', + }, + large: { + container: 96, + icon: 36, + shape: 'extraLarge', + // (container - icon) / 2 keeps the icon centered inside the visible + // square when the Extended FAB collapses to icon-only width. + leading: 30, + trailing: 30, + iconLabelGap: 16, + labelTypescale: 'headlineSmall', + }, +} as const satisfies Record; + +const stateElevation = { + enabled: 3, + hover: 4, + focus: 3, + pressed: 3, +} as const satisfies Record; + +const variants = { + primary: { container: 'primary', content: 'onPrimary' }, + secondary: { container: 'secondary', content: 'onSecondary' }, + tertiary: { container: 'tertiary', content: 'onTertiary' }, + tonalPrimary: { + container: 'primaryContainer', + content: 'onPrimaryContainer', + }, + tonalSecondary: { + container: 'secondaryContainer', + content: 'onSecondaryContainer', + }, + tonalTertiary: { + container: 'tertiaryContainer', + content: 'onTertiaryContainer', + }, +} as const satisfies Record< + Variant, + { container: ColorRole; content: ColorRole } +>; + +export const Tokens = { + sizes, + stateElevation, + variants, +}; + +const closeButton = { + container: 56, + iconSize: 20, + shape: 'full', +} as const; + +const listItem = { + height: 56, + shape: 'full', + iconSize: 24, + leading: 24, + trailing: 24, + iconLabelGap: 8, +} as const; + +const spacing = { + betweenItems: 4, + closeToLastItem: 8, +} as const; + +export const MenuTokens = { + closeButton, + listItem, + spacing, +}; + +const focusIndicator = tokens.md.sys.state.focusIndicator; +export const FOCUS_RING_THICKNESS = focusIndicator.thickness; +export const FOCUS_RING_OUTER_OFFSET = focusIndicator.outerOffset; +export const FOCUS_RING_INSET = FOCUS_RING_OUTER_OFFSET + FOCUS_RING_THICKNESS; + +export const webNoOutline = { outline: 'none' } as unknown as ViewStyle; diff --git a/src/components/FAB/useFocusRing.ts b/src/components/FAB/useFocusRing.ts new file mode 100644 index 0000000000..bb056a10c8 --- /dev/null +++ b/src/components/FAB/useFocusRing.ts @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { Platform } from 'react-native'; + +import { useSharedValue, type SharedValue } from 'react-native-reanimated'; + +export type FocusRingState = { + /** + * `true` when the surface is keyboard-focused. Drive the focus ring's + * `opacity` from this in a `useAnimatedStyle`. + */ + focusedSV: SharedValue; + /** Wire to the `Pressable`/`TouchableRipple`'s `onFocus`. */ + onFocus: () => void; + /** Wire to the `Pressable`/`TouchableRipple`'s `onBlur`. */ + onBlur: () => void; +}; + +/** + * Drives an MD3 focus indicator for FAB-flavored surfaces. On web, focus is + * gated by `:focus-visible` so a mouse click does not light the ring; on + * native, every focus event is honored. + */ +export function useFocusRing(): FocusRingState { + const focusedSV = useSharedValue(false); + + const onFocus = React.useCallback(() => { + if ( + Platform.OS === 'web' && + !document.activeElement?.matches(':focus-visible') + ) { + return; + } + focusedSV.value = true; + }, [focusedSV]); + + const onBlur = React.useCallback(() => { + focusedSV.value = false; + }, [focusedSV]); + + return { focusedSV, onFocus, onBlur }; +} diff --git a/src/components/FAB/useVisibility.ts b/src/components/FAB/useVisibility.ts new file mode 100644 index 0000000000..12914e7f64 --- /dev/null +++ b/src/components/FAB/useVisibility.ts @@ -0,0 +1,103 @@ +import * as React from 'react'; +import { Platform, type ViewStyle } from 'react-native'; + +import { + useAnimatedStyle, + useSharedValue, + withSpring, + type AnimatedStyle, + type SharedValue, +} from 'react-native-reanimated'; + +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; +import { + IOS_SHADOW_RADIUS_FACTOR, + SHADOW_OPACITY, + androidElevationLevels, + shadow, + shadowLayers, +} from '../../theme/tokens/sys/elevation'; +import { toRawSpring } from '../../theme/tokens/sys/motion'; +import type { Elevation, InternalTheme } from '../../types'; + +type UseVisibilityArgs = { + visible: boolean; + theme: InternalTheme; + initialScale?: number; + transformOrigin?: ViewStyle['transformOrigin']; + /** + * Elevation level when shown. Shadow fades in/out with the FAB. + */ + elevation?: Elevation; +}; + +type UseVisibilityResult = { + scale: SharedValue; + alpha: SharedValue; + transformOrigin: ViewStyle['transformOrigin']; + shadowStyle: AnimatedStyle; +}; + +/** + * Animates a FAB in and out: scale + alpha together. + * Reduce-motion: snap to the final value, no animation. + * + * Returns `shadowStyle` too. Put it on the same view as the transform so the + * shadow stays in sync (Android uses `elevation`, iOS uses `shadow*`, Web uses + * `boxShadow` -- the outer container's `opacity: alpha.value` handles the + * visibility fade on Web so the shadow string can be static). + */ +export function useVisibility({ + visible, + theme, + initialScale = 0, + transformOrigin = 'center', + elevation = 0, +}: UseVisibilityArgs): UseVisibilityResult { + const reduceMotion = useReduceMotion(); + const scale = useSharedValue(visible ? 1 : initialScale); + const alpha = useSharedValue(visible ? 1 : 0); + + React.useEffect(() => { + const targetScale = visible ? 1 : initialScale; + const targetAlpha = visible ? 1 : 0; + if (reduceMotion) { + scale.value = targetScale; + alpha.value = targetAlpha; + return; + } + scale.value = withSpring( + targetScale, + toRawSpring(theme.motion.spring.fast.spatial) + ); + alpha.value = withSpring( + targetAlpha, + toRawSpring(theme.motion.spring.fast.effects) + ); + }, [visible, theme, reduceMotion, scale, alpha, initialScale]); + + const restingElevationDp = androidElevationLevels[elevation]; + const shadowOffsetHeight = shadowLayers[0].height[elevation]; + const shadowRadius = shadowLayers[0].shadowRadius[elevation]; + const shadowColor = theme.colors.shadow; + + const webShadow = + Platform.OS === 'web' ? shadow(elevation, shadowColor) : null; + + const shadowStyle = useAnimatedStyle(() => { + if (Platform.OS === 'android') { + return { elevation: alpha.value * restingElevationDp }; + } + if (Platform.OS === 'web') { + return webShadow ?? {}; + } + return { + shadowColor, + shadowOpacity: alpha.value * (elevation ? SHADOW_OPACITY : 0), + shadowOffset: { width: 0, height: shadowOffsetHeight }, + shadowRadius: shadowRadius * IOS_SHADOW_RADIUS_FACTOR, + }; + }); + + return { scale, alpha, transformOrigin, shadowStyle }; +} diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index d46fcaf685..8e7c83eddb 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -1,366 +1,83 @@ -import { MutableRefObject } from 'react'; -import { Animated, ColorValue, Platform, ViewStyle } from 'react-native'; +import { ColorValue } from 'react-native'; +import { Size, Tokens, Variant } from './tokens'; +import type { TypescaleKey } from '../../theme/types'; +import { contentColorFor } from '../../theme/utils/color'; +import { resolveCornerRadius, ShapeToken } from '../../theme/utils/shape'; import type { InternalTheme } from '../../types'; -type GetCombinedStylesProps = { - isAnimatedFromRight: boolean; - isIconStatic: boolean; - isRTL: boolean; - distance: number; - animFAB: Animated.Value; +export type ResolvedColors = { + container: ColorValue; + content: ColorValue; }; -type CombinedStyles = { - innerWrapper: Animated.WithAnimatedValue; - iconWrapper: Animated.WithAnimatedValue; - absoluteFill: Animated.WithAnimatedValue; -}; - -type Variant = 'primary' | 'secondary' | 'tertiary' | 'surface'; - -type BaseProps = { - isVariant: (variant: Variant) => boolean; - theme: InternalTheme; -}; - -export const getCombinedStyles = ({ - isAnimatedFromRight, - isIconStatic, - isRTL, - distance, - animFAB, -}: GetCombinedStylesProps): CombinedStyles => { - const defaultPositionStyles = { left: -distance, right: undefined }; - - const combinedStyles: CombinedStyles = { - innerWrapper: { - ...defaultPositionStyles, - }, - iconWrapper: { - ...defaultPositionStyles, - }, - absoluteFill: {}, - }; - - const animatedFromRight = isAnimatedFromRight && !isRTL; - const animatedFromRightRTL = isAnimatedFromRight && isRTL; - const animatedFromLeft = !isAnimatedFromRight && !isRTL; - const animatedFromLeftRTL = !isAnimatedFromRight && isRTL; - - if (animatedFromRight) { - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [distance, 0], - }), - }, - ]; - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic ? 0 : animFAB, - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [Math.abs(distance) / 2, Math.abs(distance)], - }), - }, - ]; - } else if (animatedFromRightRTL) { - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic - ? 0 - : animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [-distance, 0], - }), - }, - ]; - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [-distance, 0], - }), - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [distance, 0], - outputRange: [0, distance], - }), - }, - ]; - } else if (animatedFromLeft) { - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic - ? distance - : animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [distance, distance * 2], - }), - }, - ]; - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB, - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, Math.abs(distance) / 2], - }), - }, - ]; - } else if (animatedFromLeftRTL) { - combinedStyles.iconWrapper.transform = [ - { - translateX: isIconStatic - ? animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [-distance, -distance * 2], - }) - : -distance, - }, - ]; - combinedStyles.innerWrapper.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, -distance], - }), - }, - ]; - combinedStyles.absoluteFill.transform = [ - { - translateX: animFAB.interpolate({ - inputRange: [0, distance], - outputRange: [0, -distance], - }), - }, - ]; - } - - return combinedStyles; -}; - -const getBackgroundColor = ({ - theme, - isVariant, - customBackgroundColor, -}: BaseProps & { customBackgroundColor?: ColorValue }) => { - if (customBackgroundColor) { - return customBackgroundColor; - } - - if (isVariant('primary')) { - return theme.colors.primaryContainer; - } - - if (isVariant('secondary')) { - return theme.colors.secondaryContainer; - } - - if (isVariant('tertiary')) { - return theme.colors.tertiaryContainer; - } - - if (isVariant('surface')) { - return theme.colors.surfaceContainerHigh; - } - - return theme.colors.primaryContainer; -}; - -const getForegroundColor = ({ - theme, - isVariant, - customColor, -}: BaseProps & { customColor?: ColorValue }) => { - if (typeof customColor !== 'undefined') { - return customColor; - } - - if (isVariant('primary')) { - return theme.colors.onPrimaryContainer; - } - - if (isVariant('secondary')) { - return theme.colors.onSecondaryContainer; - } - - if (isVariant('tertiary')) { - return theme.colors.onTertiaryContainer; - } - - if (isVariant('surface')) { - return theme.colors.primary; - } - - return theme.colors.onPrimaryContainer; -}; - -export const getFABColors = ({ +/** + * Resolve container + content colors. Explicit overrides win; when only + * `containerColor` is set, the content color is derived via + * `contentColorFor`. + */ +export const resolveColors = ({ theme, - variant, - customColor, - customBackgroundColor, + variant = 'tonalPrimary', + containerColor, + contentColor, }: { theme: InternalTheme; - variant: string; - customColor?: ColorValue; - customBackgroundColor?: ColorValue; -}) => { - const isVariant = (variantToCompare: Variant) => { - return variant === variantToCompare; - }; - - const baseFABColorProps = { theme, isVariant }; - - const backgroundColor = getBackgroundColor({ - ...baseFABColorProps, - customBackgroundColor, - }); - - const foregroundColor = getForegroundColor({ - ...baseFABColorProps, - customColor, - }); - - return { - backgroundColor, - foregroundColor, - }; -}; - -const getLabelColor = ({ theme }: { theme: InternalTheme }) => { - return theme.colors.onSurface; -}; - -const getStackedFABBackgroundColor = ({ theme }: { theme: InternalTheme }) => { - return theme.colors.surfaceContainerHigh; -}; - -export const getFABGroupColors = ({ + variant?: Variant; + containerColor?: ColorValue; + contentColor?: ColorValue; +}): ResolvedColors => { + const roles = Tokens.variants[variant]; + const container = containerColor ?? theme.colors[roles.container]; + const content = + contentColor ?? + (containerColor != null + ? contentColorFor(theme, container) + : theme.colors[roles.content]); + return { container, content }; +}; + +export type Dimensions = { + height: number; + width: number; + borderRadius: number; + iconSize: number; + leading: number; + trailing: number; + iconLabelGap: number; + labelTypescale: TypescaleKey; +}; + +/** + * Resolve geometry for a FAB at a given size, with optional shape, icon + * size, and leading/trailing overrides for FAB Menu items and the close + * button. + */ +export const getDimensions = ({ theme, - customBackdropColor, + size = 'default', + shape, + iconSize, + leading, + trailing, }: { theme: InternalTheme; - customBackdropColor?: ColorValue; -}) => { - return { - labelColor: getLabelColor({ theme }), - backdropColor: customBackdropColor ?? theme.colors.background, - backdropOpacity: customBackdropColor ? 1 : 0.95, - stackedFABBackgroundColor: getStackedFABBackgroundColor({ theme }), - }; -}; - -const v3SmallSize = { - height: 40, - width: 40, -}; -const v3MediumSize = { - height: 56, - width: 56, -}; -const v3LargeSize = { - height: 96, - width: 96, -}; - -const getCustomFabSize = (customSize: number) => ({ - height: customSize, - width: customSize, - borderRadius: customSize / 4, -}); - -export const getFabStyle = ({ - size, - theme, - customSize, -}: { - customSize?: number; - size: 'small' | 'medium' | 'large'; - theme: InternalTheme; -}) => { - if (customSize) return getCustomFabSize(customSize); - - switch (size) { - case 'small': - return { ...v3SmallSize, borderRadius: theme.shapes.corner.medium }; - case 'medium': - return { ...v3MediumSize, borderRadius: theme.shapes.corner.large }; - case 'large': - return { ...v3LargeSize, borderRadius: theme.shapes.corner.extraLarge }; - } -}; - -const v3Extended = { - height: 56, - borderRadius: 16, - paddingHorizontal: 16, -}; - -const getExtendedFabDimensions = (customSize: number) => ({ - height: customSize, - paddingHorizontal: 16, -}); - -export const getExtendedFabStyle = ({ - customSize, - theme: _theme, -}: { - customSize?: number; - theme: InternalTheme; -}) => { - if (customSize) return getExtendedFabDimensions(customSize); - - return v3Extended; -}; - -let cachedContext: CanvasRenderingContext2D | null = null; - -const getCanvasContext = () => { - if (cachedContext) { - return cachedContext; - } - - const canvas = document.createElement('canvas'); - cachedContext = canvas.getContext('2d'); - - return cachedContext; -}; - -export const getLabelSizeWeb = (ref: MutableRefObject) => { - if (Platform.OS !== 'web' || ref.current === null) { - return null; - } - - const canvasContext = getCanvasContext(); - - if (!canvasContext) { - return null; - } - - const elementStyles = window.getComputedStyle(ref.current); - canvasContext.font = elementStyles.font; - - const metrics = canvasContext.measureText(ref.current.innerText); - + size?: Size; + shape?: ShapeToken; + iconSize?: number; + leading?: number; + trailing?: number; +}): Dimensions => { + const spec = Tokens.sizes[size]; + const shapeToken: ShapeToken = shape ?? spec.shape; return { - width: metrics.width, - height: - (metrics.fontBoundingBoxAscent ?? 0) + - (metrics.fontBoundingBoxDescent ?? 0), + height: spec.container, + width: spec.container, + borderRadius: resolveCornerRadius(theme, shapeToken), + iconSize: iconSize ?? spec.icon, + leading: leading ?? spec.leading, + trailing: trailing ?? spec.trailing, + iconLabelGap: spec.iconLabelGap, + labelTypescale: spec.labelTypescale, }; }; diff --git a/src/components/__tests__/AnimatedFAB.test.tsx b/src/components/__tests__/AnimatedFAB.test.tsx deleted file mode 100644 index 318f59c3b9..0000000000 --- a/src/components/__tests__/AnimatedFAB.test.tsx +++ /dev/null @@ -1,183 +0,0 @@ -/// - -import * as React from 'react'; -import { Animated, StyleSheet } from 'react-native'; - -import { fireEvent } from '@testing-library/react-native'; -import { act } from 'react-test-renderer'; - -import { render } from '../../test-utils'; -import { Palette } from '../../theme/tokens'; -import AnimatedFAB from '../FAB/AnimatedFAB'; - -const styles = StyleSheet.create({ - background: { - backgroundColor: Palette.tertiary50, - }, -}); - -it('renders animated fab', () => { - const tree = render( - {}} label="" extended={false} icon="plus" /> - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders animated fab with label on the right by default', () => { - const tree = render( - {}} icon="plus" /> - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders animated fab with label on the left', () => { - const tree = render( - {}} - icon="plus" - /> - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders animated fab with only transparent container', () => { - const { getByTestId } = render( - {}} - icon="plus" - testID="animated-fab" - extended={false} - style={styles.background} - /> - ); - expect(getByTestId('animated-fab-container')).toHaveStyle({ - backgroundColor: 'transparent', - }); -}); - -it('animated value changes correctly', () => { - const value = new Animated.Value(1); - const { getByTestId } = render( - - ); - expect(getByTestId('animated-fab-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - act(() => { - jest.advanceTimersByTime(200); - }); - - expect(getByTestId('animated-fab-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); - -it('renders FAB without uppercase styling if uppercase prop is falsy', () => { - const { getByTestId } = render( - {}} - icon="plus" - testID="animated-fab" - style={styles.background} - extended={false} - uppercase={false} - /> - ); - - expect(getByTestId('animated-fab-text')).not.toHaveStyle({ - textTransform: 'uppercase', - }); -}); - -it('renders FAB with uppercase styling if uppercase prop is truthy', () => { - const { getByTestId } = render( - {}} - icon="plus" - testID="animated-fab" - style={styles.background} - extended={false} - uppercase - /> - ); - - expect(getByTestId('animated-fab-text')).toHaveStyle({ - textTransform: 'uppercase', - }); -}); - -it('renders correct elevation value for shadow views', () => { - const { getByTestId } = render( - {}} - icon="plus" - testID="animated-fab" - /> - ); - - expect(getByTestId('animated-fab-shadow')).toHaveStyle({ elevation: 3 }); - expect(getByTestId('animated-fab-extended-shadow')).toHaveStyle({ - elevation: 3, - }); -}); - -describe('AnimatedFAB events', () => { - it('onPress passes event', () => { - const onPress = jest.fn(); - const { getByTestId } = render( - - ); - - act(() => { - fireEvent(getByTestId('animated-fab'), 'onPress', { key: 'value' }); - }); - - expect(onPress).toHaveBeenCalledWith({ key: 'value' }); - }); - - it('onLongPress passes event', () => { - const onLongPress = jest.fn(); - const { getByTestId } = render( - - ); - - act(() => { - fireEvent(getByTestId('animated-fab'), 'onLongPress', { key: 'value' }); - }); - - expect(onLongPress).toHaveBeenCalledWith({ key: 'value' }); - }); -}); diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index e99c694885..c357914d96 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -1,355 +1,95 @@ import * as React from 'react'; -import { Animated, StyleSheet } from 'react-native'; import { fireEvent } from '@testing-library/react-native'; -import { act } from 'react-test-renderer'; -import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; import FAB from '../FAB'; -import { getFABColors } from '../FAB/utils'; -const styles = StyleSheet.create({ - borderRadius: { - borderRadius: 0, - }, - small: { - height: 40, - width: 40, - borderRadius: 12, - }, - medium: { - height: 56, - width: 56, - borderRadius: 16, - }, - large: { - height: 96, - width: 96, - borderRadius: 28, - }, +it('renders FAB with default props', () => { + const tree = render().toJSON(); + expect(tree).toMatchSnapshot(); }); -it('renders default FAB', () => { - const tree = render( {}} icon="plus" />).toJSON(); - +it('renders FAB with primary variant', () => { + const tree = render().toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders small FAB', () => { - const tree = render( - {}} icon="plus" /> - ).toJSON(); - +it('renders FAB with secondary variant', () => { + const tree = render().toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders large FAB', () => { - const tree = render( - {}} icon="plus" /> - ).toJSON(); - +it('renders FAB with tertiary variant', () => { + const tree = render().toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with custom size prop', () => { - const tree = render( - {}} icon="plus" /> - ).toJSON(); - +it('renders FAB with tonalSecondary variant', () => { + const tree = render().toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders extended FAB', () => { - const tree = render( - {}} icon="plus" label="Add items" /> - ).toJSON(); - +it('renders FAB with tonalTertiary variant', () => { + const tree = render().toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders extended FAB with custom size prop', () => { - const tree = render( - {}} icon="plus" label="Add items" /> - ).toJSON(); - +it('renders FAB medium size', () => { + const tree = render().toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders loading FAB', () => { - const tree = render( - {}} icon="plus" loading={true} /> - ).toJSON(); +it('renders FAB large size', () => { + const tree = render().toJSON(); + expect(tree).toMatchSnapshot(); +}); +it('renders FAB with containerColor override', () => { + const tree = render().toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders loading FAB with custom size prop', () => { +it('renders FAB with containerColor and contentColor overrides', () => { const tree = render( - {}} icon="plus" loading={true} /> + ).toJSON(); - expect(tree).toMatchSnapshot(); }); -it('renders custom color for the icon and label of the FAB', () => { +it('renders FAB with accessibilityLabel', () => { const tree = render( - {}} icon="plus" color="#AA0114" /> + ).toJSON(); - expect(tree).toMatchSnapshot(); }); -it('renders not visible FAB', () => { - const { update, toJSON } = render( {}} icon="plus" />); - update( {}} icon="plus" visible={false} />); - +it('renders FAB transitioning to not visible', () => { + const { update, toJSON } = render(); + update(); expect(toJSON()).toMatchSnapshot(); }); -it('renders visible FAB', () => { - const { update, toJSON } = render( - {}} icon="plus" visible={false} /> - ); - - update( {}} icon="plus" visible={true} />); - +it('renders FAB transitioning to visible', () => { + const { update, toJSON } = render(); + update(); expect(toJSON()).toMatchSnapshot(); }); -it('renders FAB with custom border radius', () => { - const { getByTestId } = render( - {}} - icon="plus" - testID="fab" - style={styles.borderRadius} - /> - ); - - expect(getByTestId('fab-container')).toHaveStyle({ borderRadius: 0 }); -}); - -it('renders FAB with zero border radius', () => { - const { getByTestId } = render( - {}} - icon="plus" - testID="fab" - /> - ); - - expect(getByTestId('fab-container')).toHaveStyle({ borderRadius: 0 }); -}); - -it('renders FAB without uppercase styling by default', () => { - const { getByTestId } = render( - {}} label="Add items" testID="fab" /> - ); - - expect(getByTestId('fab-text')).not.toHaveStyle({ - textTransform: 'uppercase', - }); -}); - -it('renders FAB without uppercase styling if uppercase prop is falsy', () => { - const { getByTestId } = render( - {}} label="Add items" testID="fab" uppercase={false} /> - ); - - expect(getByTestId('fab-text')).not.toHaveStyle({ - textTransform: 'uppercase', - }); -}); - -it('renders FAB with uppercase styling if uppercase prop is truthy', () => { +it('calls onPress when FAB is pressed', () => { + const onPress = jest.fn(); const { getByTestId } = render( - {}} label="Add items" testID="fab" uppercase /> + ); - - expect(getByTestId('fab-text')).toHaveStyle({ - textTransform: 'uppercase', - }); -}); - -(['small', 'medium', 'large'] as const).forEach((size) => { - it(`renders ${size} FAB with correct size and border radius`, () => { - const { getByTestId } = render( - {}} size={size} icon="plus" testID={`${size}-fab`} /> - ); - - expect(getByTestId(`${size}-fab-content`)).toHaveStyle(styles[size]); - }); -}); - -describe('getFABColors - background color', () => { - it('should return color from styles', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'primary', - customBackgroundColor: 'purple', - }) - ).toMatchObject({ - backgroundColor: 'purple', - }); - }); - - it('should return correct theme color, primary variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'primary', - }) - ).toMatchObject({ - backgroundColor: getTheme().colors.primaryContainer, - }); - }); - - it('should return correct theme color, for theme version 3, secondary variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'secondary', - }) - ).toMatchObject({ - backgroundColor: getTheme().colors.secondaryContainer, - }); - }); - - it('should return correct theme color, for theme version 3, tertiary variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'tertiary', - }) - ).toMatchObject({ - backgroundColor: getTheme().colors.tertiaryContainer, - }); - }); - - it('should return correct theme color, for theme version 3, surface variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'surface', - }) - ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceContainerHigh, - }); - }); -}); - -describe('getFABColors - foreground color', () => { - it('should return custom color', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'primary', - customColor: 'purple', - }) - ).toMatchObject({ - foregroundColor: 'purple', - }); - }); - - it('should return correct theme color, primary variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'primary', - }) - ).toMatchObject({ - foregroundColor: getTheme().colors.onPrimaryContainer, - }); - }); - - it('should return correct theme color, for theme version 3, secondary variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'secondary', - }) - ).toMatchObject({ - foregroundColor: getTheme().colors.onSecondaryContainer, - }); - }); - - it('should return correct theme color, for theme version 3, tertiary variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'tertiary', - }) - ).toMatchObject({ - foregroundColor: getTheme().colors.onTertiaryContainer, - }); - }); - - it('should return correct theme color, for theme version 3, surface variant', () => { - expect( - getFABColors({ - theme: getTheme(), - variant: 'surface', - }) - ).toMatchObject({ - foregroundColor: getTheme().colors.primary, - }); - }); + fireEvent.press(getByTestId('fab')); + expect(onPress).toHaveBeenCalledTimes(1); }); -it('animated value changes correctly', () => { - const value = new Animated.Value(1); +it('forwards event object to onPress', () => { + const onPress = jest.fn(); const { getByTestId } = render( - {}} - icon="plus" - testID="fab" - style={[{ transform: [{ scale: value }] }]} - /> + ); - expect(getByTestId('fab-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - act(() => { - jest.advanceTimersByTime(200); - }); - expect(getByTestId('fab-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); - -describe('FAB events', () => { - it('onPress passes event', () => { - const onPress = jest.fn(); - const { getByText } = render(); - - act(() => { - fireEvent(getByText('Add items'), 'onPress', { key: 'value' }); - }); - - expect(onPress).toHaveBeenCalledWith({ key: 'value' }); - }); - - it('onLongPress passes event', () => { - const onLongPress = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Add items'), 'onLongPress', { key: 'value' }); - }); - - expect(onLongPress).toHaveBeenCalledWith({ key: 'value' }); - }); + fireEvent(getByTestId('fab'), 'onPress', { key: 'value' }); + expect(onPress).toHaveBeenCalledWith({ key: 'value' }); }); diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx new file mode 100644 index 0000000000..ca72466305 --- /dev/null +++ b/src/components/__tests__/FABExtended.test.tsx @@ -0,0 +1,108 @@ +import * as React from 'react'; + +import { fireEvent } from '@testing-library/react-native'; + +import { render } from '../../test-utils'; +import FAB from '../FAB'; + +it('renders extended FAB expanded', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders extended FAB collapsed', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders extended FAB not visible', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders extended FAB medium size', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders extended FAB large size', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders extended FAB transitioning to collapsed', () => { + const { update, toJSON } = render( + + ); + update(); + expect(toJSON()).toMatchSnapshot(); +}); + +it('uses label as default accessibilityLabel', () => { + const { getByTestId } = render( + + ); + expect(getByTestId('extended-fab').props.accessibilityLabel).toBe( + 'New message' + ); +}); + +it('respects explicit accessibilityLabel', () => { + const { getByTestId } = render( + + ); + expect(getByTestId('extended-fab').props.accessibilityLabel).toBe( + 'Create new message' + ); +}); + +it('calls onPress when pressed', () => { + const onPress = jest.fn(); + const { getByTestId } = render( + + ); + fireEvent.press(getByTestId('extended-fab')); + expect(onPress).toHaveBeenCalledTimes(1); +}); + +it('forwards event object to onPress', () => { + const onPress = jest.fn(); + const { getByTestId } = render( + + ); + fireEvent(getByTestId('extended-fab'), 'onPress', { key: 'value' }); + expect(onPress).toHaveBeenCalledWith({ key: 'value' }); +}); diff --git a/src/components/__tests__/FABGroup.test.tsx b/src/components/__tests__/FABGroup.test.tsx deleted file mode 100644 index f90e534910..0000000000 --- a/src/components/__tests__/FABGroup.test.tsx +++ /dev/null @@ -1,417 +0,0 @@ -import * as React from 'react'; -import { Animated } from 'react-native'; - -import { act, fireEvent } from '@testing-library/react-native'; - -import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; -import FAB from '../FAB'; -import { getFABGroupColors } from '../FAB/utils'; - -describe('getFABGroupColors - backdrop color', () => { - it('should return custom color', () => { - expect( - getFABGroupColors({ - theme: getTheme(), - customBackdropColor: 'transparent', - }) - ).toMatchObject({ - backdropColor: 'transparent', - }); - }); - - it('should return correct backdrop color, for theme version 3', () => { - expect( - getFABGroupColors({ - theme: getTheme(), - }) - ).toMatchObject({ - backdropColor: getTheme().colors.background, - }); - }); -}); - -describe('getFABGroupColors - backdrop opacity', () => { - it('should return scrimAlpha when no custom backdrop color', () => { - expect( - getFABGroupColors({ - theme: getTheme(), - }) - ).toMatchObject({ - backdropOpacity: 0.95, - }); - }); - - it('should return 1 when custom backdrop color is provided', () => { - expect( - getFABGroupColors({ - theme: getTheme(), - customBackdropColor: 'transparent', - }) - ).toMatchObject({ - backdropOpacity: 1, - }); - }); -}); - -describe('getFABGroupColors - label color', () => { - it('should return correct theme color, for theme version 3', () => { - expect( - getFABGroupColors({ - theme: getTheme(), - }) - ).toMatchObject({ - labelColor: getTheme().colors.onSurface, - }); - }); -}); - -describe('getFABGroupColors - stacked FAB background color', () => { - it('should return correct theme color, for theme version 3', () => { - expect( - getFABGroupColors({ - theme: getTheme(), - }) - ).toMatchObject({ - stackedFABBackgroundColor: getTheme().colors.surfaceContainerHigh, - }); - }); -}); - -describe('FABActions - labelStyle - containerStyle', () => { - it('correctly applies label style', () => { - const { getByText } = render( - {}} - actions={[ - { - label: 'complete', - labelStyle: { - fontSize: 24, - fontWeight: '500', - }, - onPress() {}, - icon: '', - }, - ]} - /> - ); - - expect(getByText('complete')).toHaveStyle({ - fontSize: 24, - fontWeight: '500', - }); - }); - - it('correctly applies containerStyle style', () => { - const { getByA11yHint } = render( - {}} - actions={[ - { - label: 'remove', - accessibilityHint: 'hint', - containerStyle: { - padding: 16, - backgroundColor: '#687456', - marginLeft: 16, - }, - onPress() {}, - icon: '', - }, - ]} - /> - ); - - expect(getByA11yHint('hint')).toHaveStyle({ - padding: 16, - backgroundColor: '#687456', - }); - }); -}); - -it('correctly adds label prop', () => { - const { getByText } = render( - {}} - actions={[ - { - label: 'testing', - onPress() {}, - icon: '', - }, - ]} - /> - ); - - expect(getByText('Label test')).toBeTruthy(); -}); - -it('animated value changes correctly', () => { - const value = new Animated.Value(1); - const { getByTestId } = render( - {}} - testID="my-fab" - fabStyle={[{ transform: [{ scale: value }] }]} - actions={[ - { - label: 'testing', - onPress() {}, - icon: '', - }, - ]} - /> - ); - expect(getByTestId('my-fab-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - act(() => { - jest.advanceTimersByTime(200); - }); - expect(getByTestId('my-fab-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); - -describe('FAB.Group events', () => { - it('onPress passes event', () => { - const onPress = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onPress', { key: 'value' }); - }); - - expect(onPress).toHaveBeenCalledWith({ key: 'value' }); - }); - - it('onLongPress passes event', () => { - const onLongPress = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onLongPress', { key: 'value' }); - }); - - expect(onLongPress).toHaveBeenCalledWith({ key: 'value' }); - }); -}); - -describe('Toggle Stack visibility', () => { - it('toggles stack visibility on press', () => { - const onStateChange = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onPress'); - }); - - expect(onStateChange).toHaveBeenCalledTimes(1); - }); - - it('does not toggle stack visibility on long press', () => { - const onStateChange = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onLongPress'); - }); - - expect(onStateChange).toHaveBeenCalledTimes(0); - }); - - it('toggles stack visibility on long press with toggleStackOnLongPress prop', () => { - const onStateChange = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onLongPress'); - }); - - expect(onStateChange).toHaveBeenCalledTimes(1); - }); - - it('does not toggle stack visibility on press with toggleStackOnLongPress prop', () => { - const onStateChange = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onPress'); - }); - - expect(onStateChange).toHaveBeenCalledTimes(0); - }); - - it('does not trigger onLongPress when stack is opened', () => { - const onStateChange = jest.fn(); - const onLongPress = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onLongPress'); - }); - - expect(onLongPress).toHaveBeenCalledTimes(0); - }); - - it('does trigger onLongPress when stack is opened and enableLongPressWhenStackOpened is true', () => { - const onStateChange = jest.fn(); - const onLongPress = jest.fn(); - const { getByText } = render( - - ); - - act(() => { - fireEvent(getByText('Stack test'), 'onLongPress'); - }); - - expect(onLongPress).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/components/__tests__/FABMenu.test.tsx b/src/components/__tests__/FABMenu.test.tsx new file mode 100644 index 0000000000..cff16cc76f --- /dev/null +++ b/src/components/__tests__/FABMenu.test.tsx @@ -0,0 +1,184 @@ +import * as React from 'react'; + +import { fireEvent } from '@testing-library/react-native'; + +import { render } from '../../test-utils'; +import FAB from '../FAB'; + +const makeItems = ( + onItemPress = jest.fn() +): [ + { label: string; onPress: jest.Mock; testID: string }, + { label: string; onPress: jest.Mock; testID: string } +] => [ + { label: 'Send email', onPress: onItemPress, testID: 'item-0' }, + { label: 'Set reminder', onPress: onItemPress, testID: 'item-1' }, +]; + +it('renders FAB.Menu closed', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders FAB.Menu open', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders FAB.Menu with 6 items', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders FAB.Menu with start alignment', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders FAB.Menu with center alignment', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders FAB.Menu with items having icons', () => { + const tree = render( + + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + +it('renders FAB.Menu not expanded when trigger is not visible', () => { + const tree = render( + + ).toJSON(); + // effectiveExpanded = visible && expanded = false + expect(tree).toMatchSnapshot(); +}); + +it('calls item onPress when menu item is pressed', () => { + const onItemPress = jest.fn(); + const { getByTestId } = render( + + ); + fireEvent.press(getByTestId('item-0')); + expect(onItemPress).toHaveBeenCalledTimes(1); +}); + +it('forwards event object to item onPress', () => { + const onItemPress = jest.fn(); + const { getByTestId } = render( + + ); + fireEvent(getByTestId('item-0'), 'onPress', { key: 'value' }); + expect(onItemPress).toHaveBeenCalledWith({ key: 'value' }); +}); + +it('calls onDismiss when menu item is pressed', () => { + const onDismiss = jest.fn(); + const { getByTestId } = render( + + ); + fireEvent.press(getByTestId('item-0')); + expect(onDismiss).toHaveBeenCalledTimes(1); +}); + +it('calls trigger onPress when menu is closed', () => { + const onTriggerPress = jest.fn(); + const { getByTestId } = render( + + ); + // Shell's TouchableRipple uses the default testID 'fab-shell' + fireEvent.press(getByTestId('fab-shell')); + expect(onTriggerPress).toHaveBeenCalledTimes(1); +}); + +it('calls onDismiss when trigger is pressed while menu is open', () => { + const onDismiss = jest.fn(); + const { getByTestId } = render( + + ); + fireEvent.press(getByTestId('fab-shell')); + expect(onDismiss).toHaveBeenCalledTimes(1); +}); diff --git a/src/components/__tests__/FABUtils.test.tsx b/src/components/__tests__/FABUtils.test.tsx new file mode 100644 index 0000000000..af00e98f1c --- /dev/null +++ b/src/components/__tests__/FABUtils.test.tsx @@ -0,0 +1,157 @@ +import { getTheme } from '../../core/theming'; +import { getDimensions, resolveColors } from '../FAB/utils'; + +describe('resolveColors', () => { + it('returns theme colors for default variant (tonalPrimary)', () => { + const theme = getTheme(); + const colors = resolveColors({ theme }); + expect(colors).toEqual({ + container: theme.colors.primaryContainer, + content: theme.colors.onPrimaryContainer, + }); + }); + + it('returns theme colors for primary variant', () => { + const theme = getTheme(); + const colors = resolveColors({ theme, variant: 'primary' }); + expect(colors).toEqual({ + container: theme.colors.primary, + content: theme.colors.onPrimary, + }); + }); + + it('returns theme colors for secondary variant', () => { + const theme = getTheme(); + const colors = resolveColors({ theme, variant: 'secondary' }); + expect(colors).toEqual({ + container: theme.colors.secondary, + content: theme.colors.onSecondary, + }); + }); + + it('returns theme colors for tertiary variant', () => { + const theme = getTheme(); + const colors = resolveColors({ theme, variant: 'tertiary' }); + expect(colors).toEqual({ + container: theme.colors.tertiary, + content: theme.colors.onTertiary, + }); + }); + + it('returns theme colors for tonalSecondary variant', () => { + const theme = getTheme(); + const colors = resolveColors({ theme, variant: 'tonalSecondary' }); + expect(colors).toEqual({ + container: theme.colors.secondaryContainer, + content: theme.colors.onSecondaryContainer, + }); + }); + + it('returns theme colors for tonalTertiary variant', () => { + const theme = getTheme(); + const colors = resolveColors({ theme, variant: 'tonalTertiary' }); + expect(colors).toEqual({ + container: theme.colors.tertiaryContainer, + content: theme.colors.onTertiaryContainer, + }); + }); + + it('containerColor override wins over variant', () => { + const theme = getTheme(); + const colors = resolveColors({ + theme, + variant: 'primary', + containerColor: '#custom', + }); + expect(colors.container).toBe('#custom'); + }); + + it('derives content color from containerColor when contentColor is not set', () => { + const theme = getTheme(); + // Use a known theme color so contentColorFor can derive the on-color pairing + const colors = resolveColors({ + theme, + containerColor: theme.colors.primary, + }); + expect(colors.container).toBe(theme.colors.primary); + expect(colors.content).toBe(theme.colors.onPrimary); + }); + + it('both containerColor and contentColor overrides win', () => { + const theme = getTheme(); + const colors = resolveColors({ + theme, + containerColor: '#bg', + contentColor: '#fg', + }); + expect(colors).toEqual({ container: '#bg', content: '#fg' }); + }); + + it('contentColor-only override uses variant container', () => { + const theme = getTheme(); + const colors = resolveColors({ + theme, + variant: 'primary', + contentColor: '#custom', + }); + expect(colors.container).toBe(theme.colors.primary); + expect(colors.content).toBe('#custom'); + }); +}); + +describe('getDimensions', () => { + it('returns correct dimensions for default size', () => { + const dims = getDimensions({ theme: getTheme() }); + expect(dims.height).toBe(56); + expect(dims.width).toBe(56); + expect(dims.iconSize).toBe(24); + expect(dims.leading).toBe(16); + expect(dims.trailing).toBe(16); + expect(dims.iconLabelGap).toBe(8); + expect(dims.labelTypescale).toBe('titleMedium'); + }); + + it('returns correct dimensions for medium size', () => { + const dims = getDimensions({ theme: getTheme(), size: 'medium' }); + expect(dims.height).toBe(80); + expect(dims.width).toBe(80); + expect(dims.iconSize).toBe(28); + expect(dims.leading).toBe(26); + expect(dims.trailing).toBe(26); + expect(dims.iconLabelGap).toBe(12); + expect(dims.labelTypescale).toBe('titleLarge'); + }); + + it('returns correct dimensions for large size', () => { + const dims = getDimensions({ theme: getTheme(), size: 'large' }); + expect(dims.height).toBe(96); + expect(dims.width).toBe(96); + expect(dims.iconSize).toBe(36); + expect(dims.leading).toBe(30); + expect(dims.trailing).toBe(30); + expect(dims.iconLabelGap).toBe(16); + expect(dims.labelTypescale).toBe('headlineSmall'); + }); + + it('shape override changes borderRadius compared to default', () => { + const theme = getTheme(); + const defaultDims = getDimensions({ theme }); + const fullDims = getDimensions({ theme, shape: 'full' }); + expect(fullDims.borderRadius).not.toBe(defaultDims.borderRadius); + }); + + it('iconSize override wins over size spec', () => { + const dims = getDimensions({ theme: getTheme(), iconSize: 32 }); + expect(dims.iconSize).toBe(32); + }); + + it('leading and trailing overrides win over size spec', () => { + const dims = getDimensions({ + theme: getTheme(), + leading: 20, + trailing: 24, + }); + expect(dims.leading).toBe(20); + expect(dims.trailing).toBe(24); + }); +}); diff --git a/src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap b/src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap deleted file mode 100644 index fad6addacc..0000000000 --- a/src/components/__tests__/__snapshots__/AnimatedFAB.test.tsx.snap +++ /dev/null @@ -1,866 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders animated fab 1`] = ` - - - - - - - - - - - - - - - - - - plus - - - - - - - -`; - -exports[`renders animated fab with label on the left 1`] = ` - - - - - - - - - - - - - - - - - - plus - - - - - text - - - - -`; - -exports[`renders animated fab with label on the right by default 1`] = ` - - - - - - - - - - - - - - - - - - plus - - - - - text - - - - -`; diff --git a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap index 386ce68373..56dd997edc 100644 --- a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap @@ -1,47 +1,55 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders FAB with custom size prop 1`] = ` +exports[`renders FAB large size 1`] = ` - + - - + - - plus - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders custom color for the icon and label of the FAB 1`] = ` +exports[`renders FAB medium size 1`] = ` - + - - + - - plus - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders default FAB 1`] = ` +exports[`renders FAB transitioning to not visible 1`] = ` - + - - + - - plus - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders extended FAB 1`] = ` +exports[`renders FAB transitioning to visible 1`] = ` - + - - - - plus - - - + ], + ] + } + > - Add items + plus - -`; - -exports[`renders extended FAB with custom size prop 1`] = ` - - + +`; + +exports[`renders FAB with accessibilityLabel 1`] = ` + + - - - - plus - - - + ], + ] + } + > - Add items + plus + `; -exports[`renders large FAB 1`] = ` +exports[`renders FAB with containerColor and contentColor overrides 1`] = ` - + - - + - - plus - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders loading FAB 1`] = ` +exports[`renders FAB with containerColor override 1`] = ` - + - - - - - - - - - - - - - - - - - - - - - - - - - - + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders loading FAB with custom size prop 1`] = ` +exports[`renders FAB with default props 1`] = ` - + - - - - - - - - - - - - - - - - - - - - - - - - - - + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders not visible FAB 1`] = ` +exports[`renders FAB with primary variant 1`] = ` - + - - + - - plus - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders small FAB 1`] = ` +exports[`renders FAB with secondary variant 1`] = ` + + + + plus + + + + + + +`; + +exports[`renders FAB with tertiary variant 1`] = ` + + - - + - - plus - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; -exports[`renders visible FAB 1`] = ` +exports[`renders FAB with tonalSecondary variant 1`] = ` + + + + plus + + + + + + +`; + +exports[`renders FAB with tonalTertiary variant 1`] = ` + + - - + - - plus - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + plus + + `; diff --git a/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap b/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap new file mode 100644 index 0000000000..48d20d29eb --- /dev/null +++ b/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap @@ -0,0 +1,1435 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders extended FAB collapsed 1`] = ` +[ + + + + + + plus + + + + New message + + + + + + + , + + + New message + + , +] +`; + +exports[`renders extended FAB expanded 1`] = ` +[ + + + + + + plus + + + + New message + + + + + + + , + + + New message + + , +] +`; + +exports[`renders extended FAB large size 1`] = ` +[ + + + + + + plus + + + + New message + + + + + + + , + + + New message + + , +] +`; + +exports[`renders extended FAB medium size 1`] = ` +[ + + + + + + plus + + + + New message + + + + + + + , + + + New message + + , +] +`; + +exports[`renders extended FAB not visible 1`] = ` +[ + + + + + + plus + + + + New message + + + + + + + , + + + New message + + , +] +`; + +exports[`renders extended FAB transitioning to collapsed 1`] = ` +[ + + + + + + plus + + + + New message + + + + + + + , + + + New message + + , +] +`; diff --git a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap new file mode 100644 index 0000000000..aadf81f409 --- /dev/null +++ b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap @@ -0,0 +1,5515 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders FAB.Menu closed 1`] = ` + + + + + + + + + + + Send email + + + + + + + + + + + + + + + + Set reminder + + + + + + + + + + + + + + + + + + + plus + + + + + close + + + + + + + + + + +`; + +exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` + + + + + + + + + + + Send email + + + + + + + + + + + + + + + + Set reminder + + + + + + + + + + + + + + + + + + + plus + + + + + close + + + + + + + + + + +`; + +exports[`renders FAB.Menu open 1`] = ` + + + + + + + + + + + Send email + + + + + + + + + + + + + + + + Set reminder + + + + + + + + + + + + + + + + + + + plus + + + + + close + + + + + + + + + + +`; + +exports[`renders FAB.Menu with 6 items 1`] = ` + + + + + + + + + + + Item 1 + + + + + + + + + + + + + + + + Item 2 + + + + + + + + + + + + + + + + Item 3 + + + + + + + + + + + + + + + + Item 4 + + + + + + + + + + + + + + + + Item 5 + + + + + + + + + + + + + + + + Item 6 + + + + + + + + + + + + + + + + + + + plus + + + + + close + + + + + + + + + + +`; + +exports[`renders FAB.Menu with center alignment 1`] = ` + + + + + + + + + + + Send email + + + + + + + + + + + + + + + + Set reminder + + + + + + + + + + + + + + + + + + + plus + + + + + close + + + + + + + + + + +`; + +exports[`renders FAB.Menu with items having icons 1`] = ` + + + + + + + + + + email + + + + Send email + + + + + + + + + + + + + + + bell + + + + Set reminder + + + + + + + + + + + + + + + + + + + plus + + + + + close + + + + + + + + + + +`; + +exports[`renders FAB.Menu with start alignment 1`] = ` + + + + + + + + + + + Send email + + + + + + + + + + + + + + + + Set reminder + + + + + + + + + + + + + + + + + + + plus + + + + + close + + + + + + + + + + +`; diff --git a/src/index.tsx b/src/index.tsx index fdbf0b9be8..8863e2fa20 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -21,8 +21,6 @@ import * as List from './components/List/List'; export { Avatar, List, Drawer }; -export * from './components/FAB/AnimatedFAB'; - export { default as Badge } from './components/Badge'; export { default as ActivityIndicator } from './components/ActivityIndicator'; export { default as Banner } from './components/Banner'; @@ -35,7 +33,6 @@ export { default as DataTable } from './components/DataTable/DataTable'; export { default as Dialog } from './components/Dialog/Dialog'; export { default as Divider } from './components/Divider'; export { default as FAB } from './components/FAB'; -export { default as AnimatedFAB } from './components/FAB/AnimatedFAB'; export { default as Icon } from './components/Icon'; export { default as IconButton } from './components/IconButton/IconButton'; export { default as Menu } from './components/Menu/Menu'; @@ -58,7 +55,6 @@ export { default as Text, customText } from './components/Typography/Text'; // Types export type { Props as ActivityIndicatorProps } from './components/ActivityIndicator'; -export type { Props as AnimatedFABProps } from './components/FAB/AnimatedFAB'; export type { Props as AppbarProps } from './components/Appbar/Appbar'; export type { Props as AppbarActionProps } from './components/Appbar/AppbarAction'; export type { Props as AppbarBackActionProps } from './components/Appbar/AppbarBackAction'; @@ -99,7 +95,16 @@ export type { Props as DrawerCollapsedItemProps } from './components/Drawer/Draw export type { Props as DrawerItemProps } from './components/Drawer/DrawerItem'; export type { Props as DrawerSectionProps } from './components/Drawer/DrawerSection'; export type { Props as FABProps } from './components/FAB/FAB'; -export type { Props as FABGroupProps } from './components/FAB/FABGroup'; +export type { Props as FABExtendedProps } from './components/FAB/Extended'; +export type { + MenuProps as FABMenuProps, + MenuItemProps as FABMenuItemProps, + MenuTriggerProps as FABMenuTriggerProps, +} from './components/FAB/Menu'; +export type { + Variant as FABVariant, + Size as FABSize, +} from './components/FAB/tokens'; export type { Props as IconButtonProps } from './components/IconButton/IconButton'; export type { Props as ListAccordionProps } from './components/List/ListAccordion'; export type { Props as ListAccordionGroupProps } from './components/List/ListAccordionGroup'; diff --git a/src/theme/tokens/sys/elevation.ts b/src/theme/tokens/sys/elevation.ts index 153fe1141e..9fcc7cb4fe 100644 --- a/src/theme/tokens/sys/elevation.ts +++ b/src/theme/tokens/sys/elevation.ts @@ -43,6 +43,11 @@ export const elevationInputRange: Elevation[] = Object.values(defaultElevation); export const androidElevationLevels = [0, 1, 3, 6, 8, 12]; +export const SHADOW_OPACITY = 0.3; +// iOS shadowRadius is a Gaussian sigma; CSS blur-radius = 2*sigma. +// Dividing by this factor normalizes the spread to match Web. +export const IOS_SHADOW_RADIUS_FACTOR = 0.5; + export const shadowLayers = [ { shadowOpacity: 0.15, @@ -50,7 +55,7 @@ export const shadowLayers = [ shadowRadius: [0, 3, 6, 8, 10, 12], }, { - shadowOpacity: 0.3, + shadowOpacity: SHADOW_OPACITY, height: [0, 1, 1, 1, 2, 4], shadowRadius: [0, 2, 2, 3, 3, 4], }, @@ -87,7 +92,7 @@ export function shadow( shadowColor: ColorValue ): ViewStyle | AnimatedShadowStyle { if (Platform.OS === 'web') { - const webShadowColor = getShadowColor(shadowColor, 0.3); + const webShadowColor = getShadowColor(shadowColor, SHADOW_OPACITY); if (isAnimatedValue(elevation)) { return { @@ -117,23 +122,26 @@ export function shadow( }, shadowOpacity: elevation.interpolate({ inputRange: [0, 1], - outputRange: [0, 0.3], + outputRange: [0, SHADOW_OPACITY], extrapolate: 'clamp', }), shadowRadius: elevation.interpolate({ inputRange: elevationInputRange, - outputRange: shadowLayers[0].shadowRadius, + outputRange: shadowLayers[0].shadowRadius.map( + (r) => r * IOS_SHADOW_RADIUS_FACTOR + ), }), }; } return { shadowColor, - shadowOpacity: elevation ? 0.3 : 0, + shadowOpacity: elevation ? SHADOW_OPACITY : 0, shadowOffset: { width: 0, height: shadowLayers[0].height[elevation], }, - shadowRadius: shadowLayers[0].shadowRadius[elevation], + shadowRadius: + shadowLayers[0].shadowRadius[elevation] * IOS_SHADOW_RADIUS_FACTOR, }; } diff --git a/src/theme/utils/shape.ts b/src/theme/utils/shape.ts new file mode 100644 index 0000000000..2ce6b2feb6 --- /dev/null +++ b/src/theme/utils/shape.ts @@ -0,0 +1,22 @@ +import { cornerFull, cornerNone } from '../tokens/sys/shape'; +import type { Theme, ThemeShapeCorners } from '../types'; + +/** + * Token name accepted anywhere a corner radius is specified. `'none'` and + * `'full'` resolve to the `cornerNone` / `cornerFull` constants; named + * tokens key into `theme.shapes.corner`. + */ +export type ShapeToken = keyof ThemeShapeCorners | 'none' | 'full'; + +/** + * Resolve a `ShapeToken` to a numeric corner radius. + */ +export function resolveCornerRadius(theme: Theme, token: ShapeToken): number { + if (token === 'full') { + return cornerFull; + } + if (token === 'none') { + return cornerNone; + } + return theme.shapes.corner[token]; +} diff --git a/tsconfig.json b/tsconfig.json index cc590cc0ee..e2ff899f74 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,7 +31,7 @@ "skipLibCheck": true, "strict": true, "target": "esnext", - "types": ["jest", "node"] + "types": ["jest", "node", "@testing-library/jest-native"] }, "exclude": [ "lib/**/*" From 63443f4d3898bab82a95d02b6a3728b30a3957d4 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 15 Jun 2026 15:03:54 +0200 Subject: [PATCH 39/94] refactor: modernize typescript setup (#4999) --- .eslintrc | 2 + docs/babel.config.js | 5 +- docs/package.json | 1 + docs/src/components/AppStoreIcon.tsx | 2 - docs/src/components/DynamicColorTheme.tsx | 3 +- docs/src/components/ExtendedExample.tsx | 2 - docs/src/components/ExtendsLink.tsx | 2 - docs/src/components/GetStartedButtons.tsx | 1 - docs/src/components/GithubIcon.tsx | 2 - docs/src/components/GooglePlayIcon.tsx | 2 - docs/src/components/IconsList.tsx | 2 +- docs/src/components/Markdown.tsx | 2 - docs/src/components/PropTable.tsx | 2 - docs/src/components/ScreenshotTabs.tsx | 3 +- docs/src/components/Showcase.tsx | 2 - docs/src/components/Switch.tsx | 2 - docs/src/components/ThemeColorsTable.tsx | 9 +- docs/src/components/ThemeIcon.tsx | 2 - docs/src/utils/__tests__/themeColors.test.tsx | 2 + example/metro.config.js | 1 + example/package.json | 4 +- example/src/DrawerItems.tsx | 3 +- example/src/ExampleList.tsx | 1 - example/src/Examples/AvatarExample.tsx | 1 - example/src/Examples/BannerExample.tsx | 10 +- .../src/Examples/BottomNavigationExample.tsx | 8 +- example/src/Examples/ButtonExample.tsx | 1 - .../Dialogs/DialogWithCustomColors.tsx | 2 - .../DialogWithDismissableBackButton.tsx | 2 - .../src/Examples/Dialogs/DialogWithIcon.tsx | 1 - .../Dialogs/DialogWithLoadingIndicator.tsx | 1 - .../Examples/Dialogs/DialogWithLongText.tsx | 1 - .../Examples/Dialogs/UndismissableDialog.tsx | 2 - example/src/Examples/DividerExample.tsx | 1 - example/src/Examples/FABExample.tsx | 13 +- example/src/Examples/IconButtonExample.tsx | 1 - example/src/Examples/IconExample.tsx | 1 - example/src/Examples/ListItemExample.tsx | 1 - example/src/Examples/ListSectionExample.tsx | 1 - example/src/Examples/MenuExample.tsx | 8 +- example/src/Examples/ProgressBarExample.tsx | 2 +- .../src/Examples/SegmentedButtonsExample.tsx | 2 - example/src/Examples/SurfaceExample.tsx | 4 +- example/src/Examples/TeamsList.tsx | 1 - example/src/Examples/TextExample.tsx | 1 - example/src/Examples/ThemeExample.tsx | 1 - .../Examples/ThemingWithReactNavigation.tsx | 1 - example/src/Examples/ToggleButtonExample.tsx | 33 +- .../src/Examples/TouchableRippleExample.tsx | 1 - example/src/RootNavigator.tsx | 1 - example/src/ScreenWrapper.tsx | 10 +- example/utils/index.ts | 3 +- example/utils/themes.ts | 4 +- jest/jest-native.d.ts | 9 + package.json | 5 +- src/components/ActivityIndicator.tsx | 14 +- src/components/Appbar/Appbar.tsx | 18 +- src/components/Appbar/AppbarAction.tsx | 73 ++-- src/components/Appbar/AppbarBackAction.tsx | 25 +- src/components/Appbar/AppbarBackIcon.tsx | 4 +- src/components/Appbar/AppbarContent.tsx | 10 +- src/components/Appbar/AppbarHeader.tsx | 11 +- src/components/Avatar/AvatarIcon.tsx | 9 +- src/components/Avatar/AvatarImage.tsx | 9 +- src/components/Avatar/AvatarText.tsx | 13 +- src/components/Badge.tsx | 9 +- src/components/Banner.tsx | 6 +- .../BottomNavigation/BottomNavigation.tsx | 9 +- .../BottomNavigation/BottomNavigationBar.tsx | 13 +- .../BottomNavigationRouteScreen.tsx | 6 +- src/components/Button/Button.tsx | 92 ++--- src/components/Card/Card.tsx | 70 ++-- src/components/Card/CardActions.tsx | 7 +- src/components/Card/CardContent.tsx | 5 +- src/components/Card/CardCover.tsx | 6 +- src/components/Card/CardTitle.tsx | 11 +- src/components/Checkbox/CheckboxItem.tsx | 11 +- src/components/Chip/Chip.tsx | 16 +- src/components/CrossFadeIcon.tsx | 6 +- src/components/DataTable/DataTable.tsx | 5 +- src/components/DataTable/DataTableCell.tsx | 4 +- src/components/DataTable/DataTableHeader.tsx | 5 +- .../DataTable/DataTablePagination.tsx | 5 +- src/components/DataTable/DataTableRow.tsx | 5 +- src/components/DataTable/DataTableTitle.tsx | 10 +- src/components/Dialog/Dialog.tsx | 11 +- src/components/Dialog/DialogActions.tsx | 7 +- src/components/Dialog/DialogContent.tsx | 5 +- src/components/Dialog/DialogIcon.tsx | 7 +- src/components/Dialog/DialogScrollArea.tsx | 5 +- src/components/Dialog/DialogTitle.tsx | 3 +- src/components/Dialog/utils.ts | 2 +- src/components/Divider.tsx | 4 +- src/components/Drawer/DrawerCollapsedItem.tsx | 14 +- src/components/Drawer/DrawerItem.tsx | 16 +- src/components/Drawer/DrawerSection.tsx | 5 +- src/components/FAB/Content.tsx | 16 +- src/components/FAB/Extended.tsx | 244 ++++++------ src/components/FAB/FAB.tsx | 77 ++-- src/components/FAB/Menu.tsx | 15 +- src/components/FAB/Shell.tsx | 320 ++++++++------- src/components/FAB/utils.ts | 8 +- src/components/Icon.tsx | 9 +- src/components/IconButton/IconButton.tsx | 219 +++++------ src/components/List/ListAccordion.tsx | 10 +- src/components/List/ListIcon.tsx | 13 +- src/components/List/ListImage.tsx | 10 +- src/components/List/ListItem.tsx | 59 ++- src/components/List/ListSection.tsx | 11 +- src/components/List/ListSubheader.tsx | 3 +- src/components/List/utils.ts | 3 +- src/components/MaterialCommunityIcon.tsx | 10 +- src/components/Menu/Menu.tsx | 16 +- src/components/Menu/MenuItem.tsx | 13 +- src/components/Modal.tsx | 11 +- src/components/Portal/Portal.tsx | 3 +- src/components/ProgressBar.tsx | 10 +- src/components/RadioButton/RadioButton.tsx | 4 +- .../RadioButton/RadioButtonAndroid.tsx | 3 +- src/components/RadioButton/RadioButtonIOS.tsx | 7 +- .../RadioButton/RadioButtonItem.tsx | 14 +- src/components/Searchbar.tsx | 371 +++++++++--------- .../SegmentedButtons/SegmentedButtonItem.tsx | 11 +- .../SegmentedButtons/SegmentedButtons.tsx | 6 +- src/components/SegmentedButtons/utils.ts | 2 +- src/components/Snackbar.tsx | 10 +- src/components/Surface.tsx | 336 ++++++++-------- src/components/TextInput/TextInput.tsx | 21 +- .../TextInput/TextInputErrorIcon.tsx | 4 +- src/components/TextInput/TextInputIcon.tsx | 3 +- src/components/TextInput/hooks.ts | 7 +- src/components/TextInput/utils.ts | 13 +- src/components/ToggleButton/ToggleButton.tsx | 131 +++---- .../ToggleButton/ToggleButtonRow.tsx | 3 +- src/components/Tooltip/Tooltip.tsx | 6 +- src/components/Tooltip/utils.ts | 9 +- .../TouchableRipple.native.tsx | 41 +- .../TouchableRipple/TouchableRipple.tsx | 41 +- src/components/Typography/AnimatedText.tsx | 108 ++--- src/components/Typography/Text.tsx | 33 +- .../__tests__/ActivityIndicator.test.tsx | 2 +- .../__tests__/Appbar/Appbar.test.tsx | 24 +- ...ct-native-safe-area-context-jest-mock.d.ts | 5 - src/components/__tests__/Avatar.test.tsx | 2 +- src/components/__tests__/Badge.test.tsx | 2 +- src/components/__tests__/Banner.test.tsx | 10 +- .../__tests__/BottomNavigation.test.tsx | 19 +- src/components/__tests__/Button.test.tsx | 2 +- src/components/__tests__/Card/Card.test.tsx | 2 +- .../__tests__/Checkbox/Checkbox.test.tsx | 2 +- .../__tests__/Checkbox/CheckboxItem.test.tsx | 3 +- .../__tests__/Checkbox/utils.test.tsx | 2 + src/components/__tests__/Chip.test.tsx | 2 +- src/components/__tests__/DataTable.test.tsx | 2 +- src/components/__tests__/Dialog.test.tsx | 4 +- .../Drawer/DrawerCollapsedItem.test.tsx | 2 +- .../__tests__/Drawer/DrawerSection.test.tsx | 3 +- src/components/__tests__/DrawerItem.test.tsx | 2 +- src/components/__tests__/FAB.test.tsx | 3 +- src/components/__tests__/FABExtended.test.tsx | 3 +- src/components/__tests__/FABMenu.test.tsx | 9 +- src/components/__tests__/FABUtils.test.tsx | 2 + src/components/__tests__/Icon.test.tsx | 3 +- src/components/__tests__/IconButton.test.tsx | 2 +- .../__tests__/ListAccordion.test.tsx | 3 +- src/components/__tests__/ListImage.test.tsx | 3 +- src/components/__tests__/ListItem.test.tsx | 5 +- src/components/__tests__/ListSection.test.tsx | 3 +- src/components/__tests__/ListUtils.test.tsx | 3 +- src/components/__tests__/Menu.test.tsx | 4 +- src/components/__tests__/MenuItem.test.tsx | 2 +- src/components/__tests__/Modal.test.tsx | 10 +- src/components/__tests__/Portal.test.tsx | 2 +- src/components/__tests__/ProgressBar.test.tsx | 4 +- .../RadioButton/RadioButton.test.tsx | 42 +- .../RadioButton/RadioButtonGroup.test.tsx | 2 +- .../RadioButton/RadioButtonItem.test.tsx | 2 +- .../__tests__/RadioButton/utils.test.tsx | 2 + src/components/__tests__/Searchbar.test.tsx | 2 +- .../__tests__/SegmentedButton.test.tsx | 87 +++- src/components/__tests__/Snackbar.test.tsx | 2 +- src/components/__tests__/Surface.test.tsx | 157 ++++---- src/components/__tests__/Switch.test.tsx | 2 +- src/components/__tests__/TextInput.test.tsx | 12 +- .../__tests__/ToggleButton.test.tsx | 2 +- src/components/__tests__/Tooltip.test.tsx | 57 ++- .../__tests__/TouchableRipple.test.tsx | 7 +- .../__tests__/Typography/Text.test.tsx | 2 +- src/core/PaperProvider.tsx | 3 +- src/core/SafeAreaProviderCompat.tsx | 10 +- src/core/__tests__/PaperProvider.test.tsx | 96 +++-- src/core/__tests__/theming.test.tsx | 2 + src/core/settings.tsx | 5 +- src/core/useSystemColorScheme.ts | 3 +- src/theme/__tests__/provider.test.ts | 2 + src/theme/provider.tsx | 3 +- src/utils/__tests__/hasTouchHandler.test.ts | 2 + src/utils/__tests__/splitStyles.test.ts | 2 + src/utils/forwardRef.tsx | 23 -- src/utils/useIsKeyboardShown.tsx | 3 +- tsconfig.json | 13 +- yarn.lock | 110 +++--- 202 files changed, 1836 insertions(+), 1990 deletions(-) create mode 100644 jest/jest-native.d.ts delete mode 100644 src/components/__tests__/Appbar/react-native-safe-area-context-jest-mock.d.ts delete mode 100644 src/utils/forwardRef.tsx diff --git a/.eslintrc b/.eslintrc index 380df91c1e..223dd94031 100644 --- a/.eslintrc +++ b/.eslintrc @@ -16,6 +16,8 @@ "no-nested-ternary": "off", "no-undef": "off", "global-require": "off", + "react/react-in-jsx-scope": "off", + "react/jsx-uses-react": "off", "import/no-extraneous-dependencies": "off", "import/first": "off", diff --git a/docs/babel.config.js b/docs/babel.config.js index 1a907e925f..b36362b0fd 100644 --- a/docs/babel.config.js +++ b/docs/babel.config.js @@ -1,4 +1,7 @@ module.exports = { presets: ['@docusaurus/core/lib/babel/preset'], - plugins: ['react-native-reanimated/plugin'], + plugins: [ + ['@babel/plugin-transform-react-jsx', { runtime: 'automatic' }], + 'react-native-reanimated/plugin', + ], }; diff --git a/docs/package.json b/docs/package.json index 994a05fe16..ded467eb2b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -39,6 +39,7 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "^2.3.6", + "@jest/globals": "^29.7.0", "@tsconfig/docusaurus": "^1.0.6", "@types/marked": "^4.0.7", "@types/react-color": "^3.0.6", diff --git a/docs/src/components/AppStoreIcon.tsx b/docs/src/components/AppStoreIcon.tsx index eda83143e1..897c04c12e 100644 --- a/docs/src/components/AppStoreIcon.tsx +++ b/docs/src/components/AppStoreIcon.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - type Props = { color: string; }; diff --git a/docs/src/components/DynamicColorTheme.tsx b/docs/src/components/DynamicColorTheme.tsx index 24e1191b21..19f37f8d6b 100644 --- a/docs/src/components/DynamicColorTheme.tsx +++ b/docs/src/components/DynamicColorTheme.tsx @@ -1,4 +1,5 @@ -import React, { useState, ReactNode } from 'react'; +import React, { useState } from 'react'; +import type { ReactNode } from 'react'; import Color from 'color'; //@ts-ignore diff --git a/docs/src/components/ExtendedExample.tsx b/docs/src/components/ExtendedExample.tsx index c3eac15fdf..ec6a3363b4 100644 --- a/docs/src/components/ExtendedExample.tsx +++ b/docs/src/components/ExtendedExample.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - //@ts-ignore import CodeBlock from '@theme/CodeBlock'; //@ts-ignore diff --git a/docs/src/components/ExtendsLink.tsx b/docs/src/components/ExtendsLink.tsx index 9b39db2c8c..03b9462a48 100644 --- a/docs/src/components/ExtendsLink.tsx +++ b/docs/src/components/ExtendsLink.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - export default function ExtendsLink({ name, link, diff --git a/docs/src/components/GetStartedButtons.tsx b/docs/src/components/GetStartedButtons.tsx index fd00420b46..76a126600f 100644 --- a/docs/src/components/GetStartedButtons.tsx +++ b/docs/src/components/GetStartedButtons.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet, View } from 'react-native'; //@ts-ignore diff --git a/docs/src/components/GithubIcon.tsx b/docs/src/components/GithubIcon.tsx index c94cc381af..d768e3cdc9 100644 --- a/docs/src/components/GithubIcon.tsx +++ b/docs/src/components/GithubIcon.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - type Props = { color: string; }; diff --git a/docs/src/components/GooglePlayIcon.tsx b/docs/src/components/GooglePlayIcon.tsx index a0a88e36c5..e086eefd8c 100644 --- a/docs/src/components/GooglePlayIcon.tsx +++ b/docs/src/components/GooglePlayIcon.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - type Props = { color: string; }; diff --git a/docs/src/components/IconsList.tsx b/docs/src/components/IconsList.tsx index 73f26c91d7..791265b5b6 100644 --- a/docs/src/components/IconsList.tsx +++ b/docs/src/components/IconsList.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import { useState } from 'react'; const latestIcons: { [key in string]: number } = { ...require('../../node_modules/@react-native-vector-icons/material-design-icons/glyphmaps/MaterialDesignIcons.json'), diff --git a/docs/src/components/Markdown.tsx b/docs/src/components/Markdown.tsx index 27d429900f..2e0635ade0 100644 --- a/docs/src/components/Markdown.tsx +++ b/docs/src/components/Markdown.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - const { marked } = require('marked'); interface MarkdownProps { diff --git a/docs/src/components/PropTable.tsx b/docs/src/components/PropTable.tsx index 317d1e652e..39980fb703 100644 --- a/docs/src/components/PropTable.tsx +++ b/docs/src/components/PropTable.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - // @ts-ignore // eslint-disable-next-line import/no-unresolved import useDoc from '@site/component-docs-plugin/useDocs'; diff --git a/docs/src/components/ScreenshotTabs.tsx b/docs/src/components/ScreenshotTabs.tsx index e352c951d7..9eb9241480 100644 --- a/docs/src/components/ScreenshotTabs.tsx +++ b/docs/src/components/ScreenshotTabs.tsx @@ -1,4 +1,5 @@ -import React, { ReactNode } from 'react'; +import React from 'react'; +import type { ReactNode } from 'react'; //@ts-ignore import TabItem from '@theme/TabItem'; diff --git a/docs/src/components/Showcase.tsx b/docs/src/components/Showcase.tsx index 53c44f8999..40b95be11f 100644 --- a/docs/src/components/Showcase.tsx +++ b/docs/src/components/Showcase.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import color from 'color'; import AppStoreIcon from './AppStoreIcon'; diff --git a/docs/src/components/Switch.tsx b/docs/src/components/Switch.tsx index baa4f12bf0..30b7e62d03 100644 --- a/docs/src/components/Switch.tsx +++ b/docs/src/components/Switch.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - interface SwitchProps { value: boolean; onValueChange?: () => void; diff --git a/docs/src/components/ThemeColorsTable.tsx b/docs/src/components/ThemeColorsTable.tsx index ef7e5cd7f8..cf98c3e2df 100644 --- a/docs/src/components/ThemeColorsTable.tsx +++ b/docs/src/components/ThemeColorsTable.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import type { ReactNode } from 'react'; //@ts-ignore import Admonition from '@theme/Admonition'; @@ -7,11 +7,8 @@ import TabItem from '@theme/TabItem'; //@ts-ignore import Tabs from '@theme/Tabs'; -import { - DataObject, - getMaxNestedLevel, - getUniqueNestedKeys, -} from '../utils/themeColors'; +import { getMaxNestedLevel, getUniqueNestedKeys } from '../utils/themeColors'; +import type { DataObject } from '../utils/themeColors'; const getTableHeader = (keys: string[]): ReactNode[] => { return keys.map((key) => {key}); diff --git a/docs/src/components/ThemeIcon.tsx b/docs/src/components/ThemeIcon.tsx index 0f643f50db..d787e4b2a3 100644 --- a/docs/src/components/ThemeIcon.tsx +++ b/docs/src/components/ThemeIcon.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - type Props = { type: 'dark' | 'light'; }; diff --git a/docs/src/utils/__tests__/themeColors.test.tsx b/docs/src/utils/__tests__/themeColors.test.tsx index 4803ea7a6b..43a1f2af01 100644 --- a/docs/src/utils/__tests__/themeColors.test.tsx +++ b/docs/src/utils/__tests__/themeColors.test.tsx @@ -1,3 +1,5 @@ +import { describe, expect, it } from '@jest/globals'; + import { getMaxNestedLevel, getUniqueNestedKeys } from '../themeColors'; const Button = { diff --git a/example/metro.config.js b/example/metro.config.js index 418cdb3d81..b288ba7c49 100644 --- a/example/metro.config.js +++ b/example/metro.config.js @@ -6,4 +6,5 @@ const { withMetroConfig } = require('react-native-monorepo-config'); module.exports = withMetroConfig(getDefaultConfig(__dirname), { root: path.resolve(__dirname, '..'), dirname: __dirname, + conditions: ['react-native-paper-source'], }); diff --git a/example/package.json b/example/package.json index 992ead9c23..b7713e23fa 100644 --- a/example/package.json +++ b/example/package.json @@ -38,8 +38,8 @@ "devDependencies": { "@babel/core": "^7.29.0", "babel-preset-expo": "~56.0.0", - "react-native-builder-bob": "^0.41.0", - "react-native-monorepo-config": "^0.3.4" + "react-native-builder-bob": "^0.42.1", + "react-native-monorepo-config": "^0.4.0" }, "engines": { "node": ">=20" diff --git a/example/src/DrawerItems.tsx b/example/src/DrawerItems.tsx index ea4627bb64..c23e850adf 100644 --- a/example/src/DrawerItems.tsx +++ b/example/src/DrawerItems.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { ColorValue, Platform, StyleSheet, View } from 'react-native'; +import { Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue } from 'react-native'; import { DrawerContentScrollView } from '@react-navigation/drawer'; import Constants, { ExecutionEnvironment } from 'expo-constants'; diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index 18c958a4bb..53132e299f 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { FlatList } from 'react-native'; import { useNavigation } from '@react-navigation/native'; diff --git a/example/src/Examples/AvatarExample.tsx b/example/src/Examples/AvatarExample.tsx index 94a7da8570..6d6349cbba 100644 --- a/example/src/Examples/AvatarExample.tsx +++ b/example/src/Examples/AvatarExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import { Avatar, List, Palette } from 'react-native-paper'; diff --git a/example/src/Examples/BannerExample.tsx b/example/src/Examples/BannerExample.tsx index 78ef19fed4..b3a0d8a002 100644 --- a/example/src/Examples/BannerExample.tsx +++ b/example/src/Examples/BannerExample.tsx @@ -1,12 +1,6 @@ import * as React from 'react'; -import { - Dimensions, - Image, - LayoutChangeEvent, - Platform, - StyleSheet, - View, -} from 'react-native'; +import { Dimensions, Image, Platform, StyleSheet, View } from 'react-native'; +import type { LayoutChangeEvent } from 'react-native'; import { Banner, FAB, Palette, useTheme } from 'react-native-paper'; diff --git a/example/src/Examples/BottomNavigationExample.tsx b/example/src/Examples/BottomNavigationExample.tsx index 0c0fc8d4cc..8b2495fdbc 100644 --- a/example/src/Examples/BottomNavigationExample.tsx +++ b/example/src/Examples/BottomNavigationExample.tsx @@ -9,12 +9,8 @@ import { } from 'react-native'; import { useNavigation } from '@react-navigation/native'; -import { - Appbar, - BottomNavigation, - BottomNavigationRoute, - Menu, -} from 'react-native-paper'; +import { Appbar, BottomNavigation, Menu } from 'react-native-paper'; +import type { BottomNavigationRoute } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import ScreenWrapper from '../ScreenWrapper'; diff --git a/example/src/Examples/ButtonExample.tsx b/example/src/Examples/ButtonExample.tsx index b56b0a8327..e250644756 100644 --- a/example/src/Examples/ButtonExample.tsx +++ b/example/src/Examples/ButtonExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Image, StyleSheet, View } from 'react-native'; import { Button, List, Text, useTheme } from 'react-native-paper'; diff --git a/example/src/Examples/Dialogs/DialogWithCustomColors.tsx b/example/src/Examples/Dialogs/DialogWithCustomColors.tsx index c811523b9e..f3625b835e 100644 --- a/example/src/Examples/Dialogs/DialogWithCustomColors.tsx +++ b/example/src/Examples/Dialogs/DialogWithCustomColors.tsx @@ -1,5 +1,3 @@ -import * as React from 'react'; - import { Button, Portal, Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; diff --git a/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx b/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx index 21f647e75c..e9a7189068 100644 --- a/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx +++ b/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx @@ -1,5 +1,3 @@ -import * as React from 'react'; - import { Button, Portal, Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; diff --git a/example/src/Examples/Dialogs/DialogWithIcon.tsx b/example/src/Examples/Dialogs/DialogWithIcon.tsx index f12089193d..6281e9bf6c 100644 --- a/example/src/Examples/Dialogs/DialogWithIcon.tsx +++ b/example/src/Examples/Dialogs/DialogWithIcon.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet } from 'react-native'; import { Button, Portal, Dialog, Palette } from 'react-native-paper'; diff --git a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx index 86cbe67c0d..a28620e79c 100644 --- a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx +++ b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { ActivityIndicator, Platform, StyleSheet, View } from 'react-native'; import { Dialog, Palette, Portal } from 'react-native-paper'; diff --git a/example/src/Examples/Dialogs/DialogWithLongText.tsx b/example/src/Examples/Dialogs/DialogWithLongText.tsx index 3f826c1b40..eeac1c7d3c 100644 --- a/example/src/Examples/Dialogs/DialogWithLongText.tsx +++ b/example/src/Examples/Dialogs/DialogWithLongText.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Dimensions, ScrollView, StyleSheet } from 'react-native'; import { Button, Portal, Dialog } from 'react-native-paper'; diff --git a/example/src/Examples/Dialogs/UndismissableDialog.tsx b/example/src/Examples/Dialogs/UndismissableDialog.tsx index 3a9fe10cb8..208c809967 100644 --- a/example/src/Examples/Dialogs/UndismissableDialog.tsx +++ b/example/src/Examples/Dialogs/UndismissableDialog.tsx @@ -1,5 +1,3 @@ -import * as React from 'react'; - import { Button, Portal, Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; diff --git a/example/src/Examples/DividerExample.tsx b/example/src/Examples/DividerExample.tsx index c3c2c6b770..77df26a85a 100644 --- a/example/src/Examples/DividerExample.tsx +++ b/example/src/Examples/DividerExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { FlatList } from 'react-native'; import { Divider, List, useTheme } from 'react-native-paper'; diff --git a/example/src/Examples/FABExample.tsx b/example/src/Examples/FABExample.tsx index 04b8d2d140..09ce39aa14 100644 --- a/example/src/Examples/FABExample.tsx +++ b/example/src/Examples/FABExample.tsx @@ -1,24 +1,17 @@ import * as React from 'react'; -import { - FlatList, - NativeScrollEvent, - NativeSyntheticEvent, - ScrollView, - StyleSheet, - View, -} from 'react-native'; +import { FlatList, ScrollView, StyleSheet, View } from 'react-native'; +import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native'; import { Chip, Divider, FAB, - FABSize, - FABVariant, List, Switch, Text, useTheme, } from 'react-native-paper'; +import type { FABSize, FABVariant } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; type FabType = 'icon' | 'extended' | 'extendedTransforming' | 'menu'; diff --git a/example/src/Examples/IconButtonExample.tsx b/example/src/Examples/IconButtonExample.tsx index 5d2a22e235..5fb463880b 100644 --- a/example/src/Examples/IconButtonExample.tsx +++ b/example/src/Examples/IconButtonExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import { IconButton, List, Palette } from 'react-native-paper'; diff --git a/example/src/Examples/IconExample.tsx b/example/src/Examples/IconExample.tsx index 1fd2e15f35..b08a389f5c 100644 --- a/example/src/Examples/IconExample.tsx +++ b/example/src/Examples/IconExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet, View, Image } from 'react-native'; import { Icon, List } from 'react-native-paper'; diff --git a/example/src/Examples/ListItemExample.tsx b/example/src/Examples/ListItemExample.tsx index bad5b35893..4d5a69b400 100644 --- a/example/src/Examples/ListItemExample.tsx +++ b/example/src/Examples/ListItemExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { View, StyleSheet } from 'react-native'; import { List, Divider, Checkbox, Avatar, Switch } from 'react-native-paper'; diff --git a/example/src/Examples/ListSectionExample.tsx b/example/src/Examples/ListSectionExample.tsx index 79f4d7f624..d7efbe6adc 100644 --- a/example/src/Examples/ListSectionExample.tsx +++ b/example/src/Examples/ListSectionExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet, Image, View } from 'react-native'; import { List, Text, Chip, Divider } from 'react-native-paper'; diff --git a/example/src/Examples/MenuExample.tsx b/example/src/Examples/MenuExample.tsx index 831e811574..2a55069090 100644 --- a/example/src/Examples/MenuExample.tsx +++ b/example/src/Examples/MenuExample.tsx @@ -1,10 +1,6 @@ import * as React from 'react'; -import { - GestureResponderEvent, - Platform, - StyleSheet, - View, -} from 'react-native'; +import { Platform, StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { diff --git a/example/src/Examples/ProgressBarExample.tsx b/example/src/Examples/ProgressBarExample.tsx index 5b59566d6e..a42474b99d 100644 --- a/example/src/Examples/ProgressBarExample.tsx +++ b/example/src/Examples/ProgressBarExample.tsx @@ -5,10 +5,10 @@ import { Button, Palette, ProgressBar, - ProgressBarProps, Text, useTheme, } from 'react-native-paper'; +import type { ProgressBarProps } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; diff --git a/example/src/Examples/SegmentedButtonsExample.tsx b/example/src/Examples/SegmentedButtonsExample.tsx index d6f2d610a4..c2cbf782a5 100644 --- a/example/src/Examples/SegmentedButtonsExample.tsx +++ b/example/src/Examples/SegmentedButtonsExample.tsx @@ -1,5 +1,3 @@ -import * as React from 'react'; - import { useNavigation } from '@react-navigation/native'; import { List } from 'react-native-paper'; diff --git a/example/src/Examples/SurfaceExample.tsx b/example/src/Examples/SurfaceExample.tsx index b7e46b9053..449c9e4dda 100644 --- a/example/src/Examples/SurfaceExample.tsx +++ b/example/src/Examples/SurfaceExample.tsx @@ -1,7 +1,7 @@ -import * as React from 'react'; import { ScrollView, StyleSheet, View } from 'react-native'; -import { Elevation, Surface, Text, Palette, List } from 'react-native-paper'; +import { Surface, Text, Palette, List } from 'react-native-paper'; +import type { Elevation } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; diff --git a/example/src/Examples/TeamsList.tsx b/example/src/Examples/TeamsList.tsx index 98abd9cbbb..c75d2fac37 100644 --- a/example/src/Examples/TeamsList.tsx +++ b/example/src/Examples/TeamsList.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet } from 'react-native'; import { useNavigation } from '@react-navigation/native'; diff --git a/example/src/Examples/TextExample.tsx b/example/src/Examples/TextExample.tsx index ea814c1c18..18f1eb8b84 100644 --- a/example/src/Examples/TextExample.tsx +++ b/example/src/Examples/TextExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; import { diff --git a/example/src/Examples/ThemeExample.tsx b/example/src/Examples/ThemeExample.tsx index 39f033aa06..eb838f5147 100644 --- a/example/src/Examples/ThemeExample.tsx +++ b/example/src/Examples/ThemeExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { StyleSheet } from 'react-native'; import { useNavigation } from '@react-navigation/native'; diff --git a/example/src/Examples/ThemingWithReactNavigation.tsx b/example/src/Examples/ThemingWithReactNavigation.tsx index 394640ae6c..d18728415c 100644 --- a/example/src/Examples/ThemingWithReactNavigation.tsx +++ b/example/src/Examples/ThemingWithReactNavigation.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { View, StyleSheet, Platform } from 'react-native'; import Icon from '@react-native-vector-icons/material-design-icons'; diff --git a/example/src/Examples/ToggleButtonExample.tsx b/example/src/Examples/ToggleButtonExample.tsx index 1bd3724904..f07caaaffb 100644 --- a/example/src/Examples/ToggleButtonExample.tsx +++ b/example/src/Examples/ToggleButtonExample.tsx @@ -8,19 +8,21 @@ import ScreenWrapper from '../ScreenWrapper'; type StatusState = 'checked' | 'unchecked'; type Fruits = 'watermelon' | 'strawberries'; -enum FontsEnum { - NoFormat = 'no-format', - Italic = 'italic', - Bold = 'bold', - Underline = 'underlined', - ColorText = 'format-color', -} +const fonts = { + noFormat: 'no-format', + italic: 'italic', + bold: 'bold', + underline: 'underlined', + colorText: 'format-color', +} as const; + +type Font = (typeof fonts)[keyof typeof fonts]; const ToggleButtonExample = () => { const [first, setFirst] = React.useState('bold'); const [fruit, setFruit] = React.useState('watermelon'); const [status, setStatus] = React.useState('checked'); - const [font, setFont] = React.useState(FontsEnum.NoFormat); + const [font, setFont] = React.useState(fonts.noFormat); const handleFruit = (value: Fruits) => setFruit(value); @@ -53,17 +55,10 @@ const ToggleButtonExample = () => { - - - - + + + + diff --git a/example/src/Examples/TouchableRippleExample.tsx b/example/src/Examples/TouchableRippleExample.tsx index 37d8c6c8c1..41d511cb4f 100644 --- a/example/src/Examples/TouchableRippleExample.tsx +++ b/example/src/Examples/TouchableRippleExample.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { View, StyleSheet } from 'react-native'; import { Text, TouchableRipple } from 'react-native-paper'; diff --git a/example/src/RootNavigator.tsx b/example/src/RootNavigator.tsx index 7e0876dcd0..5631de73d2 100644 --- a/example/src/RootNavigator.tsx +++ b/example/src/RootNavigator.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; import { useNavigation } from '@react-navigation/native'; diff --git a/example/src/ScreenWrapper.tsx b/example/src/ScreenWrapper.tsx index c944d59eb1..a551da31b0 100644 --- a/example/src/ScreenWrapper.tsx +++ b/example/src/ScreenWrapper.tsx @@ -1,12 +1,6 @@ import * as React from 'react'; -import { - ScrollView, - ScrollViewProps, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { ScrollView, StyleSheet, View } from 'react-native'; +import type { ScrollViewProps, StyleProp, ViewStyle } from 'react-native'; import { useTheme } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; diff --git a/example/utils/index.ts b/example/utils/index.ts index bd26ae81e6..8cd541363c 100644 --- a/example/utils/index.ts +++ b/example/utils/index.ts @@ -1,6 +1,7 @@ import { Platform } from 'react-native'; -import { DarkTheme, LightTheme, Theme } from 'react-native-paper'; +import { DarkTheme, LightTheme } from 'react-native-paper'; +import type { Theme } from 'react-native-paper'; type ReducerAction = { payload: State[T]; diff --git a/example/utils/themes.ts b/example/utils/themes.ts index 3bfc622e2c..22fda309e7 100644 --- a/example/utils/themes.ts +++ b/example/utils/themes.ts @@ -1,15 +1,15 @@ import { DarkTheme as NavigationDarkTheme, DefaultTheme as NavigationDefaultTheme, - Theme as ReactNavigationTheme, } from '@react-navigation/native'; +import type { Theme as ReactNavigationTheme } from '@react-navigation/native'; import { adaptNavigationTheme, DarkTheme, LightTheme, configureFonts, - Theme, } from 'react-native-paper'; +import type { Theme } from 'react-native-paper'; const { LightTheme: NavLightTheme, DarkTheme: NavDarkTheme } = adaptNavigationTheme({ diff --git a/jest/jest-native.d.ts b/jest/jest-native.d.ts new file mode 100644 index 0000000000..d5dc396a76 --- /dev/null +++ b/jest/jest-native.d.ts @@ -0,0 +1,9 @@ +import '@jest/expect'; +import type { JestNativeMatchers } from '@testing-library/jest-native/extend-expect'; +import 'expect'; + +declare module 'expect' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Matchers, T = unknown> + extends JestNativeMatchers {} +} diff --git a/package.json b/package.json index 34ae7e3d9e..e5cf4ec146 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "5.15.1", "description": "Material design for React Native", "main": "lib/module/index.js", - "source": "src/index.tsx", + "react-native-paper-source": "src/index.tsx", "types": "lib/typescript/src/index.d.ts", "files": [ "src", @@ -58,6 +58,7 @@ "@babel/runtime": "^7.29.0", "@callstack/eslint-config": "^13.0.2", "@commitlint/config-conventional": "^8.3.4", + "@jest/globals": "^29.7.0", "@react-native-vector-icons/material-design-icons": "^12.0.0", "@react-native/babel-preset": "^0.85.3", "@react-native/jest-preset": "^0.85.3", @@ -93,7 +94,7 @@ "react": "19.2.3", "react-dom": "19.2.3", "react-native": "0.85.3", - "react-native-builder-bob": "^0.41.0", + "react-native-builder-bob": "^0.42.1", "react-native-reanimated": "4.3.1", "react-native-safe-area-context": "5.7.0", "react-native-worklets": "0.8.3", diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 95da56ae0c..9436979208 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -1,19 +1,11 @@ import * as React from 'react'; -import { - Animated, - ColorValue, - Easing, - Platform, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { Animated, Easing, Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../core/theming'; import type { ThemeProp } from '../types'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Whether to show the indicator or hide it. */ diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index 27948ad5ee..114ca59e20 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -1,30 +1,20 @@ import * as React from 'react'; -import { - Animated, - StyleProp, - StyleSheet, - View, - ViewStyle, - ColorValue, -} from 'react-native'; +import { Animated, StyleSheet, View } from 'react-native'; +import type { ColorValue, StyleProp, ViewProps, ViewStyle } from 'react-native'; import AppbarContent from './AppbarContent'; import { - AppbarModes, getAppbarBackgroundColor, modeAppbarHeight, renderAppbarContent, filterAppbarActions, - AppbarChildProps, } from './utils'; +import type { AppbarModes, AppbarChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { Elevation, ThemeProp } from '../../types'; import Surface from '../Surface'; -export type Props = Omit< - Partial>, - 'style' -> & { +export type Props = Omit, 'style'> & { /** * Whether the background color is a dark color. A dark appbar will render light text and vice-versa. */ diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index 2d0be0227c..0826372c8f 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -9,7 +9,6 @@ import type { import { useInternalTheme } from '../../core/theming'; import type { Theme, ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; @@ -45,7 +44,7 @@ export type Props = React.ComponentPropsWithoutRef & { */ isLeading?: boolean; style?: Animated.WithAnimatedValue>; - ref?: React.RefObject; + ref?: React.Ref; /** * @optional */ @@ -74,45 +73,41 @@ export type Props = React.ComponentPropsWithoutRef & { * export default MyComponent; * ``` */ -const AppbarAction = forwardRef( - ( - { - size = 24, - color: iconColor, - icon, - disabled, - onPress, - accessibilityLabel, - isLeading, - theme: themeOverrides, - ...rest - }: Props, - ref - ) => { - const theme = useInternalTheme(themeOverrides); - const { colors } = theme as Theme; +const AppbarAction = ({ + size = 24, + color: iconColor, + icon, + disabled, + onPress, + accessibilityLabel, + isLeading, + theme: themeOverrides, + ref, + ...rest +}: Props) => { + const theme = useInternalTheme(themeOverrides); + const { colors } = theme as Theme; - const actionIconColor = iconColor - ? iconColor - : isLeading - ? colors.onSurface - : colors.onSurfaceVariant; + const actionIconColor = iconColor + ? iconColor + : isLeading + ? colors.onSurface + : colors.onSurfaceVariant; - return ( - - ); - } -); + return ( + + ); +}; AppbarAction.displayName = 'Appbar.Action'; diff --git a/src/components/Appbar/AppbarBackAction.tsx b/src/components/Appbar/AppbarBackAction.tsx index ff24d9dad2..80c7df2f95 100644 --- a/src/components/Appbar/AppbarBackAction.tsx +++ b/src/components/Appbar/AppbarBackAction.tsx @@ -11,7 +11,6 @@ import type { import type { $Omit } from './../../types'; import AppbarAction from './AppbarAction'; import AppbarBackIcon from './AppbarBackIcon'; -import { forwardRef } from '../../utils/forwardRef'; export type Props = $Omit< React.ComponentPropsWithoutRef, @@ -38,7 +37,7 @@ export type Props = $Omit< */ onPress?: (e: GestureResponderEvent) => void; style?: Animated.WithAnimatedValue>; - ref?: React.RefObject; + ref?: React.Ref; }; /** @@ -58,16 +57,18 @@ export type Props = $Omit< * export default MyComponent; * ``` */ -const AppbarBackAction = forwardRef( - ({ accessibilityLabel = 'Back', ...rest }: Props, ref) => ( - - ) +const AppbarBackAction = ({ + accessibilityLabel = 'Back', + ref, + ...rest +}: Props) => ( + ); AppbarBackAction.displayName = 'Appbar.BackAction'; diff --git a/src/components/Appbar/AppbarBackIcon.tsx b/src/components/Appbar/AppbarBackIcon.tsx index 8981fc979f..ff827b3f5f 100644 --- a/src/components/Appbar/AppbarBackIcon.tsx +++ b/src/components/Appbar/AppbarBackIcon.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; -import { ColorValue, Image, Platform, StyleSheet, View } from 'react-native'; +import { Image, Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue } from 'react-native'; import { useLocale } from '../../core/locale'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index e899311694..513a4de04e 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -1,13 +1,10 @@ import * as React from 'react'; -import { +import { Platform, StyleSheet, Pressable, View } from 'react-native'; +import type { AccessibilityRole, GestureResponderEvent, - Platform, StyleProp, - StyleSheet, TextStyle, - Pressable, - View, ViewStyle, ViewProps, } from 'react-native'; @@ -20,7 +17,8 @@ import type { TypescaleKey, ThemeProp, } from '../../types'; -import Text, { TextRef } from '../Typography/Text'; +import Text from '../Typography/Text'; +import type { TextRef } from '../Typography/Text'; type TitleString = { title: string; diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index a51b0a3e69..d869242046 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -1,13 +1,6 @@ import * as React from 'react'; -import { - Animated, - ColorValue, - Platform, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { Animated, Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; diff --git a/src/components/Avatar/AvatarIcon.tsx b/src/components/Avatar/AvatarIcon.tsx index 44e543561a..9c3b813e59 100644 --- a/src/components/Avatar/AvatarIcon.tsx +++ b/src/components/Avatar/AvatarIcon.tsx @@ -1,15 +1,16 @@ -import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; import type { ThemeProp } from '../../types'; import getContrastingColor from '../../utils/getContrastingColor'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; const defaultSize = 64; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Icon to display for the `Avatar`. */ diff --git a/src/components/Avatar/AvatarImage.tsx b/src/components/Avatar/AvatarImage.tsx index 96dbba3869..51330dd70c 100644 --- a/src/components/Avatar/AvatarImage.tsx +++ b/src/components/Avatar/AvatarImage.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; -import { - Image, +import { Image, StyleSheet, View } from 'react-native'; +import type { ImageProps, ImageSourcePropType, StyleProp, - StyleSheet, - View, + ViewProps, ViewStyle, } from 'react-native'; @@ -18,7 +17,7 @@ export type AvatarImageSource = | ImageSourcePropType | ((props: { size: number }) => React.ReactNode); -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Image to display for the `Avatar`. * It accepts a standard React Native Image `source` prop diff --git a/src/components/Avatar/AvatarText.tsx b/src/components/Avatar/AvatarText.tsx index 4014dbb3b0..d9e16c8090 100644 --- a/src/components/Avatar/AvatarText.tsx +++ b/src/components/Avatar/AvatarText.tsx @@ -1,12 +1,5 @@ -import * as React from 'react'; -import { - StyleProp, - StyleSheet, - TextStyle, - useWindowDimensions, - View, - ViewStyle, -} from 'react-native'; +import { StyleSheet, useWindowDimensions, View } from 'react-native'; +import type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; @@ -16,7 +9,7 @@ import Text from '../Typography/Text'; const defaultSize = 64; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Initials to show as the text in the `Avatar`. */ diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index d8bf38015b..0fe09c6558 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -1,11 +1,6 @@ import * as React from 'react'; -import { - Animated, - StyleProp, - StyleSheet, - TextStyle, - useWindowDimensions, -} from 'react-native'; +import { Animated, StyleSheet, useWindowDimensions } from 'react-native'; +import type { StyleProp, TextStyle } from 'react-native'; import { useInternalTheme } from '../core/theming'; import type { ThemeProp } from '../types'; diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index b1a00b4ce4..967c627149 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -1,11 +1,13 @@ import * as React from 'react'; -import { Animated, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { Animated, StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import type { LayoutChangeEvent } from 'react-native'; import useLatestCallback from 'use-latest-callback'; import Button from './Button/Button'; -import Icon, { IconSource } from './Icon'; +import Icon from './Icon'; +import type { IconSource } from './Icon'; import Surface from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index 09de625d6b..1f18b17aae 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -1,12 +1,9 @@ import * as React from 'react'; -import { - Animated, +import { Animated, Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, EasingFunction, - Platform, StyleProp, - StyleSheet, - View, ViewStyle, } from 'react-native'; @@ -18,7 +15,7 @@ import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import useAnimatedValueArray from '../../utils/useAnimatedValueArray'; import type { IconSource } from '../Icon'; -import { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; export type BaseRoute = { key: string; diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index 57e96f67cc..83998977b9 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -1,13 +1,9 @@ import * as React from 'react'; -import { - Animated, +import { Animated, Platform, StyleSheet, Pressable, View } from 'react-native'; +import type { ColorValue, EasingFunction, - Platform, StyleProp, - StyleSheet, - Pressable, - View, ViewStyle, } from 'react-native'; @@ -25,10 +21,11 @@ import useAnimatedValueArray from '../../utils/useAnimatedValueArray'; import useIsKeyboardShown from '../../utils/useIsKeyboardShown'; import useLayout from '../../utils/useLayout'; import Badge from '../Badge'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; import Surface from '../Surface'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; -import { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; type BaseRoute = { diff --git a/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx b/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx index e91f3ebf4c..5773e2e1fd 100644 --- a/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx +++ b/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx @@ -1,5 +1,7 @@ -import React, { ReactNode } from 'react'; -import { Animated, Platform, View, ViewProps } from 'react-native'; +import React from 'react'; +import type { ReactNode } from 'react'; +import { Animated, Platform, View } from 'react-native'; +import type { ViewProps } from 'react-native'; interface Props extends ViewProps { visibility?: 0 | 1 | Animated.AnimatedInterpolation; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 17d40fb035..b2a1a21fdf 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,34 +1,27 @@ import * as React from 'react'; -import { +import { Animated, Platform, StyleSheet, View } from 'react-native'; +import type { AccessibilityRole, - Animated, ColorValue, GestureResponderEvent, - Platform, PressableAndroidRippleConfig, StyleProp, - StyleSheet, TextStyle, - View, ViewStyle, } from 'react-native'; -import { - ButtonMode, - getButtonColors, - getButtonTouchableRippleStyle, -} from './utils'; +import { getButtonColors, getButtonTouchableRippleStyle } from './utils'; +import type { ButtonMode } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $Omit, Theme, ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import hasTouchHandler from '../../utils/hasTouchHandler'; import { splitStyles } from '../../utils/splitStyles'; import ActivityIndicator from '../ActivityIndicator'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; import Surface from '../Surface'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; export type Props = $Omit, 'mode'> & { @@ -142,6 +135,7 @@ export type Props = $Omit, 'mode'> & { * Reference for the touchable */ touchableRef?: React.RefObject; + ref?: React.Ref; /** * testID to be used on tests. */ @@ -165,40 +159,38 @@ export type Props = $Omit, 'mode'> & { * export default MyComponent; * ``` */ -const Button = ( - { - disabled, - compact, - mode = 'text', - dark, - loading, - icon, - buttonColor: customButtonColor, - textColor: customTextColor, - children, - accessibilityLabel, - accessibilityHint, - accessibilityRole = 'button', - hitSlop, - onPress, - onPressIn, - onPressOut, - onLongPress, - delayLongPress, - style, - theme: themeOverrides, - uppercase: uppercaseProp, - contentStyle, - labelStyle, - testID = 'button', - accessible, - background, - maxFontSizeMultiplier, - touchableRef, - ...rest - }: Props, - ref: React.ForwardedRef -) => { +const Button = ({ + disabled, + compact, + mode = 'text', + dark, + loading, + icon, + buttonColor: customButtonColor, + textColor: customTextColor, + children, + accessibilityLabel, + accessibilityHint, + accessibilityRole = 'button', + hitSlop, + onPress, + onPressIn, + onPressOut, + onLongPress, + delayLongPress, + style, + theme: themeOverrides, + uppercase: uppercaseProp, + contentStyle, + labelStyle, + testID = 'button', + accessible, + background, + maxFontSizeMultiplier, + touchableRef, + ref, + ...rest +}: Props) => { const theme = useInternalTheme(themeOverrides); const isMode = React.useCallback( (modeToCompare: ButtonMode) => { @@ -502,4 +494,4 @@ const styles = StyleSheet.create({ }, }); -export default forwardRef(Button); +export default Button; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 6dc433322a..e1cc58c52a 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -1,13 +1,6 @@ import * as React from 'react'; -import { - Animated, - GestureResponderEvent, - StyleProp, - StyleSheet, - Pressable, - View, - ViewStyle, -} from 'react-native'; +import { Animated, StyleSheet, Pressable, View } from 'react-native'; +import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; import useLatestCallback from 'use-latest-callback'; @@ -18,18 +11,10 @@ import CardTitle from './CardTitle'; import { getCardColors } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $Omit, ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import hasTouchHandler from '../../utils/hasTouchHandler'; import { splitStyles } from '../../utils/splitStyles'; import Surface from '../Surface'; -type CardComposition = { - Content: typeof CardContent; - Actions: typeof CardActions; - Cover: typeof CardCover; - Title: typeof CardTitle; -}; - type OutlinedCardProps = { mode: 'outlined'; elevation?: never; @@ -137,26 +122,24 @@ export type Props = $Omit, 'mode'> & { * ``` */ -const Card = ( - { - elevation: cardElevation = 1, - delayLongPress, - onPress, - onLongPress, - onPressOut, - onPressIn, - mode: cardMode = 'elevated', - children, - style, - contentStyle, - theme: themeOverrides, - testID = 'card', - accessible, - disabled, - ...rest - }: (OutlinedCardProps | ElevatedCardProps | ContainedCardProps) & Props, - ref: React.ForwardedRef -) => { +const Card = ({ + elevation: cardElevation = 1, + delayLongPress, + onPress, + onLongPress, + onPressOut, + onPressIn, + mode: cardMode = 'elevated', + children, + style, + contentStyle, + theme: themeOverrides, + testID = 'card', + accessible, + disabled, + ref, + ...rest +}: (OutlinedCardProps | ElevatedCardProps | ContainedCardProps) & Props) => { const theme = useInternalTheme(themeOverrides); const isMode = React.useCallback( (modeToCompare: Mode) => { @@ -292,18 +275,15 @@ const Card = ( }; Card.displayName = 'Card'; -const Component = forwardRef(Card); - -const CardComponent = Component as typeof Component & CardComposition; // @component ./CardContent.tsx -CardComponent.Content = CardContent; +Card.Content = CardContent; // @component ./CardActions.tsx -CardComponent.Actions = CardActions; +Card.Actions = CardActions; // @component ./CardCover.tsx -CardComponent.Cover = CardCover; +Card.Cover = CardCover; // @component ./CardTitle.tsx -CardComponent.Title = CardTitle; +Card.Title = CardTitle; const styles = StyleSheet.create({ innerContainer: { @@ -318,4 +298,4 @@ const styles = StyleSheet.create({ }, }); -export default CardComponent; +export default Card; diff --git a/src/components/Card/CardActions.tsx b/src/components/Card/CardActions.tsx index daa26a19fd..689a97d1c1 100644 --- a/src/components/Card/CardActions.tsx +++ b/src/components/Card/CardActions.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; -import { CardActionChildProps } from './utils'; +import type { CardActionChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Items inside the `CardActions`. */ diff --git a/src/components/Card/CardContent.tsx b/src/components/Card/CardContent.tsx index 7a23db4756..af69fd7459 100644 --- a/src/components/Card/CardContent.tsx +++ b/src/components/Card/CardContent.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; -import { StyleSheet, StyleProp, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Items inside the `Card.Content`. */ diff --git a/src/components/Card/CardCover.tsx b/src/components/Card/CardCover.tsx index 43d01cd852..e68bfb4adb 100644 --- a/src/components/Card/CardCover.tsx +++ b/src/components/Card/CardCover.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; -import { Image, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { Image, StyleSheet, View } from 'react-native'; +import type { ImageProps, StyleProp, ViewStyle } from 'react-native'; import { getCardCoverStyle } from './utils'; import { useInternalTheme } from '../../core/theming'; @@ -7,7 +7,7 @@ import { grey200 } from '../../theme/colors'; import type { ThemeProp } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ImageProps & { /** * @internal */ diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index a8cda33fdc..2737fe6430 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -1,17 +1,12 @@ import * as React from 'react'; -import { - StyleProp, - StyleSheet, - TextStyle, - View, - ViewStyle, -} from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { TypescaleKey, ThemeProp } from '../../types'; import Text from '../Typography/Text'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Text for the title. Note that this will only accept a string or ``-based node. */ diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 70c7ec737e..50e758a412 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -1,11 +1,9 @@ -import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, TextStyle, - View, ViewStyle, } from 'react-native'; @@ -13,9 +11,8 @@ import Checkbox from './Checkbox'; import { useInternalTheme } from '../../core/theming'; import { getStateLayer } from '../../theme/utils/state'; import type { ThemeProp, TypescaleKey } from '../../types'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; export type Props = { diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 78b240703c..c59d02fa6e 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -1,22 +1,19 @@ import * as React from 'react'; -import { +import { Animated, Platform, StyleSheet, Pressable, View } from 'react-native'; +import type { AccessibilityState, - Animated, ColorValue, GestureResponderEvent, - Platform, PressableAndroidRippleConfig, StyleProp, - StyleSheet, TextStyle, - Pressable, - View, ViewStyle, } from 'react-native'; import useLatestCallback from 'use-latest-callback'; -import { ChipAvatarProps, getChipColors } from './helpers'; +import { getChipColors } from './helpers'; +import type { ChipAvatarProps } from './helpers'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; import type { $Omit, EllipsizeProp, Theme, ThemeProp } from '../../types'; @@ -25,9 +22,8 @@ import type { IconSource } from '../Icon'; import Icon from '../Icon'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import Surface from '../Surface'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; export type Props = $Omit, 'mode'> & { diff --git a/src/components/CrossFadeIcon.tsx b/src/components/CrossFadeIcon.tsx index 54852fb1a7..db1fd244d8 100644 --- a/src/components/CrossFadeIcon.tsx +++ b/src/components/CrossFadeIcon.tsx @@ -1,7 +1,9 @@ import * as React from 'react'; -import { Animated, ColorValue, StyleSheet, View } from 'react-native'; +import { Animated, StyleSheet, View } from 'react-native'; +import type { ColorValue } from 'react-native'; -import Icon, { IconSource, isEqualIcon, isValidIcon } from './Icon'; +import Icon, { isEqualIcon, isValidIcon } from './Icon'; +import type { IconSource } from './Icon'; import { useInternalTheme } from '../core/theming'; import type { ThemeProp } from '../types'; diff --git a/src/components/DataTable/DataTable.tsx b/src/components/DataTable/DataTable.tsx index 42fe7ad080..d038b8c579 100644 --- a/src/components/DataTable/DataTable.tsx +++ b/src/components/DataTable/DataTable.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { StyleSheet, StyleProp, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import DataTableCell from './DataTableCell'; import DataTableHeader, { @@ -17,7 +18,7 @@ import DataTableTitle, { DataTableTitle as _DataTableTitle, } from './DataTableTitle'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Content of the `DataTable`. */ diff --git a/src/components/DataTable/DataTableCell.tsx b/src/components/DataTable/DataTableCell.tsx index 1801d1ecc8..ce59206af6 100644 --- a/src/components/DataTable/DataTableCell.tsx +++ b/src/components/DataTable/DataTableCell.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { - StyleSheet, +import { StyleSheet } from 'react-native'; +import type { StyleProp, ViewStyle, TextStyle, diff --git a/src/components/DataTable/DataTableHeader.tsx b/src/components/DataTable/DataTableHeader.tsx index 1fee2a02ab..cacad34340 100644 --- a/src/components/DataTable/DataTableHeader.tsx +++ b/src/components/DataTable/DataTableHeader.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Content of the `DataTableHeader`. */ diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 5e26051819..862185e9c5 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; @@ -10,7 +11,7 @@ import MaterialCommunityIcon from '../MaterialCommunityIcon'; import Menu from '../Menu/Menu'; import Text from '../Typography/Text'; -export type Props = React.ComponentPropsWithRef & +export type Props = ViewProps & PaginationControlsProps & PaginationDropdownProps & { /** diff --git a/src/components/DataTable/DataTableRow.tsx b/src/components/DataTable/DataTableRow.tsx index d492f4ce53..51964a5f34 100644 --- a/src/components/DataTable/DataTableRow.tsx +++ b/src/components/DataTable/DataTableRow.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent, StyleProp, - StyleSheet, - View, ViewProps, ViewStyle, } from 'react-native'; diff --git a/src/components/DataTable/DataTableTitle.tsx b/src/components/DataTable/DataTableTitle.tsx index b31ec8b899..4a972341e6 100644 --- a/src/components/DataTable/DataTableTitle.tsx +++ b/src/components/DataTable/DataTableTitle.tsx @@ -1,11 +1,9 @@ import * as React from 'react'; -import { - Animated, +import { Animated, PixelRatio, Pressable, StyleSheet } from 'react-native'; +import type { GestureResponderEvent, - PixelRatio, - Pressable, + PressableProps, StyleProp, - StyleSheet, TextStyle, ViewStyle, } from 'react-native'; @@ -16,7 +14,7 @@ import type { ThemeProp } from '../../types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import Text from '../Typography/Text'; -export type Props = React.ComponentPropsWithRef & { +export type Props = PressableProps & { /** * Text content of the `DataTableTitle`. */ diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 209d606afc..e79804d324 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -1,11 +1,6 @@ import * as React from 'react'; -import { - Animated, - Platform, - StyleProp, - StyleSheet, - ViewStyle, -} from 'react-native'; +import { Animated, Platform, StyleSheet } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -17,7 +12,7 @@ import DialogTitle from './DialogTitle'; import { useInternalTheme } from '../../core/theming'; import type { Theme, ThemeProp } from '../../types'; import Modal from '../Modal'; -import { DialogChildProps } from './utils'; +import type { DialogChildProps } from './utils'; export type Props = { /** diff --git a/src/components/Dialog/DialogActions.tsx b/src/components/Dialog/DialogActions.tsx index 0c8d8c5f74..7e3799451e 100644 --- a/src/components/Dialog/DialogActions.tsx +++ b/src/components/Dialog/DialogActions.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; -import { DialogActionChildProps } from './utils'; +import type { DialogActionChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Content of the `DialogActions`. */ diff --git a/src/components/Dialog/DialogContent.tsx b/src/components/Dialog/DialogContent.tsx index b426b91f70..a084188b32 100644 --- a/src/components/Dialog/DialogContent.tsx +++ b/src/components/Dialog/DialogContent.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; -import { View, ViewStyle, StyleSheet, StyleProp } from 'react-native'; +import { View, StyleSheet } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Content of the `DialogContent`. */ diff --git a/src/components/Dialog/DialogIcon.tsx b/src/components/Dialog/DialogIcon.tsx index e72d7f2821..199becd06c 100644 --- a/src/components/Dialog/DialogIcon.tsx +++ b/src/components/Dialog/DialogIcon.tsx @@ -1,10 +1,11 @@ -import * as React from 'react'; -import { ColorValue, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { ColorValue } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import type { Theme } from '../../types'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; export type Props = { /** diff --git a/src/components/Dialog/DialogScrollArea.tsx b/src/components/Dialog/DialogScrollArea.tsx index ed9af2dbe6..b76d4cf031 100644 --- a/src/components/Dialog/DialogScrollArea.tsx +++ b/src/components/Dialog/DialogScrollArea.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import type { Theme } from '../../types'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Content of the `DialogScrollArea`. */ diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index 5ee8a990ac..f869887205 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, TextStyle } from 'react-native'; +import { StyleSheet } from 'react-native'; +import type { StyleProp, TextStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { Theme, ThemeProp } from '../../types'; diff --git a/src/components/Dialog/utils.ts b/src/components/Dialog/utils.ts index c827d7b598..64ccec77cc 100644 --- a/src/components/Dialog/utils.ts +++ b/src/components/Dialog/utils.ts @@ -1,4 +1,4 @@ -import { StyleProp, ViewStyle } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; export type DialogChildProps = { style?: StyleProp; diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index c697a2a04a..aadaed4eb3 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import { useInternalTheme } from '../core/theming'; import type { $RemoveChildren, ThemeProp } from '../types'; diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index c10c7ac0b6..3f79fce421 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -1,24 +1,22 @@ import * as React from 'react'; -import { - Animated, +import { Animated, Platform, Pressable, StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent, NativeSyntheticEvent, - Platform, - Pressable, StyleProp, - StyleSheet, TextLayoutEventData, - View, + ViewProps, ViewStyle, } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import Badge from '../Badge'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; import Text from '../Typography/Text'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * The label text of the item. */ diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index abcd6ae894..95ef989248 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -1,23 +1,23 @@ import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, - View, + ViewProps, ViewStyle, } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import Icon, { IconSource } from '../Icon'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * The label text of the item. */ diff --git a/src/components/Drawer/DrawerSection.tsx b/src/components/Drawer/DrawerSection.tsx index 14a44dcd6f..d267cf4c17 100644 --- a/src/components/Drawer/DrawerSection.tsx +++ b/src/components/Drawer/DrawerSection.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import { Palette } from '../../theme/tokens'; @@ -7,7 +8,7 @@ import type { ThemeProp } from '../../types'; import Divider from '../Divider'; import Text from '../Typography/Text'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Title to show as the header for the section. */ diff --git a/src/components/FAB/Content.tsx b/src/components/FAB/Content.tsx index d7dc81a9cb..0e5728c2e3 100644 --- a/src/components/FAB/Content.tsx +++ b/src/components/FAB/Content.tsx @@ -1,16 +1,12 @@ -import * as React from 'react'; -import { - ColorValue, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; -import Reanimated, { AnimatedStyle } from 'react-native-reanimated'; +import Reanimated from 'react-native-reanimated'; +import type { AnimatedStyle } from 'react-native-reanimated'; import type { TypescaleKey } from '../../theme/types'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; import AnimatedText from '../Typography/AnimatedText'; export type ContentProps = { diff --git a/src/components/FAB/Extended.tsx b/src/components/FAB/Extended.tsx index d10d624596..d15fe6a6fa 100644 --- a/src/components/FAB/Extended.tsx +++ b/src/components/FAB/Extended.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { AccessibilityState, ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, - View, ViewStyle, } from 'react-native'; @@ -20,13 +19,12 @@ import Reanimated, { import { scheduleOnUI } from 'react-native-worklets'; import Shell from './Shell'; -import { Size, Variant } from './tokens'; +import type { Size, Variant } from './tokens'; import { getDimensions } from './utils'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { toRawSpring } from '../../theme/tokens/sys/motion'; import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; import AnimatedText from '../Typography/AnimatedText'; @@ -100,7 +98,7 @@ export type Props = { * @optional */ theme?: ThemeProp; - ref?: React.RefObject; + ref?: React.Ref; }; /** @@ -140,138 +138,134 @@ export type Props = { * export default MyComponent; * ``` */ -const Extended = forwardRef( - ( - { - icon, - label, - variant = 'tonalPrimary', - containerColor, - contentColor, - size = 'default', - expanded, - visible = true, - onPress, - accessibilityLabel = label, - accessibilityState, - labelMaxFontSizeMultiplier, - background, - style, - testID = 'extended-floating-action-button', - theme: themeOverrides, - }, - ref - ) => { - const theme = useInternalTheme(themeOverrides); - const reduceMotion = useReduceMotion(); +const Extended = ({ + icon, + label, + variant = 'tonalPrimary', + containerColor, + contentColor, + size = 'default', + expanded, + visible = true, + onPress, + accessibilityLabel = label, + accessibilityState, + labelMaxFontSizeMultiplier, + background, + style, + testID = 'extended-floating-action-button', + theme: themeOverrides, + ref, +}: Props) => { + const theme = useInternalTheme(themeOverrides); + const reduceMotion = useReduceMotion(); - const dimensions = getDimensions({ theme, size }); + const dimensions = getDimensions({ theme, size }); - const offscreenLabelRef = useAnimatedRef(); + const offscreenLabelRef = useAnimatedRef(); - const widthValue = useSharedValue(dimensions.width); - const labelOpacity = useSharedValue(expanded ? 1 : 0); + const widthValue = useSharedValue(dimensions.width); + const labelOpacity = useSharedValue(expanded ? 1 : 0); - React.useEffect(() => { - const { - width: collapsedWidth, - leading, - iconSize, - iconLabelGap, - trailing, - } = dimensions; - const targetOpacity = expanded ? 1 : 0; - - if (reduceMotion) { - scheduleOnUI(() => { - 'worklet'; - const m = measure(offscreenLabelRef); - const lw = m?.width ?? 0; - widthValue.value = expanded - ? leading + iconSize + iconLabelGap + lw + trailing - : collapsedWidth; - labelOpacity.value = targetOpacity; - }); - return; - } - - const widthSpring = toRawSpring( - expanded - ? theme.motion.spring.fast.spatial - : theme.motion.spring.default.spatial - ); - const opacitySpring = toRawSpring( - expanded - ? theme.motion.spring.default.effects - : theme.motion.spring.fast.effects - ); + React.useEffect(() => { + const { + width: collapsedWidth, + leading, + iconSize, + iconLabelGap, + trailing, + } = dimensions; + const targetOpacity = expanded ? 1 : 0; + if (reduceMotion) { scheduleOnUI(() => { 'worklet'; const m = measure(offscreenLabelRef); const lw = m?.width ?? 0; - const expandedWidth = leading + iconSize + iconLabelGap + lw + trailing; - widthValue.value = withSpring( - expanded ? expandedWidth : collapsedWidth, - widthSpring - ); - labelOpacity.value = withSpring(targetOpacity, opacitySpring); + widthValue.value = expanded + ? leading + iconSize + iconLabelGap + lw + trailing + : collapsedWidth; + labelOpacity.value = targetOpacity; }); - }, [ - expanded, - label, - dimensions, - theme, - reduceMotion, - widthValue, - labelOpacity, - offscreenLabelRef, - ]); + return; + } - const labelAnimatedStyle = useAnimatedStyle(() => ({ - opacity: labelOpacity.value, - })); + const widthSpring = toRawSpring( + expanded + ? theme.motion.spring.fast.spatial + : theme.motion.spring.default.spatial + ); + const opacitySpring = toRawSpring( + expanded + ? theme.motion.spring.default.effects + : theme.motion.spring.fast.effects + ); - return ( - <> - - { + 'worklet'; + const m = measure(offscreenLabelRef); + const lw = m?.width ?? 0; + const expandedWidth = leading + iconSize + iconLabelGap + lw + trailing; + widthValue.value = withSpring( + expanded ? expandedWidth : collapsedWidth, + widthSpring + ); + labelOpacity.value = withSpring(targetOpacity, opacitySpring); + }); + }, [ + expanded, + label, + dimensions, + theme, + reduceMotion, + widthValue, + labelOpacity, + offscreenLabelRef, + ]); + + const labelAnimatedStyle = useAnimatedStyle(() => ({ + opacity: labelOpacity.value, + })); + + return ( + <> + + + - - {label} - - - - ); - } -); + {label} + + + + ); +}; const styles = StyleSheet.create({ offscreenMeasure: { diff --git a/src/components/FAB/FAB.tsx b/src/components/FAB/FAB.tsx index 2a897ddbb9..5319d18e36 100644 --- a/src/components/FAB/FAB.tsx +++ b/src/components/FAB/FAB.tsx @@ -1,18 +1,17 @@ import * as React from 'react'; -import { +import { View } from 'react-native'; +import type { AccessibilityState, ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - View, ViewStyle, } from 'react-native'; import Shell from './Shell'; -import { Size, Variant } from './tokens'; +import type { Size, Variant } from './tokens'; import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; export type Props = { @@ -71,7 +70,7 @@ export type Props = { * @optional */ theme?: ThemeProp; - ref?: React.RefObject; + ref?: React.Ref; }; /** @@ -103,42 +102,38 @@ export type Props = { * export default MyComponent; * ``` */ -const FAB = forwardRef( - ( - { - icon, - variant = 'tonalPrimary', - size = 'default', - visible = true, - onPress, - containerColor, - contentColor, - accessibilityLabel, - accessibilityState, - background, - style, - testID = 'floating-action-button', - theme, - }, - ref - ) => ( - - ) +const FAB = ({ + icon, + variant = 'tonalPrimary', + size = 'default', + visible = true, + onPress, + containerColor, + contentColor, + accessibilityLabel, + accessibilityState, + background, + style, + testID = 'floating-action-button', + theme, + ref, +}: Props) => ( + ); export default FAB; diff --git a/src/components/FAB/Menu.tsx b/src/components/FAB/Menu.tsx index 0af09b5eb5..1fc362edb2 100644 --- a/src/components/FAB/Menu.tsx +++ b/src/components/FAB/Menu.tsx @@ -1,11 +1,6 @@ import * as React from 'react'; -import { - ColorValue, - GestureResponderEvent, - Platform, - StyleSheet, - View, -} from 'react-native'; +import { Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent } from 'react-native'; import Animated, { interpolate, @@ -21,13 +16,12 @@ import Content from './Content'; import Shell from './Shell'; import { MenuTokens, - Size, Tokens, - Variant, FOCUS_RING_INSET, FOCUS_RING_THICKNESS, webNoOutline, } from './tokens'; +import type { Size, Variant } from './tokens'; import { useFocusRing } from './useFocusRing'; import { resolveColors } from './utils'; import { useLocale } from '../../core/locale'; @@ -36,7 +30,8 @@ import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { toRawSpring } from '../../theme/tokens/sys/motion'; import { resolveCornerRadius } from '../../theme/utils/shape'; import type { InternalTheme, ThemeProp } from '../../types'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; export type MenuItemProps = { diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index b748b11e40..4f9b9b5da9 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -1,39 +1,35 @@ import * as React from 'react'; -import { +import { Platform, StyleSheet, View } from 'react-native'; +import type { AccessibilityState, ColorValue, GestureResponderEvent, - Platform, PressableAndroidRippleConfig, StyleProp, - StyleSheet, - View, ViewStyle, } from 'react-native'; import Reanimated, { - AnimatedStyle, useAnimatedStyle, useSharedValue, - type SharedValue, } from 'react-native-reanimated'; +import type { SharedValue } from 'react-native-reanimated'; +import type { AnimatedStyle } from 'react-native-reanimated'; import Content from './Content'; import { - Size, Tokens, - Variant, FOCUS_RING_INSET, FOCUS_RING_THICKNESS, webNoOutline, } from './tokens'; +import type { Size, Variant } from './tokens'; import { useFocusRing } from './useFocusRing'; import { useVisibility } from './useVisibility'; import { getDimensions, resolveColors } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ShapeToken } from '../../theme/utils/shape'; import type { Elevation, ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; @@ -160,7 +156,7 @@ export type ShellProps = { * @optional */ theme?: ThemeProp; - ref?: React.RefObject; + ref?: React.Ref; }; /** @@ -172,173 +168,167 @@ export type ShellProps = { * * Not exported from the package. */ -const Shell = forwardRef( - ( - { - icon, - label, - variant = 'tonalPrimary', - size = 'default', - containerColor, - contentColor, - shape, - iconSize, - leading, - trailing, - elevation = Tokens.stateElevation.enabled, - visible = true, - onPress, - accessibilityLabel = label, - accessibilityState, - labelMaxFontSizeMultiplier, - labelAnimatedStyle, - background, - widthShared, - heightShared, - borderRadiusShared, - transparentBackground = false, - overlay, - children, - style, - testID = 'fab-shell', - theme: themeOverrides, - }, - ref - ) => { - const theme = useInternalTheme(themeOverrides); +const Shell = ({ + icon, + label, + variant = 'tonalPrimary', + size = 'default', + containerColor, + contentColor, + shape, + iconSize, + leading, + trailing, + elevation = Tokens.stateElevation.enabled, + visible = true, + onPress, + accessibilityLabel = label, + accessibilityState, + labelMaxFontSizeMultiplier, + labelAnimatedStyle, + background, + widthShared, + heightShared, + borderRadiusShared, + transparentBackground = false, + overlay, + children, + style, + testID = 'fab-shell', + theme: themeOverrides, + ref, +}: ShellProps) => { + const theme = useInternalTheme(themeOverrides); - const dimensions = React.useMemo( - () => getDimensions({ theme, size, shape, iconSize, leading, trailing }), - [theme, size, shape, iconSize, leading, trailing] - ); + const dimensions = React.useMemo( + () => getDimensions({ theme, size, shape, iconSize, leading, trailing }), + [theme, size, shape, iconSize, leading, trailing] + ); - const colors = React.useMemo( - () => resolveColors({ theme, variant, containerColor, contentColor }), - [theme, variant, containerColor, contentColor] - ); + const colors = React.useMemo( + () => resolveColors({ theme, variant, containerColor, contentColor }), + [theme, variant, containerColor, contentColor] + ); - const { scale, alpha, shadowStyle } = useVisibility({ - visible, - theme, - elevation, - }); + const { scale, alpha, shadowStyle } = useVisibility({ + visible, + theme, + elevation, + }); - // Fallback shared values track the static size-driven dimensions. Consumers - // that don't supply their own animated shared values get these. Keeping - // everything as a shared value means there's exactly one animated style - // per view — no static-vs-animated merge surprises. - const fallbackWidth = useSharedValue(dimensions.width); - const fallbackHeight = useSharedValue(dimensions.height); - const fallbackBorderRadius = useSharedValue(dimensions.borderRadius); - React.useEffect(() => { - fallbackWidth.value = dimensions.width; - fallbackHeight.value = dimensions.height; - fallbackBorderRadius.value = dimensions.borderRadius; - }, [ - dimensions.width, - dimensions.height, - dimensions.borderRadius, - fallbackWidth, - fallbackHeight, - fallbackBorderRadius, - ]); + // Fallback shared values track the static size-driven dimensions. Consumers + // that don't supply their own animated shared values get these. Keeping + // everything as a shared value means there's exactly one animated style + // per view — no static-vs-animated merge surprises. + const fallbackWidth = useSharedValue(dimensions.width); + const fallbackHeight = useSharedValue(dimensions.height); + const fallbackBorderRadius = useSharedValue(dimensions.borderRadius); + React.useEffect(() => { + fallbackWidth.value = dimensions.width; + fallbackHeight.value = dimensions.height; + fallbackBorderRadius.value = dimensions.borderRadius; + }, [ + dimensions.width, + dimensions.height, + dimensions.borderRadius, + fallbackWidth, + fallbackHeight, + fallbackBorderRadius, + ]); - const width = widthShared ?? fallbackWidth; - const height = heightShared ?? fallbackHeight; - const borderRadius = borderRadiusShared ?? fallbackBorderRadius; - const containerBg = transparentBackground - ? 'transparent' - : colors.container; + const width = widthShared ?? fallbackWidth; + const height = heightShared ?? fallbackHeight; + const borderRadius = borderRadiusShared ?? fallbackBorderRadius; + const containerBg = transparentBackground ? 'transparent' : colors.container; - const outerStyle = useAnimatedStyle( - () => ({ - transform: [{ scale: scale.value }], - opacity: alpha.value, - width: width.value, - height: height.value, - borderRadius: borderRadius.value, - backgroundColor: containerBg, - }), - [width, height, borderRadius, containerBg] - ); + const outerStyle = useAnimatedStyle( + () => ({ + transform: [{ scale: scale.value }], + opacity: alpha.value, + width: width.value, + height: height.value, + borderRadius: borderRadius.value, + backgroundColor: containerBg, + }), + [width, height, borderRadius, containerBg] + ); - const clipStyle = useAnimatedStyle( - () => ({ - borderRadius: borderRadius.value, - backgroundColor: containerBg, - }), - [borderRadius, containerBg] - ); + const clipStyle = useAnimatedStyle( + () => ({ + borderRadius: borderRadius.value, + backgroundColor: containerBg, + }), + [borderRadius, containerBg] + ); - const { focusedSV, onFocus, onBlur } = useFocusRing(); - const focusRingStyle = useAnimatedStyle( - () => ({ - opacity: focusedSV.value ? 1 : 0, - borderRadius: borderRadius.value + FOCUS_RING_INSET, - }), - [borderRadius] - ); + const { focusedSV, onFocus, onBlur } = useFocusRing(); + const focusRingStyle = useAnimatedStyle( + () => ({ + opacity: focusedSV.value ? 1 : 0, + borderRadius: borderRadius.value + FOCUS_RING_INSET, + }), + [borderRadius] + ); - return ( - - - {overlay} - - {children ?? ( - - )} - - - + + {overlay} + + > + {children ?? ( + + )} + - ); - } -); + + + ); +}; const styles = StyleSheet.create({ container: { diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index 8e7c83eddb..75b7ddb940 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -1,9 +1,11 @@ -import { ColorValue } from 'react-native'; +import type { ColorValue } from 'react-native'; -import { Size, Tokens, Variant } from './tokens'; +import { Tokens } from './tokens'; +import type { Size, Variant } from './tokens'; import type { TypescaleKey } from '../../theme/types'; import { contentColorFor } from '../../theme/utils/color'; -import { resolveCornerRadius, ShapeToken } from '../../theme/utils/shape'; +import { resolveCornerRadius } from '../../theme/utils/shape'; +import type { ShapeToken } from '../../theme/utils/shape'; import type { InternalTheme } from '../../types'; export type ResolvedColors = { diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index d24a6caeec..cb2c0b5e39 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -1,10 +1,7 @@ import * as React from 'react'; -import { - Image, - ImageSourcePropType, - Platform, - type ColorValue, -} from 'react-native'; +import { Image, Platform } from 'react-native'; +import type { ColorValue } from 'react-native'; +import type { ImageSourcePropType } from 'react-native'; import { accessibilityProps } from './MaterialCommunityIcon'; import { useLocale } from '../core/locale'; diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 41b5370c78..899d1e5a46 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -1,21 +1,19 @@ import * as React from 'react'; -import { - Animated, +import { Animated, StyleSheet, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent, StyleProp, - StyleSheet, - View, ViewStyle, } from 'react-native'; import { getIconButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import ActivityIndicator from '../ActivityIndicator'; import CrossFadeIcon from '../CrossFadeIcon'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; import Surface from '../Surface'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; @@ -72,7 +70,7 @@ export type Props = Omit<$RemoveChildren, 'style'> & { */ onPress?: (e: GestureResponderEvent) => void; style?: Animated.WithAnimatedValue>; - ref?: React.RefObject; + ref?: React.Ref; /** * TestID used for testing purposes */ @@ -109,119 +107,114 @@ export type Props = Omit<$RemoveChildren, 'style'> & { * * @extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple */ -const IconButton = forwardRef( - ( - { - icon, - iconColor: customIconColor, - containerColor: customContainerColor, - size = 24, - accessibilityLabel, - disabled, - onPress, - selected = false, - animated = false, - mode, - style, - theme: themeOverrides, - testID = 'icon-button', - loading = false, - contentStyle, - ...rest - }: Props, - ref - ) => { - const theme = useInternalTheme(themeOverrides); +const IconButton = ({ + icon, + iconColor: customIconColor, + containerColor: customContainerColor, + size = 24, + accessibilityLabel, + disabled, + onPress, + selected = false, + animated = false, + mode, + style, + theme: themeOverrides, + testID = 'icon-button', + loading = false, + contentStyle, + ref, + ...rest +}: Props) => { + const theme = useInternalTheme(themeOverrides); - const IconComponent = animated ? CrossFadeIcon : Icon; + const IconComponent = animated ? CrossFadeIcon : Icon; - const { - iconColor, - iconOpacity, - backgroundColor, - borderColor, - backgroundOpacity, - } = getIconButtonColor({ - theme, - disabled, - selected, - mode, - customIconColor, - customContainerColor, - }); + const { + iconColor, + iconOpacity, + backgroundColor, + borderColor, + backgroundOpacity, + } = getIconButtonColor({ + theme, + disabled, + selected, + mode, + customIconColor, + customContainerColor, + }); - const buttonSize = size + 2 * PADDING; + const buttonSize = size + 2 * PADDING; - const { - borderWidth = mode === 'outlined' && !selected ? 1 : 0, - borderRadius = buttonSize / 2, - } = (StyleSheet.flatten(style) || {}) as ViewStyle; + const { + borderWidth = mode === 'outlined' && !selected ? 1 : 0, + borderRadius = buttonSize / 2, + } = (StyleSheet.flatten(style) || {}) as ViewStyle; - const borderStyles = { - borderWidth, - borderRadius, - borderColor, - }; + const borderStyles = { + borderWidth, + borderRadius, + borderColor, + }; - return ( - + {backgroundOpacity < 1 && ( + + )} + - {backgroundOpacity < 1 && ( - - )} - - - {loading ? ( - - ) : ( - - )} - - - - ); - } -); + + {loading ? ( + + ) : ( + + )} + + + + ); +}; const styles = StyleSheet.create({ container: { diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index ef7699f184..5dbca2ee60 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -1,14 +1,13 @@ import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent, NativeSyntheticEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, TextLayoutEventData, TextStyle, - View, ViewProps, ViewStyle, } from 'react-native'; @@ -20,9 +19,8 @@ import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; export type Props = { diff --git a/src/components/List/ListIcon.tsx b/src/components/List/ListIcon.tsx index 0bef35d4c0..0851ed6055 100644 --- a/src/components/List/ListIcon.tsx +++ b/src/components/List/ListIcon.tsx @@ -1,15 +1,10 @@ -import * as React from 'react'; -import { - ColorValue, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import Icon, { IconSource } from '../Icon'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; export type Props = { /** diff --git a/src/components/List/ListImage.tsx b/src/components/List/ListImage.tsx index ec1cf18f82..907c1b63b2 100644 --- a/src/components/List/ListImage.tsx +++ b/src/components/List/ListImage.tsx @@ -1,11 +1,5 @@ -import * as React from 'react'; -import { - StyleSheet, - StyleProp, - Image, - ImageSourcePropType, - ImageStyle, -} from 'react-native'; +import { StyleSheet, Image } from 'react-native'; +import type { StyleProp, ImageSourcePropType, ImageStyle } from 'react-native'; import type { ThemeProp } from '../../types'; diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 371c1da407..a6f0181f02 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -1,20 +1,19 @@ import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent, NativeSyntheticEvent, StyleProp, - StyleSheet, TextLayoutEventData, TextStyle, - View, ViewStyle, } from 'react-native'; -import { Style, getLeftStyles, getRightStyles } from './utils'; +import { getLeftStyles, getRightStyles } from './utils'; +import type { Style } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, EllipsizeProp, ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; @@ -65,6 +64,7 @@ export type Props = $RemoveChildren & { * Style that is passed to the root TouchableRipple container. */ style?: StyleProp; + ref?: React.Ref; /** * Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description. */ @@ -138,30 +138,28 @@ export type Props = $RemoveChildren & { * * @extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple */ -const ListItem = ( - { - left, - right, - title, - description, - onPress, - theme: themeOverrides, - style, - containerStyle, - contentStyle, - titleStyle, - titleNumberOfLines = 1, - descriptionNumberOfLines = 2, - titleEllipsizeMode, - descriptionEllipsizeMode, - descriptionStyle, - descriptionMaxFontSizeMultiplier, - titleMaxFontSizeMultiplier, - testID, - ...rest - }: Props, - ref: React.ForwardedRef -) => { +const ListItem = ({ + left, + right, + title, + description, + onPress, + theme: themeOverrides, + style, + containerStyle, + contentStyle, + titleStyle, + titleNumberOfLines = 1, + descriptionNumberOfLines = 2, + titleEllipsizeMode, + descriptionEllipsizeMode, + descriptionStyle, + descriptionMaxFontSizeMultiplier, + titleMaxFontSizeMultiplier, + testID, + ref, + ...rest +}: Props) => { const theme = useInternalTheme(themeOverrides); const [alignToTop, setAlignToTop] = React.useState(false); @@ -264,7 +262,6 @@ const ListItem = ( }; ListItem.displayName = 'List.Item'; -const Component = forwardRef(ListItem); const styles = StyleSheet.create({ container: { @@ -292,4 +289,4 @@ const styles = StyleSheet.create({ }, }); -export default Component; +export default ListItem; diff --git a/src/components/List/ListSection.tsx b/src/components/List/ListSection.tsx index be5481ab21..c9dfc470b2 100644 --- a/src/components/List/ListSection.tsx +++ b/src/components/List/ListSection.tsx @@ -1,17 +1,12 @@ import * as React from 'react'; -import { - StyleProp, - StyleSheet, - TextStyle, - View, - ViewStyle, -} from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native'; import ListSubheader from './ListSubheader'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Title text for the section. */ diff --git a/src/components/List/ListSubheader.tsx b/src/components/List/ListSubheader.tsx index 96b7ae1dcf..ada0ff1e61 100644 --- a/src/components/List/ListSubheader.tsx +++ b/src/components/List/ListSubheader.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { StyleProp, StyleSheet, TextStyle } from 'react-native'; +import { StyleSheet } from 'react-native'; +import type { StyleProp, TextStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; diff --git a/src/components/List/utils.ts b/src/components/List/utils.ts index af030dd1cf..7b7a868de0 100644 --- a/src/components/List/utils.ts +++ b/src/components/List/utils.ts @@ -1,4 +1,5 @@ -import { StyleSheet, StyleProp, ViewStyle } from 'react-native'; +import { StyleSheet } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import type { EllipsizeProp, InternalTheme, ThemeProp } from '../../types'; diff --git a/src/components/MaterialCommunityIcon.tsx b/src/components/MaterialCommunityIcon.tsx index ce2e73a743..df4955578d 100644 --- a/src/components/MaterialCommunityIcon.tsx +++ b/src/components/MaterialCommunityIcon.tsx @@ -1,12 +1,6 @@ import * as React from 'react'; -import { - ColorValue, - Platform, - Role, - StyleSheet, - Text, - ViewProps, -} from 'react-native'; +import { Platform, StyleSheet, Text } from 'react-native'; +import type { ColorValue, Role, ViewProps } from 'react-native'; import { black } from '../theme/colors'; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 832eac30bb..4c3d6fe65e 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -3,20 +3,22 @@ import { Animated, Dimensions, Easing, - EmitterSubscription, Keyboard, - KeyboardEvent as RNKeyboardEvent, - LayoutRectangle, - NativeEventSubscription, Platform, ScrollView, - ScrollViewProps, - StyleProp, StyleSheet, View, - ViewStyle, Pressable, } from 'react-native'; +import type { KeyboardEvent as RNKeyboardEvent } from 'react-native'; +import type { + EmitterSubscription, + LayoutRectangle, + NativeEventSubscription, + ScrollViewProps, + StyleProp, + ViewStyle, +} from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 89d185257b..2d2fd78edb 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { AccessibilityState, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, TextStyle, - View, ViewStyle, } from 'react-native'; @@ -18,10 +17,10 @@ import { } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { Theme, ThemeProp } from '../../types'; -import Icon, { IconSource } from '../Icon'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import Icon from '../Icon'; +import type { IconSource } from '../Icon'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; export type Props = { diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index d6590881a3..a8412da806 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,13 +1,6 @@ import * as React from 'react'; -import { - Animated, - Easing, - StyleProp, - StyleSheet, - Pressable, - View, - ViewStyle, -} from 'react-native'; +import { Animated, Easing, StyleSheet, Pressable, View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import useLatestCallback from 'use-latest-callback'; diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index 4b9eb7f170..b3bc9bffb3 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import PortalConsumer from './PortalConsumer'; -import PortalHost, { PortalContext, PortalMethods } from './PortalHost'; +import PortalHost, { PortalContext } from './PortalHost'; +import type { PortalMethods } from './PortalHost'; import { LocaleContext, LocaleProvider } from '../../core/locale'; import { Consumer as SettingsConsumer, diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index bf1941490f..c14780166b 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -1,11 +1,9 @@ import * as React from 'react'; -import { - Animated, +import { Animated, Platform, StyleSheet, View } from 'react-native'; +import type { LayoutChangeEvent, - Platform, StyleProp, - StyleSheet, - View, + ViewProps, ViewStyle, } from 'react-native'; @@ -13,7 +11,7 @@ import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import type { ThemeProp } from '../types'; -export type Props = React.ComponentPropsWithRef & { +export type Props = ViewProps & { /** * Animated value (between 0 and 1). This tells the progress bar to rely on this value to animate it. * Note: It should not be used in parallel with the `progress` prop. diff --git a/src/components/RadioButton/RadioButton.tsx b/src/components/RadioButton/RadioButton.tsx index e43fa9c695..cb8a621e2d 100644 --- a/src/components/RadioButton/RadioButton.tsx +++ b/src/components/RadioButton/RadioButton.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; -import { GestureResponderEvent, Platform } from 'react-native'; +import { Platform } from 'react-native'; +import type { GestureResponderEvent } from 'react-native'; import RadioButtonAndroid from './RadioButtonAndroid'; import RadioButtonIOS from './RadioButtonIOS'; diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index eeb42c218e..a907b46b09 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import { Animated, StyleSheet, View } from 'react-native'; -import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; +import { RadioButtonContext } from './RadioButtonGroup'; +import type { RadioButtonContextType } from './RadioButtonGroup'; import { getSelectionControlColor, handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, ThemeProp } from '../../types'; diff --git a/src/components/RadioButton/RadioButtonIOS.tsx b/src/components/RadioButton/RadioButtonIOS.tsx index d5d8df7090..e2e5f06c7c 100644 --- a/src/components/RadioButton/RadioButtonIOS.tsx +++ b/src/components/RadioButton/RadioButtonIOS.tsx @@ -1,7 +1,8 @@ -import * as React from 'react'; -import { GestureResponderEvent, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent } from 'react-native'; -import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; +import { RadioButtonContext } from './RadioButtonGroup'; +import type { RadioButtonContextType } from './RadioButtonGroup'; import { handlePress, isChecked } from './utils'; import { getSelectionControlIOSColor } from './utils'; import { useInternalTheme } from '../../core/theming'; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index 4c6bcb824d..1591760980 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -1,25 +1,23 @@ -import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, TextStyle, - View, ViewStyle, } from 'react-native'; import RadioButton from './RadioButton'; import RadioButtonAndroid from './RadioButtonAndroid'; -import { RadioButtonContext, RadioButtonContextType } from './RadioButtonGroup'; +import { RadioButtonContext } from './RadioButtonGroup'; +import type { RadioButtonContextType } from './RadioButtonGroup'; import RadioButtonIOS from './RadioButtonIOS'; import { handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; import { getStateLayer } from '../../theme/utils/state'; import type { ThemeProp, TypescaleKey } from '../../types'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; export type Props = { diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 975af841a6..a081b62fc4 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -1,15 +1,11 @@ import * as React from 'react'; -import { - Animated, +import { Animated, Platform, StyleSheet, TextInput, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent, - Platform, StyleProp, - StyleSheet, - TextInput, TextInputProps, TextStyle, - View, ViewStyle, } from 'react-native'; @@ -23,13 +19,12 @@ import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import { cornerNone } from '../theme/tokens/sys/shape'; import type { Theme, ThemeProp } from '../types'; -import { forwardRef } from '../utils/forwardRef'; interface Style { marginRight: number; } -export type Props = React.ComponentPropsWithRef & { +export type Props = TextInputProps & { /** * Hint text shown when the input is empty. */ @@ -134,6 +129,7 @@ export type Props = React.ComponentPropsWithRef & { * @optional */ theme?: ThemeProp; + ref?: React.Ref; }; type TextInputHandles = Pick< @@ -165,210 +161,201 @@ type TextInputHandles = Pick< * ``` */ -const Searchbar = forwardRef( - ( - { - icon, - iconColor: customIconColor, - onIconPress, - searchAccessibilityLabel = 'search', - clearIcon, - clearAccessibilityLabel = 'clear', - onClearIconPress, - traileringIcon, - traileringIconColor, - traileringIconAccessibilityLabel, - onTraileringIconPress, - right, - mode = 'bar', - showDivider = true, - inputStyle, - placeholder, - elevation = 0, - style, - theme: themeOverrides, - value, - loading = false, - testID = 'search-bar', - ...rest - }: Props, - ref - ) => { - const theme = useInternalTheme(themeOverrides); - const { direction } = useLocale(); - const { colors, fonts } = theme as Theme; - const root = React.useRef(null); +const Searchbar = ({ + icon, + iconColor: customIconColor, + onIconPress, + searchAccessibilityLabel = 'search', + clearIcon, + clearAccessibilityLabel = 'clear', + onClearIconPress, + traileringIcon, + traileringIconColor, + traileringIconAccessibilityLabel, + onTraileringIconPress, + right, + mode = 'bar', + showDivider = true, + inputStyle, + placeholder, + elevation = 0, + style, + theme: themeOverrides, + value, + loading = false, + testID = 'search-bar', + ref, + ...rest +}: Props) => { + const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); + const { colors, fonts } = theme as Theme; + const root = React.useRef(null); - React.useImperativeHandle(ref, () => ({ - focus: () => root.current?.focus(), - clear: () => root.current?.clear(), - setNativeProps: (args: TextInputProps) => - root.current?.setNativeProps(args), - isFocused: () => root.current?.isFocused() || false, - blur: () => root.current?.blur(), - setSelection: (start: number, end: number) => - root.current?.setSelection(start, end), - })); + React.useImperativeHandle(ref, () => ({ + focus: () => root.current?.focus(), + clear: () => root.current?.clear(), + setNativeProps: (args: TextInputProps) => + root.current?.setNativeProps(args), + isFocused: () => root.current?.isFocused() || false, + blur: () => root.current?.blur(), + setSelection: (start: number, end: number) => + root.current?.setSelection(start, end), + })); - const handleClearPress = (e: any) => { - root.current?.clear(); - rest.onChangeText?.(''); - onClearIconPress?.(e); - }; + const handleClearPress = (e: any) => { + root.current?.clear(); + rest.onChangeText?.(''); + onClearIconPress?.(e); + }; - const { dark } = theme; + const { dark } = theme; - const placeholderTextColor = theme.colors.onSurface; - const textColor = theme.colors.onSurfaceVariant; - const iconColor = customIconColor || theme.colors.onSurfaceVariant; + const placeholderTextColor = theme.colors.onSurface; + const textColor = theme.colors.onSurfaceVariant; + const iconColor = customIconColor || theme.colors.onSurfaceVariant; - const font = { - ...fonts.bodyLarge, - lineHeight: Platform.select({ - ios: 0, - default: fonts.bodyLarge.lineHeight, - }), - }; + const font = { + ...fonts.bodyLarge, + lineHeight: Platform.select({ + ios: 0, + default: fonts.bodyLarge.lineHeight, + }), + }; - const isBarMode = mode === 'bar'; - const inputTextAlign = direction === 'rtl' ? 'right' : 'left'; - const shouldRenderTraileringIcon = - isBarMode && - traileringIcon && - !loading && - (!value || right !== undefined); + const isBarMode = mode === 'bar'; + const inputTextAlign = direction === 'rtl' ? 'right' : 'left'; + const shouldRenderTraileringIcon = + isBarMode && traileringIcon && !loading && (!value || right !== undefined); - return ( - + ( + + )) + } + theme={theme} + accessibilityLabel={searchAccessibilityLabel} + testID={`${testID}-icon`} + /> + + placeholder={placeholder || ''} + placeholderTextColor={placeholderTextColor} + selectionColor={colors.primary} + underlineColorAndroid="transparent" + returnKeyType="search" + keyboardAppearance={dark ? 'dark' : 'light'} + accessibilityRole="search" + ref={root} + value={value} + testID={testID} + {...rest} + /> + {loading ? ( + + ) : ( + // Clear icon should be always rendered within Searchbar – it's transparent, + // without touch events, when there is no value. It's done to avoid issues + // with the abruptly stopping ripple effect and changing bar width on web, + // when clearing the value. + + ( + + )) + } + testID={`${testID}-clear-icon`} + accessibilityRole="button" + theme={theme} + /> + + )} + {shouldRenderTraileringIcon ? ( ( - - )) - } - theme={theme} - accessibilityLabel={searchAccessibilityLabel} - testID={`${testID}-icon`} + onPress={onTraileringIconPress} + iconColor={traileringIconColor || colors.onSurfaceVariant} + icon={traileringIcon} + accessibilityLabel={traileringIconAccessibilityLabel} + testID={`${testID}-trailering-icon`} /> - - {loading ? ( - - ) : ( - // Clear icon should be always rendered within Searchbar – it's transparent, - // without touch events, when there is no value. It's done to avoid issues - // with the abruptly stopping ripple effect and changing bar width on web, - // when clearing the value. - - ( - - )) - } - testID={`${testID}-clear-icon`} - accessibilityRole="button" - theme={theme} - /> - - )} - {shouldRenderTraileringIcon ? ( - - ) : null} - {isBarMode && - right?.({ color: textColor, style: styles.rightStyle, testID })} - {!isBarMode && showDivider && ( - - )} - - ); - } -); + )} + + ); +}; const styles = StyleSheet.create({ container: { diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 621fe5c85d..0d30d8aee4 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -1,12 +1,10 @@ import * as React from 'react'; -import { - Animated, +import { Animated, StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, - StyleSheet, TextStyle, - View, ViewStyle, } from 'react-native'; @@ -19,9 +17,8 @@ import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; -import TouchableRipple, { - Props as TouchableRippleProps, -} from '../TouchableRipple/TouchableRipple'; +import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; export type Props = { diff --git a/src/components/SegmentedButtons/SegmentedButtons.tsx b/src/components/SegmentedButtons/SegmentedButtons.tsx index bdc4a70f89..6096639888 100644 --- a/src/components/SegmentedButtons/SegmentedButtons.tsx +++ b/src/components/SegmentedButtons/SegmentedButtons.tsx @@ -1,10 +1,8 @@ -import * as React from 'react'; -import { +import { StyleSheet, View } from 'react-native'; +import type { GestureResponderEvent, StyleProp, - StyleSheet, TextStyle, - View, ViewStyle, } from 'react-native'; diff --git a/src/components/SegmentedButtons/utils.ts b/src/components/SegmentedButtons/utils.ts index bf56e25139..1c74f5c49f 100644 --- a/src/components/SegmentedButtons/utils.ts +++ b/src/components/SegmentedButtons/utils.ts @@ -1,4 +1,4 @@ -import { ViewStyle } from 'react-native'; +import type { ViewStyle } from 'react-native'; import { tokens } from '../../theme/tokens'; import type { InternalTheme } from '../../types'; diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index f6eb590e84..2d6ee23211 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -1,12 +1,6 @@ import * as React from 'react'; -import { - Animated, - Easing, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { Animated, Easing, StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import useLatestCallback from 'use-latest-callback'; diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index fdfed67696..5966469df9 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -1,12 +1,10 @@ import * as React from 'react'; -import { - Animated, +import { Animated, Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, - Platform, ShadowStyleIOS, StyleProp, - StyleSheet, - View, + ViewProps, ViewStyle, } from 'react-native'; @@ -19,12 +17,11 @@ import { } from '../theme/tokens/sys/elevation'; import type { Elevation, Theme, ThemeProp } from '../types'; import { isAnimatedValue } from '../utils/animations'; -import { forwardRef } from '../utils/forwardRef'; import { splitStyles } from '../utils/splitStyles'; type SurfaceElevation = Elevation | Animated.Value; -export type Props = Omit, 'style'> & { +export type Props = Omit & { /** * Content of the `Surface`. */ @@ -56,7 +53,7 @@ export type Props = Omit, 'style'> & { * TestID used for testing purposes */ testID?: string; - ref?: React.RefObject; + ref?: React.Ref; /** * @internal */ @@ -119,90 +116,83 @@ function getStyleForShadowLayer( }; } -const SurfaceIOS = forwardRef< - View, - Omit & { - elevation: SurfaceElevation; - backgroundColor?: - | ColorValue - | Animated.AnimatedInterpolation; - shadowColor: ColorValue; - } ->( - ( - { - elevation, - style, - backgroundColor, - shadowColor, - testID, - children, - mode = 'elevated', - container, - ...props - }, - ref - ) => { - const [outerLayerViewStyles, innerLayerViewStyles] = React.useMemo(() => { - const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; +type SurfaceIOSProps = Omit & { + elevation: SurfaceElevation; + backgroundColor?: + | ColorValue + | Animated.AnimatedInterpolation; + shadowColor: ColorValue; +}; - const [filteredStyles, outerLayerStyles, borderRadiusStyles] = - splitStyles( - flattenedStyles, - (style) => - outerLayerStyleProperties.includes(style) || - style.startsWith('margin'), - (style) => style.startsWith('border') && style.endsWith('Radius') - ); +const SurfaceIOS = ({ + elevation, + style, + backgroundColor, + shadowColor, + testID, + children, + mode = 'elevated', + container, + ref, + ...props +}: SurfaceIOSProps) => { + const [outerLayerViewStyles, innerLayerViewStyles] = React.useMemo(() => { + const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; - if ( - process.env.NODE_ENV !== 'production' && - filteredStyles.overflow === 'hidden' && - elevation !== 0 - ) { - console.warn( - 'When setting overflow to hidden on Surface the shadow will not be displayed correctly. Wrap the content of your component in a separate View with the overflow style.' - ); - } + const [filteredStyles, outerLayerStyles, borderRadiusStyles] = splitStyles( + flattenedStyles, + (style) => + outerLayerStyleProperties.includes(style) || style.startsWith('margin'), + (style) => style.startsWith('border') && style.endsWith('Radius') + ); - const bgColor = flattenedStyles.backgroundColor || backgroundColor; + if ( + process.env.NODE_ENV !== 'production' && + filteredStyles.overflow === 'hidden' && + elevation !== 0 + ) { + console.warn( + 'When setting overflow to hidden on Surface the shadow will not be displayed correctly. Wrap the content of your component in a separate View with the overflow style.' + ); + } - const isElevated = mode === 'elevated'; + const bgColor = flattenedStyles.backgroundColor || backgroundColor; - const outerLayerViewStyles = { - ...(isElevated && getStyleForShadowLayer(elevation, 0, shadowColor)), - ...outerLayerStyles, - ...borderRadiusStyles, - backgroundColor: bgColor, - }; + const isElevated = mode === 'elevated'; - const innerLayerViewStyles = { - ...(isElevated && getStyleForShadowLayer(elevation, 1, shadowColor)), - ...filteredStyles, - ...borderRadiusStyles, - flex: - flattenedStyles.height || (!container && flattenedStyles.flex) - ? 1 - : undefined, - backgroundColor: bgColor, - }; + const outerLayerViewStyles = { + ...(isElevated && getStyleForShadowLayer(elevation, 0, shadowColor)), + ...outerLayerStyles, + ...borderRadiusStyles, + backgroundColor: bgColor, + }; - return [outerLayerViewStyles, innerLayerViewStyles]; - }, [style, elevation, backgroundColor, shadowColor, mode, container]); + const innerLayerViewStyles = { + ...(isElevated && getStyleForShadowLayer(elevation, 1, shadowColor)), + ...filteredStyles, + ...borderRadiusStyles, + flex: + flattenedStyles.height || (!container && flattenedStyles.flex) + ? 1 + : undefined, + backgroundColor: bgColor, + }; - return ( - - - {children} - + return [outerLayerViewStyles, innerLayerViewStyles]; + }, [style, elevation, backgroundColor, shadowColor, mode, container]); + + return ( + + + {children} - ); - } -); + + ); +}; /** * Surface is a basic container that can give depth to an element with elevation shadow. @@ -235,115 +225,111 @@ const SurfaceIOS = forwardRef< * }); * ``` */ -const Surface = forwardRef( - ( - { - elevation = 1, - children, - theme: overridenTheme, - style, - testID = 'surface', - mode = 'elevated', - ...props - }: Props, - ref - ) => { - const theme = useInternalTheme(overridenTheme); +const Surface = ({ + elevation = 1, + children, + theme: overridenTheme, + style, + testID = 'surface', + mode = 'elevated', + ref, + ...props +}: Props) => { + const theme = useInternalTheme(overridenTheme); - const { colors } = theme as Theme; + const { colors } = theme as Theme; - const backgroundColor = (() => { + const backgroundColor = (() => { + if (isAnimatedValue(elevation)) { + return elevation.interpolate({ + inputRange: elevationInputRange, + outputRange: elevationInputRange.map((elevation) => { + return colors.elevation?.[`level${elevation}`]; + }), + }); + } + + return colors.elevation?.[`level${elevation}`]; + })(); + + const isElevated = mode === 'elevated'; + + if (Platform.OS === 'web') { + const { pointerEvents = 'auto' } = props; + return ( + + {children} + + ); + } + + if (Platform.OS === 'android') { + const getElevationAndroid = () => { if (isAnimatedValue(elevation)) { return elevation.interpolate({ inputRange: elevationInputRange, - outputRange: elevationInputRange.map((elevation) => { - return colors.elevation?.[`level${elevation}`]; - }), + outputRange: androidElevationLevels, }); } - return colors.elevation?.[`level${elevation}`]; - })(); - - const isElevated = mode === 'elevated'; - - if (Platform.OS === 'web') { - const { pointerEvents = 'auto' } = props; - return ( - - {children} - - ); - } - - if (Platform.OS === 'android') { - const getElevationAndroid = () => { - if (isAnimatedValue(elevation)) { - return elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: androidElevationLevels, - }); - } - - return androidElevationLevels[elevation]; - }; - - const { margin, padding, transform, borderRadius } = (StyleSheet.flatten( - style - ) || {}) as ViewStyle; + return androidElevationLevels[elevation]; + }; - const outerLayerStyles = { margin, padding, transform, borderRadius }; - const sharedStyle = [{ backgroundColor }, style]; + const { margin, padding, transform, borderRadius } = (StyleSheet.flatten( + style + ) || {}) as ViewStyle; - return ( - - {children} - - ); - } + const outerLayerStyles = { margin, padding, transform, borderRadius }; + const sharedStyle = [{ backgroundColor }, style]; return ( - {children} - + ); } -); + + return ( + + {children} + + ); +}; export default Surface; diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx index 2f7b122613..806dd4585a 100644 --- a/src/components/TextInput/TextInput.tsx +++ b/src/components/TextInput/TextInput.tsx @@ -1,20 +1,23 @@ import React from 'react'; import { + Pressable, + Text, + TextInput as NativeTextInput, + View, +} from 'react-native'; +import type { TextInputProps as NativeTextInputProps } from 'react-native'; +import type { AccessibilityProps, BlurEvent, ColorValue, FocusEvent, - Pressable, StyleProp, - Text, - TextInput as NativeTextInput, - TextInputProps as NativeTextInputProps, TextStyle, - View, ViewStyle, } from 'react-native'; -import Animated, { AnimatedStyle } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; +import type { AnimatedStyle } from 'react-native-reanimated'; import { useTextInput } from './hooks'; import { styles } from './styles'; @@ -166,9 +169,11 @@ export type TextInputHookReturn = SharedTextInputStyleData & { focusInput: () => void; }; -export type TextInputRenderProps = React.ComponentPropsWithRef< +export type TextInputRenderProps = React.ComponentPropsWithoutRef< typeof NativeTextInput ->; +> & { + ref?: React.RefObject; +}; export type TextInputHandles = Pick< NativeTextInput, diff --git a/src/components/TextInput/TextInputErrorIcon.tsx b/src/components/TextInput/TextInputErrorIcon.tsx index 3af74ec3ad..b477753259 100644 --- a/src/components/TextInput/TextInputErrorIcon.tsx +++ b/src/components/TextInput/TextInputErrorIcon.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { StyleProp, View, ViewStyle } from 'react-native'; +import { View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import { ACCESSORY_SIZE } from './constants'; import { useInternalTheme } from '../../core/theming'; diff --git a/src/components/TextInput/TextInputIcon.tsx b/src/components/TextInput/TextInputIcon.tsx index e76528324f..4fcd7b6e18 100644 --- a/src/components/TextInput/TextInputIcon.tsx +++ b/src/components/TextInput/TextInputIcon.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { StyleProp, View, ViewStyle } from 'react-native'; +import { View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import { ACCESSORY_SIZE } from './constants'; import { styles } from './styles'; diff --git a/src/components/TextInput/hooks.ts b/src/components/TextInput/hooks.ts index 968cde91f0..1cc033fd7e 100644 --- a/src/components/TextInput/hooks.ts +++ b/src/components/TextInput/hooks.ts @@ -5,11 +5,8 @@ import { useState, type RefObject, } from 'react'; -import { - BlurEvent, - FocusEvent, - TextInput as NativeTextInput, -} from 'react-native'; +import { TextInput as NativeTextInput } from 'react-native'; +import type { BlurEvent, FocusEvent } from 'react-native'; import { interpolate, diff --git a/src/components/TextInput/utils.ts b/src/components/TextInput/utils.ts index 6ca77029e2..e8da1a4de3 100644 --- a/src/components/TextInput/utils.ts +++ b/src/components/TextInput/utils.ts @@ -1,12 +1,7 @@ -import { - ColorValue, - Platform, - StyleProp, - TextStyle, - ViewStyle, -} from 'react-native'; - -import { AnimatedStyle } from 'react-native-reanimated'; +import { Platform } from 'react-native'; +import type { ColorValue, StyleProp, TextStyle, ViewStyle } from 'react-native'; + +import type { AnimatedStyle } from 'react-native-reanimated'; import { ACTIVE_INDICATOR_SIZE, diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index 71bc5bad9c..8d4cdd556f 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -1,18 +1,11 @@ import * as React from 'react'; -import { - GestureResponderEvent, - StyleProp, - StyleSheet, - ViewStyle, - View, - Animated, -} from 'react-native'; +import { StyleSheet, View, Animated } from 'react-native'; +import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; import { ToggleButtonGroupContext } from './ToggleButtonGroup'; import { getToggleButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; @@ -54,7 +47,7 @@ export type Props = { * @optional */ theme?: ThemeProp; - ref?: React.RefObject; + ref?: React.Ref; /** * testID to be used on tests. */ @@ -91,72 +84,66 @@ export type Props = { * * ``` */ -const ToggleButton = forwardRef( - ( - { - icon, - size, - theme: themeOverrides, - accessibilityLabel, - disabled, - style, - value, - status, - onPress, - ...rest - }: Props, - ref - ) => { - const theme = useInternalTheme(themeOverrides); - const borderRadius = theme.shapes.corner.extraSmall; +const ToggleButton = ({ + icon, + size, + theme: themeOverrides, + accessibilityLabel, + disabled, + style, + value, + status, + onPress, + ref, + ...rest +}: Props) => { + const theme = useInternalTheme(themeOverrides); + const borderRadius = theme.shapes.corner.extraSmall; - return ( - - {( - context: { value: string | null; onValueChange: Function } | null - ) => { - const checked: boolean | null = - (context && context.value === value) || status === 'checked'; + return ( + + {(context: { value: string | null; onValueChange: Function } | null) => { + const checked: boolean | null = + (context && context.value === value) || status === 'checked'; - const backgroundColor = getToggleButtonColor({ theme, checked }); - const borderColor = theme.colors.outline; + const backgroundColor = getToggleButtonColor({ theme, checked }); + const borderColor = theme.colors.outline; - return ( - { - if (onPress) { - onPress(e); - } + return ( + { + if (onPress) { + onPress(e); + } - if (context) { - context.onValueChange(!checked ? value : null); - } - }} - size={size} - accessibilityLabel={accessibilityLabel} - accessibilityState={{ disabled, selected: checked }} - disabled={disabled} - style={[ - styles.content, - { - backgroundColor, - borderRadius, - borderColor, - }, - style, - ]} - ref={ref} - theme={theme} - {...rest} - /> - ); - }} - - ); - } -); + if (context) { + context.onValueChange(!checked ? value : null); + } + }} + size={size} + accessibilityLabel={accessibilityLabel} + accessibilityState={{ disabled, selected: checked }} + disabled={disabled} + style={[ + styles.content, + { + backgroundColor, + borderRadius, + borderColor, + }, + style, + ]} + ref={ref} + theme={theme} + {...rest} + /> + ); + }} + + ); +}; const styles = StyleSheet.create({ content: { diff --git a/src/components/ToggleButton/ToggleButtonRow.tsx b/src/components/ToggleButton/ToggleButtonRow.tsx index 380ae77203..0b7d656446 100644 --- a/src/components/ToggleButton/ToggleButtonRow.tsx +++ b/src/components/ToggleButton/ToggleButtonRow.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { StyleSheet, View, StyleProp, ViewStyle } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import ToggleButton from './ToggleButton'; import ToggleButtonGroup from './ToggleButtonGroup'; diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 86e2e3cff5..36dc08971b 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -2,14 +2,14 @@ import * as React from 'react'; import { Dimensions, View, - LayoutChangeEvent, StyleSheet, Platform, Pressable, - ViewStyle, } from 'react-native'; +import type { LayoutChangeEvent, ViewStyle } from 'react-native'; -import { getTooltipPosition, Measurement, TooltipChildProps } from './utils'; +import { getTooltipPosition } from './utils'; +import type { Measurement, TooltipChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import { addEventListener } from '../../utils/addEventListener'; diff --git a/src/components/Tooltip/utils.ts b/src/components/Tooltip/utils.ts index 29dd14ba3e..43baf684fd 100644 --- a/src/components/Tooltip/utils.ts +++ b/src/components/Tooltip/utils.ts @@ -1,10 +1,5 @@ -import { - Dimensions, - LayoutRectangle, - StyleProp, - ViewStyle, - StyleSheet, -} from 'react-native'; +import { Dimensions, StyleSheet } from 'react-native'; +import type { LayoutRectangle, StyleProp, ViewStyle } from 'react-native'; type ChildrenMeasurement = { width: number; diff --git a/src/components/TouchableRipple/TouchableRipple.native.tsx b/src/components/TouchableRipple/TouchableRipple.native.tsx index b6c0f298ea..9165baf71b 100644 --- a/src/components/TouchableRipple/TouchableRipple.native.tsx +++ b/src/components/TouchableRipple/TouchableRipple.native.tsx @@ -1,22 +1,20 @@ import * as React from 'react'; -import { +import { Platform, StyleSheet, View } from 'react-native'; +import type { PressableAndroidRippleConfig, StyleProp, - Platform, ViewStyle, - StyleSheet, GestureResponderEvent, - View, ColorValue, } from 'react-native'; import type { PressableProps } from './Pressable'; import { Pressable } from './Pressable'; import { getTouchableRippleColors } from './utils'; -import { Settings, SettingsContext } from '../../core/settings'; +import { SettingsContext } from '../../core/settings'; +import type { Settings } from '../../core/settings'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import hasTouchHandler from '../../utils/hasTouchHandler'; const ANDROID_VERSION_LOLLIPOP = 21; @@ -35,23 +33,22 @@ export type Props = PressableProps & { underlayColor?: string; children: React.ReactNode; style?: StyleProp; + ref?: React.Ref; theme?: ThemeProp; }; -const TouchableRipple = ( - { - style, - background, - borderless = false, - disabled: disabledProp, - rippleColor, - underlayColor, - children, - theme: themeOverrides, - ...rest - }: Props, - ref: React.ForwardedRef -) => { +const TouchableRipple = ({ + style, + background, + borderless = false, + disabled: disabledProp, + rippleColor, + underlayColor, + children, + theme: themeOverrides, + ref, + ...rest +}: Props) => { const theme = useInternalTheme(themeOverrides); const { rippleEffectEnabled } = React.useContext(SettingsContext); @@ -141,6 +138,4 @@ const styles = StyleSheet.create({ }, }); -const Component = forwardRef(TouchableRipple); - -export default Component as typeof Component & { supported: boolean }; +export default TouchableRipple; diff --git a/src/components/TouchableRipple/TouchableRipple.tsx b/src/components/TouchableRipple/TouchableRipple.tsx index 86b5e14c5e..93c07111ef 100644 --- a/src/components/TouchableRipple/TouchableRipple.tsx +++ b/src/components/TouchableRipple/TouchableRipple.tsx @@ -1,11 +1,9 @@ import * as React from 'react'; -import { +import { Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, GestureResponderEvent, - Platform, StyleProp, - StyleSheet, - View, ViewStyle, } from 'react-native'; @@ -14,10 +12,10 @@ import color from 'color'; import type { PressableProps, PressableStateCallbackType } from './Pressable'; import { Pressable } from './Pressable'; import { getTouchableRippleColors } from './utils'; -import { Settings, SettingsContext } from '../../core/settings'; +import { SettingsContext } from '../../core/settings'; +import type { Settings } from '../../core/settings'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; import hasTouchHandler from '../../utils/hasTouchHandler'; export type Props = PressableProps & { @@ -72,6 +70,7 @@ export type Props = PressableProps & { | StyleProp | ((state: PressableStateCallbackType) => StyleProp) | undefined; + ref?: React.Ref; /** * @optional */ @@ -103,20 +102,18 @@ export type Props = PressableProps & { * * @extends Pressable props https://reactnative.dev/docs/Pressable#props */ -const TouchableRipple = ( - { - style, - background: _background, - borderless = false, - disabled: disabledProp, - rippleColor, - underlayColor: _underlayColor, - children, - theme: themeOverrides, - ...rest - }: Props, - ref: React.ForwardedRef -) => { +const TouchableRipple = ({ + style, + background: _background, + borderless = false, + disabled: disabledProp, + rippleColor, + underlayColor: _underlayColor, + children, + theme: themeOverrides, + ref, + ...rest +}: Props) => { const theme = useInternalTheme(themeOverrides); const { calculatedRippleColor } = getTouchableRippleColors({ theme, @@ -323,6 +320,4 @@ const styles = StyleSheet.create({ }, }); -const Component = forwardRef(TouchableRipple); - -export default Component as typeof Component & { supported: boolean }; +export default TouchableRipple; diff --git a/src/components/Typography/AnimatedText.tsx b/src/components/Typography/AnimatedText.tsx index 5da34b474d..ea47c43b58 100644 --- a/src/components/Typography/AnimatedText.tsx +++ b/src/components/Typography/AnimatedText.tsx @@ -1,14 +1,14 @@ import * as React from 'react'; -import { ReactNode } from 'react'; -import { Animated, StyleProp, StyleSheet, TextStyle, Text } from 'react-native'; +import type { ReactNode } from 'react'; +import { Animated, StyleSheet, Text } from 'react-native'; +import type { StyleProp, TextProps, TextStyle } from 'react-native'; import type { VariantProp } from './types'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; -type Props = React.ComponentPropsWithRef & { +type Props = Animated.AnimatedProps & { /** * Variant defines appropriate text styles for type role and its size. * Available variants: @@ -25,6 +25,7 @@ type Props = React.ComponentPropsWithRef & { */ variant?: VariantProp; style?: StyleProp; + ref?: React.Ref; /** * @optional */ @@ -36,59 +37,60 @@ type Props = React.ComponentPropsWithRef & { * * @extends Text props https://reactnative.dev/docs/text#props */ -const AnimatedText = forwardRef>( - function AnimatedText( - { style, theme: themeOverrides, variant, ...rest }, - ref - ) { - const theme = useInternalTheme(themeOverrides); - const { direction: writingDirection } = useLocale(); +function AnimatedText({ + style, + theme: themeOverrides, + variant, + ref, + ...rest +}: Props) { + const theme = useInternalTheme(themeOverrides); + const { direction: writingDirection } = useLocale(); - if (variant) { - const font = theme.fonts[variant]; - if (typeof font !== 'object') { - throw new Error( - `Variant ${variant} was not provided properly. Valid variants are ${Object.keys( - theme.fonts - ).join(', ')}.` - ); - } - - return ( - - ); - } else { - const font = theme.fonts.bodyMedium; - const textStyle = { - ...font, - color: theme.colors.onSurface, - }; - return ( - + if (variant) { + const font = theme.fonts[variant]; + if (typeof font !== 'object') { + throw new Error( + `Variant ${variant} was not provided properly. Valid variants are ${Object.keys( + theme.fonts + ).join(', ')}.` ); } + + return ( + + ); + } else { + const font = theme.fonts.bodyMedium; + const textStyle = { + ...font, + color: theme.colors.onSurface, + }; + return ( + + ); } -); +} const styles = StyleSheet.create({ text: { diff --git a/src/components/Typography/Text.tsx b/src/components/Typography/Text.tsx index 4ed4ddd529..df957411f9 100644 --- a/src/components/Typography/Text.tsx +++ b/src/components/Typography/Text.tsx @@ -1,18 +1,13 @@ import * as React from 'react'; -import { ReactNode } from 'react'; -import { - StyleProp, - StyleSheet, - Text as NativeText, - TextStyle, -} from 'react-native'; +import type { ReactNode } from 'react'; +import { StyleSheet, Text as NativeText } from 'react-native'; +import type { StyleProp, TextStyle } from 'react-native'; import AnimatedText from './AnimatedText'; import type { VariantProp } from './types'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import { forwardRef } from '../../utils/forwardRef'; export type Props = React.ComponentProps & { /** @@ -35,11 +30,12 @@ export type Props = React.ComponentProps & { children: React.ReactNode; theme?: ThemeProp; style?: StyleProp; + ref?: React.Ref; }; -export type TextRef = React.ForwardedRef<{ +export type TextRef = { setNativeProps(args: Object): void; -}>; +}; // @component-group Typography @@ -80,10 +76,13 @@ export type TextRef = React.ForwardedRef<{ * * @extends Text props https://reactnative.dev/docs/text#props */ -const Text = ( - { style, variant, theme: initialTheme, ...rest }: Props, - ref: TextRef -) => { +const Text = ({ + style, + variant, + theme: initialTheme, + ref, + ...rest +}: Props) => { const root = React.useRef(null); // FIXME: destructure it in TS 4.6+ const theme = useInternalTheme(initialTheme); @@ -173,11 +172,9 @@ const styles = StyleSheet.create({ }, }); -type TextComponent = ( - props: Props & { ref?: React.RefObject } -) => ReactNode; +type TextComponent = (props: Props) => ReactNode; -const Component = forwardRef(Text) as TextComponent; +const Component = Text as TextComponent; export const customText = () => Component as unknown as TextComponent; diff --git a/src/components/__tests__/ActivityIndicator.test.tsx b/src/components/__tests__/ActivityIndicator.test.tsx index 1b276f9234..e59daf24fd 100644 --- a/src/components/__tests__/ActivityIndicator.test.tsx +++ b/src/components/__tests__/ActivityIndicator.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { expect, it } from '@jest/globals'; import { render } from '../../test-utils'; import ActivityIndicator from '../ActivityIndicator'; diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 69a6ad13e5..420dd79f3a 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -1,8 +1,8 @@ -import React from 'react'; import { Animated } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; -import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; import { getTheme } from '../../../core/theming'; import { render } from '../../../test-utils'; @@ -148,22 +148,22 @@ describe('renderAppbarContent', () => { it('Is recognized as a header when no onPress callback has been pressed', () => { const { getByRole } = render( - + - + ); expect(getByRole('header')).toBeTruthy(); }); it('is recognized as a button when onPress callback has been passed', () => { const { getByTestId } = render( - + {}} /> - + ); expect(getByTestId('appbar-content').props.accessibilityRole).toEqual( @@ -178,11 +178,11 @@ describe('renderAppbarContent', () => { }); it('is recognized as a disabled button when onPress and disabled is passed', () => { const { getByTestId } = render( - + {}} disabled /> - + ); expect(getByTestId('appbar-content').props.accessibilityRole).toEqual( @@ -393,14 +393,14 @@ describe('animated value changes correctly', () => { it('header animated value changes correctly', () => { const value = new Animated.Value(1); const { getByTestId } = render( - + {null} - + ); expect(getByTestId('appbar-header-outer-layer')).toHaveStyle({ transform: [{ scale: 1 }], @@ -425,11 +425,11 @@ describe('animated value changes correctly', () => { const style = { borderBottomLeftRadius: 16, borderBottomRightRadius: 16 }; const { getByTestId } = render( - + {null} - + ); expect(getByTestId('appbar-header-root-layer')).toHaveStyle(style); }); diff --git a/src/components/__tests__/Appbar/react-native-safe-area-context-jest-mock.d.ts b/src/components/__tests__/Appbar/react-native-safe-area-context-jest-mock.d.ts deleted file mode 100644 index 12480d5c2a..0000000000 --- a/src/components/__tests__/Appbar/react-native-safe-area-context-jest-mock.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module 'react-native-safe-area-context/jest/mock' { - export default mockSafeAreaContext = { - SafeAreaProvider: React.Component, - }; -} diff --git a/src/components/__tests__/Avatar.test.tsx b/src/components/__tests__/Avatar.test.tsx index 19842d435a..549813369a 100644 --- a/src/components/__tests__/Avatar.test.tsx +++ b/src/components/__tests__/Avatar.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { StyleSheet } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { fireEvent } from '@testing-library/react-native'; import { render } from '../../test-utils'; diff --git a/src/components/__tests__/Badge.test.tsx b/src/components/__tests__/Badge.test.tsx index 3f7e6c8fcc..525363349d 100644 --- a/src/components/__tests__/Badge.test.tsx +++ b/src/components/__tests__/Badge.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { expect, it } from '@jest/globals'; import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; diff --git a/src/components/__tests__/Banner.test.tsx b/src/components/__tests__/Banner.test.tsx index 8066555354..70f35be7a7 100644 --- a/src/components/__tests__/Banner.test.tsx +++ b/src/components/__tests__/Banner.test.tsx @@ -1,6 +1,14 @@ -import * as React from 'react'; import { Animated, Image } from 'react-native'; +import { + afterAll, + beforeAll, + beforeEach, + describe, + expect, + it, + jest, +} from '@jest/globals'; import { act } from '@testing-library/react-native'; import { render } from '../../test-utils'; diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index 6034efd07c..4effa0b9c0 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Animated, Easing, Platform, StyleSheet } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { act, fireEvent } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; @@ -513,13 +513,16 @@ describe('getInactiveTintColor', () => { }); describe('getLabelColor', () => { - it.each` - tintColor | focused | expected - ${'#FBF7DB'} | ${true} | ${'#FBF7DB'} - ${'#853D4B'} | ${true} | ${'#853D4B'} - ${undefined} | ${true} | ${Palette.neutral10} - ${undefined} | ${false} | ${Palette.neutralVariant30} - `( + it.each([ + { tintColor: '#FBF7DB', focused: true, expected: '#FBF7DB' }, + { tintColor: '#853D4B', focused: true, expected: '#853D4B' }, + { tintColor: undefined, focused: true, expected: Palette.neutral10 }, + { + tintColor: undefined, + focused: false, + expected: Palette.neutralVariant30, + }, + ])( 'returns $expected when tintColor: $tintColor, focused: $focused', ({ tintColor, focused, expected }) => { const theme = getTheme(false); diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 7f631d5dcb..02268b8a1a 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { act, fireEvent } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index b11e006445..267cb65abb 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -1,6 +1,6 @@ -import React from 'react'; import { Animated, StyleSheet, Text } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; import { getTheme } from '../../../core/theming'; diff --git a/src/components/__tests__/Checkbox/Checkbox.test.tsx b/src/components/__tests__/Checkbox/Checkbox.test.tsx index 1a4d618387..63a35b6ad9 100644 --- a/src/components/__tests__/Checkbox/Checkbox.test.tsx +++ b/src/components/__tests__/Checkbox/Checkbox.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { expect, it } from '@jest/globals'; import { render } from '../../../test-utils'; import Checkbox from '../../Checkbox'; diff --git a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx index d66de5fef7..ee96e4a5a4 100644 --- a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx +++ b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; - +import { expect, it, jest } from '@jest/globals'; import { act, fireEvent } from '@testing-library/react-native'; import { render } from '../../../test-utils'; diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index f2476c2cc5..3e340bc961 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,3 +1,5 @@ +import { describe, expect, it } from '@jest/globals'; + import { getTheme } from '../../../core/theming'; import { tokens } from '../../../theme/tokens'; import { getSelectionVisualState } from '../../Checkbox/utils'; diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 7b724dd4ac..1c04130ebb 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Animated } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; import color from 'color'; diff --git a/src/components/__tests__/DataTable.test.tsx b/src/components/__tests__/DataTable.test.tsx index 01c70031ec..6d6e3a1dcf 100644 --- a/src/components/__tests__/DataTable.test.tsx +++ b/src/components/__tests__/DataTable.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { describe, expect, it } from '@jest/globals'; import { render } from '../../test-utils'; import Checkbox from '../Checkbox'; diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 742f44b941..eee5eaf631 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -1,12 +1,12 @@ -import React from 'react'; import { Text, StyleSheet, Platform, BackHandler as RNBackHandler, - BackHandlerStatic as RNBackHandlerStatic, } from 'react-native'; +import type { BackHandlerStatic as RNBackHandlerStatic } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { act, fireEvent } from '@testing-library/react-native'; import Dialog from '../../components/Dialog/Dialog'; diff --git a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx index 18908acc8a..9538fcc56e 100644 --- a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx +++ b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import { describe, expect, it } from '@jest/globals'; import { render } from '../../../test-utils'; import DrawerCollapsedItem from '../../Drawer/DrawerCollapsedItem'; diff --git a/src/components/__tests__/Drawer/DrawerSection.test.tsx b/src/components/__tests__/Drawer/DrawerSection.test.tsx index feb8c2aad7..08283ba7a2 100644 --- a/src/components/__tests__/Drawer/DrawerSection.test.tsx +++ b/src/components/__tests__/Drawer/DrawerSection.test.tsx @@ -1,6 +1,7 @@ -import React from 'react'; import { View } from 'react-native'; +import { describe, expect, it } from '@jest/globals'; + import { render } from '../../../test-utils'; import DrawerSection from '../../Drawer/DrawerSection'; diff --git a/src/components/__tests__/DrawerItem.test.tsx b/src/components/__tests__/DrawerItem.test.tsx index a12478d51c..375d1c460b 100644 --- a/src/components/__tests__/DrawerItem.test.tsx +++ b/src/components/__tests__/DrawerItem.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { expect, it } from '@jest/globals'; import { render } from '../../test-utils'; import DrawerItem from '../Drawer/DrawerItem'; diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index c357914d96..38ee6308b6 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; - +import { expect, it, jest } from '@jest/globals'; import { fireEvent } from '@testing-library/react-native'; import { render } from '../../test-utils'; diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx index ca72466305..e3c1490f65 100644 --- a/src/components/__tests__/FABExtended.test.tsx +++ b/src/components/__tests__/FABExtended.test.tsx @@ -1,5 +1,4 @@ -import * as React from 'react'; - +import { expect, it, jest } from '@jest/globals'; import { fireEvent } from '@testing-library/react-native'; import { render } from '../../test-utils'; diff --git a/src/components/__tests__/FABMenu.test.tsx b/src/components/__tests__/FABMenu.test.tsx index cff16cc76f..87b3b38f2f 100644 --- a/src/components/__tests__/FABMenu.test.tsx +++ b/src/components/__tests__/FABMenu.test.tsx @@ -1,15 +1,14 @@ -import * as React from 'react'; - +import { expect, it, jest } from '@jest/globals'; import { fireEvent } from '@testing-library/react-native'; import { render } from '../../test-utils'; import FAB from '../FAB'; const makeItems = ( - onItemPress = jest.fn() + onItemPress = jest.fn<(event?: unknown) => void>() ): [ - { label: string; onPress: jest.Mock; testID: string }, - { label: string; onPress: jest.Mock; testID: string } + { label: string; onPress: typeof onItemPress; testID: string }, + { label: string; onPress: typeof onItemPress; testID: string } ] => [ { label: 'Send email', onPress: onItemPress, testID: 'item-0' }, { label: 'Set reminder', onPress: onItemPress, testID: 'item-1' }, diff --git a/src/components/__tests__/FABUtils.test.tsx b/src/components/__tests__/FABUtils.test.tsx index af00e98f1c..b72de366f7 100644 --- a/src/components/__tests__/FABUtils.test.tsx +++ b/src/components/__tests__/FABUtils.test.tsx @@ -1,3 +1,5 @@ +import { describe, expect, it } from '@jest/globals'; + import { getTheme } from '../../core/theming'; import { getDimensions, resolveColors } from '../FAB/utils'; diff --git a/src/components/__tests__/Icon.test.tsx b/src/components/__tests__/Icon.test.tsx index 3c1d55af19..940637e1a3 100644 --- a/src/components/__tests__/Icon.test.tsx +++ b/src/components/__tests__/Icon.test.tsx @@ -1,6 +1,7 @@ -import React from 'react'; import { Image } from 'react-native'; +import { describe, expect, it } from '@jest/globals'; + import { render } from '../../test-utils'; import Icon from '../Icon'; diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index 0f34aaec38..e61594509e 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Animated, StyleSheet } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index 96b057e5b1..b1e4ac79d2 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -1,6 +1,7 @@ -import * as React from 'react'; import { StyleSheet, View } from 'react-native'; +import { describe, expect, it } from '@jest/globals'; + import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; diff --git a/src/components/__tests__/ListImage.test.tsx b/src/components/__tests__/ListImage.test.tsx index 2277eb324a..96c524467a 100644 --- a/src/components/__tests__/ListImage.test.tsx +++ b/src/components/__tests__/ListImage.test.tsx @@ -1,6 +1,7 @@ -import * as React from 'react'; import { StyleSheet } from 'react-native'; +import { expect, it } from '@jest/globals'; + import { render } from '../../test-utils'; import ListImage from '../List/ListImage'; diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index e1c2f20357..088be073fc 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -1,7 +1,8 @@ -import * as React from 'react'; +import type { GestureResponderEvent } from 'react-native'; import { Platform, StyleSheet } from 'react-native'; import { Text, View } from 'react-native'; +import { expect, it, jest } from '@jest/globals'; import { fireEvent } from '@testing-library/react-native'; import { render } from '../../test-utils'; @@ -133,7 +134,7 @@ it('renders with a description with typeof number', () => { it('calling onPress on ListItem right component', () => { Platform.OS = 'web'; - const onPress = jest.fn(); + const onPress = jest.fn<(event: GestureResponderEvent) => void>(); const { getByTestId } = render( { describe('on default platform', () => { beforeAll(() => { - jest.mock('react-native', () => { - const RN = jest.requireActual('react-native'); + mockJest.mock('react-native', () => { + const RN = + mockJest.requireActual('react-native'); - RN.Platform = () => ({ - select: (objs: { default: object }) => objs.default, - }); - - return RN; + return { + ...RN, + Platform: { + ...RN.Platform, + select: (objs: { default: object }) => objs.default, + }, + }; }); }); @@ -27,14 +36,17 @@ describe('RadioButton', () => { describe('on ios platform', () => { beforeAll(() => { - jest.mock('react-native', () => { - const RN = jest.requireActual('react-native'); - - RN.Platform = () => ({ - select: (objs: { ios: object }) => objs.ios, - }); + mockJest.mock('react-native', () => { + const RN = + mockJest.requireActual('react-native'); - return RN; + return { + ...RN, + Platform: { + ...RN.Platform, + select: (objs: { ios: object }) => objs.ios, + }, + }; }); }); diff --git a/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx b/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx index ce1ae61c70..b4fe69463e 100644 --- a/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import { describe, expect, it } from '@jest/globals'; import { render } from '../../../test-utils'; import RadioButton from '../../RadioButton'; diff --git a/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx b/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx index 6afd21a48a..b8714367bb 100644 --- a/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Platform } from 'react-native'; +import { expect, it, jest } from '@jest/globals'; import { act, fireEvent } from '@testing-library/react-native'; import { render } from '../../../test-utils'; diff --git a/src/components/__tests__/RadioButton/utils.test.tsx b/src/components/__tests__/RadioButton/utils.test.tsx index 12486a9c3b..e0a6736bd6 100644 --- a/src/components/__tests__/RadioButton/utils.test.tsx +++ b/src/components/__tests__/RadioButton/utils.test.tsx @@ -1,3 +1,5 @@ +import { describe, expect, it } from '@jest/globals'; + import { getTheme } from '../../../core/theming'; import { tokens } from '../../../theme/tokens'; import { getSelectionControlIOSColor } from '../../RadioButton/utils'; diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 255225f7ae..12b66e5d11 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Animated } from 'react-native'; +import { expect, it, jest } from '@jest/globals'; import { act, fireEvent } from '@testing-library/react-native'; import { render } from '../../test-utils'; diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 0904d02829..144f12992e 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { describe, expect, it, jest } from '@jest/globals'; import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; @@ -55,20 +55,75 @@ it('renders checked segmented button with selected check', async () => { }); describe('getSegmentedButtonColors', () => { - it.each` - theme | disabled | checked | checkedColor | uncheckedColor | expected - ${getTheme()} | ${false} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSecondaryContainer} - ${getTheme()} | ${false} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} - ${getTheme()} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} - ${getTheme()} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} - ${getTheme()} | ${false} | ${true} | ${'a125f5'} | ${undefined} | ${'a125f5'} - ${getTheme()} | ${false} | ${false} | ${undefined} | ${'000'} | ${'000'} - ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${'000'} | ${'000'} - ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${undefined} | ${getTheme().colors.onSurface} - ${getTheme()} | ${false} | ${true} | ${undefined} | ${'000'} | ${getTheme().colors.onSecondaryContainer} - `( + const theme = getTheme(); + + it.each([ + { + disabled: false, + checked: true, + checkedColor: undefined, + uncheckedColor: undefined, + expected: theme.colors.onSecondaryContainer, + }, + { + disabled: false, + checked: false, + checkedColor: undefined, + uncheckedColor: undefined, + expected: theme.colors.onSurface, + }, + { + disabled: true, + checked: true, + checkedColor: undefined, + uncheckedColor: undefined, + expected: theme.colors.onSurface, + }, + { + disabled: true, + checked: false, + checkedColor: undefined, + uncheckedColor: undefined, + expected: theme.colors.onSurface, + }, + { + disabled: false, + checked: true, + checkedColor: 'a125f5', + uncheckedColor: undefined, + expected: 'a125f5', + }, + { + disabled: false, + checked: false, + checkedColor: undefined, + uncheckedColor: '000', + expected: '000', + }, + { + disabled: false, + checked: false, + checkedColor: 'a125f5', + uncheckedColor: '000', + expected: '000', + }, + { + disabled: false, + checked: false, + checkedColor: 'a125f5', + uncheckedColor: undefined, + expected: theme.colors.onSurface, + }, + { + disabled: false, + checked: true, + checkedColor: undefined, + uncheckedColor: '000', + expected: theme.colors.onSecondaryContainer, + }, + ])( 'returns $expected when disabled: $disabled, checked: $checked, checkedColor is $checkedColor and uncheckedColor is $uncheckedColor', - ({ theme, disabled, checked, checkedColor, uncheckedColor, expected }) => { + ({ disabled, checked, checkedColor, uncheckedColor, expected }) => { expect( getSegmentedButtonColors({ theme, @@ -347,7 +402,7 @@ describe('should have `accessibilityState={ checked: true }` when selected', () it('should have two button selected', () => { const onValueChange = jest.fn(); const { getAllByA11yState } = render( - multiSelect value={['walk', 'transit']} buttons={[ @@ -367,7 +422,7 @@ describe('should have `accessibilityState={ checked: true }` when selected', () const onValueChange = jest.fn(); const { getByTestId } = render( - multiSelect value={['walk', 'transit']} buttons={[ diff --git a/src/components/__tests__/Snackbar.test.tsx b/src/components/__tests__/Snackbar.test.tsx index 334a9bca0a..129db058e5 100644 --- a/src/components/__tests__/Snackbar.test.tsx +++ b/src/components/__tests__/Snackbar.test.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Animated, StyleSheet, Text, View } from 'react-native'; +import { expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; import { render } from '../../test-utils'; diff --git a/src/components/__tests__/Surface.test.tsx b/src/components/__tests__/Surface.test.tsx index c58f173945..879f5b48ee 100644 --- a/src/components/__tests__/Surface.test.tsx +++ b/src/components/__tests__/Surface.test.tsx @@ -1,11 +1,18 @@ -import * as React from 'react'; +import type { ViewStyle } from 'react-native'; import { StyleSheet } from 'react-native'; import { Platform } from 'react-native'; +import { describe, expect, it } from '@jest/globals'; + import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; import Surface from '../Surface'; +type StyleCase = { + property: keyof ViewStyle; + value: ViewStyle[keyof ViewStyle]; +}; + describe('Surface', () => { it('should properly render passed props', () => { const testID = 'surface-container'; @@ -68,85 +75,91 @@ describe('Surface', () => { }); }); - it.each` - property | value - ${'opacity'} | ${0.7} - ${'transform'} | ${[{ scale: 1.02 }]} - ${'width'} | ${'42%'} - ${'height'} | ${'32.5%'} - ${'margin'} | ${13} - ${'marginLeft'} | ${13.1} - ${'marginRight'} | ${13.2} - ${'marginTop'} | ${13.3} - ${'marginBottom'} | ${13.4} - ${'marginHorizontal'} | ${13.5} - ${'marginVertical'} | ${13.6} - ${'position'} | ${'absolute'} - ${'alignSelf'} | ${'flex-start'} - ${'top'} | ${1.1} - ${'right'} | ${1.2} - ${'bottom'} | ${1.3} - ${'left'} | ${1.4} - ${'start'} | ${1.5} - ${'end'} | ${1.6} - ${'flex'} | ${6} - `('applies $property to outer layer only', ({ property, value }) => { - const style = { [property]: value }; + it.each([ + { property: 'opacity', value: 0.7 }, + { property: 'transform', value: [{ scale: 1.02 }] }, + { property: 'width', value: '42%' }, + { property: 'height', value: '32.5%' }, + { property: 'margin', value: 13 }, + { property: 'marginLeft', value: 13.1 }, + { property: 'marginRight', value: 13.2 }, + { property: 'marginTop', value: 13.3 }, + { property: 'marginBottom', value: 13.4 }, + { property: 'marginHorizontal', value: 13.5 }, + { property: 'marginVertical', value: 13.6 }, + { property: 'position', value: 'absolute' }, + { property: 'alignSelf', value: 'flex-start' }, + { property: 'top', value: 1.1 }, + { property: 'right', value: 1.2 }, + { property: 'bottom', value: 1.3 }, + { property: 'left', value: 1.4 }, + { property: 'start', value: 1.5 }, + { property: 'end', value: 1.6 }, + { property: 'flex', value: 6 }, + ] satisfies StyleCase[])( + 'applies $property to outer layer only', + ({ property, value }) => { + const style = { [property]: value }; - const { getByTestId } = render( - - {null} - - ); + const { getByTestId } = render( + + {null} + + ); - expect(getByTestId('surface-test-outer-layer')).toHaveStyle(style); - expect(getByTestId('surface-test')).not.toHaveStyle(style); - }); + expect(getByTestId('surface-test-outer-layer')).toHaveStyle(style); + expect(getByTestId('surface-test')).not.toHaveStyle(style); + } + ); - it.each` - property | value - ${'padding'} | ${12} - ${'paddingLeft'} | ${12.1} - ${'paddingRight'} | ${12.2} - ${'paddingTop'} | ${12.3} - ${'paddingBottom'} | ${12.4} - ${'paddingHorizontal'} | ${12.5} - ${'paddingVertical'} | ${12.6} - ${'borderWidth'} | ${2} - ${'borderColor'} | ${'black'} - `('applies $property to inner layer only', ({ property, value }) => { - const style = { [property]: value }; + it.each([ + { property: 'padding', value: 12 }, + { property: 'paddingLeft', value: 12.1 }, + { property: 'paddingRight', value: 12.2 }, + { property: 'paddingTop', value: 12.3 }, + { property: 'paddingBottom', value: 12.4 }, + { property: 'paddingHorizontal', value: 12.5 }, + { property: 'paddingVertical', value: 12.6 }, + { property: 'borderWidth', value: 2 }, + { property: 'borderColor', value: 'black' }, + ] satisfies StyleCase[])( + 'applies $property to inner layer only', + ({ property, value }) => { + const style = { [property]: value }; - const { getByTestId } = render( - - {null} - - ); + const { getByTestId } = render( + + {null} + + ); - expect(getByTestId('surface-test-outer-layer')).not.toHaveStyle(style); - expect(getByTestId('surface-test')).toHaveStyle(style); - }); + expect(getByTestId('surface-test-outer-layer')).not.toHaveStyle(style); + expect(getByTestId('surface-test')).toHaveStyle(style); + } + ); - it.each` - property | value - ${'borderRadius'} | ${3} - ${'borderTopLeftRadius'} | ${1} - ${'borderTopRightRadius'} | ${2} - ${'borderBottomLeftRadius'} | ${3} - ${'borderBottomRightRadius'} | ${4} - ${'backgroundColor'} | ${'rgb(4, 5, 6)'} - `('applies $property to every layer', ({ property, value }) => { - const style = { [property]: value }; + it.each([ + { property: 'borderRadius', value: 3 }, + { property: 'borderTopLeftRadius', value: 1 }, + { property: 'borderTopRightRadius', value: 2 }, + { property: 'borderBottomLeftRadius', value: 3 }, + { property: 'borderBottomRightRadius', value: 4 }, + { property: 'backgroundColor', value: 'rgb(4, 5, 6)' }, + ] satisfies StyleCase[])( + 'applies $property to every layer', + ({ property, value }) => { + const style = { [property]: value }; - const { getByTestId } = render( - - {null} - - ); + const { getByTestId } = render( + + {null} + + ); - expect(getByTestId('surface-test-outer-layer')).toHaveStyle(style); - expect(getByTestId('surface-test')).toHaveStyle(style); - }); + expect(getByTestId('surface-test-outer-layer')).toHaveStyle(style); + expect(getByTestId('surface-test')).toHaveStyle(style); + } + ); describe('outer layer', () => { it('should not render rest style', () => { diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index 0c781d67f2..bdc34ca48a 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { describe, expect, it, jest } from '@jest/globals'; import { fireEvent, render } from '../../test-utils'; import Switch from '../Switch/Switch'; diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index c1df23b5fa..14ded21b21 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -5,6 +5,16 @@ import { TextInput as NativeTextInput, View, } from 'react-native'; +import type { GestureResponderEvent } from 'react-native'; + +import { + afterAll, + afterEach, + beforeAll, + expect, + it, + jest, +} from '@jest/globals'; import { act, fireEvent, render } from '../../test-utils'; import { tokens } from '../../theme/tokens'; @@ -145,7 +155,7 @@ it('renders outlined TextInput with TextInput.Icon accessories when error is tru }); it('fires onPress on TextInput.Icon end accessory', () => { - const onClear = jest.fn(); + const onClear = jest.fn<(event: GestureResponderEvent) => void>(); const { getAllByTestId } = render( ({ }), })); -const DummyComponent = React.forwardRef((props, ref) => ( - }> +const DummyComponent = ({ + ref, + ...props +}: ViewProps & { + ref?: React.RefObject; +}) => ( + dummy component -)); +); describe('Tooltip', () => { const getTrigger = (getByText: (text: string) => ReactTestInstance) => @@ -77,8 +92,12 @@ describe('Tooltip', () => { jest.clearAllMocks(); }); describe('Unmount', () => { - beforeAll(() => jest.spyOn(global, 'clearTimeout')); - afterEach(() => jest.clearAllMocks()); + beforeAll(() => { + jest.spyOn(global, 'clearTimeout'); + }); + afterEach(() => { + jest.clearAllMocks(); + }); it('removes hideTooltipTimer when the component unmounts', async () => { const { @@ -108,8 +127,12 @@ describe('Tooltip', () => { }); describe('longPress', () => { - beforeAll(() => jest.spyOn(global, 'clearTimeout')); - afterEach(() => jest.clearAllMocks()); + beforeAll(() => { + jest.spyOn(global, 'clearTimeout'); + }); + afterEach(() => { + jest.clearAllMocks(); + }); it('clears the hide timer when the user start pressing the component again', () => { jest.spyOn(global, 'clearTimeout'); @@ -263,8 +286,12 @@ describe('Tooltip', () => { Platform.OS = 'web'; }); describe('Unmount', () => { - beforeAll(() => jest.spyOn(global, 'clearTimeout')); - afterEach(() => jest.clearAllMocks()); + beforeAll(() => { + jest.spyOn(global, 'clearTimeout'); + }); + afterEach(() => { + jest.clearAllMocks(); + }); it('removes showTooltipTimer when the component unmounts', async () => { const { @@ -307,8 +334,12 @@ describe('Tooltip', () => { }); describe('hoverIn', () => { - beforeAll(() => jest.spyOn(global, 'clearTimeout')); - afterEach(() => jest.clearAllMocks()); + beforeAll(() => { + jest.spyOn(global, 'clearTimeout'); + }); + afterEach(() => { + jest.clearAllMocks(); + }); it('clears the hide timer when the user start hovering the component again', () => { jest.spyOn(global, 'clearTimeout'); diff --git a/src/components/__tests__/TouchableRipple.test.tsx b/src/components/__tests__/TouchableRipple.test.tsx index 89bebfa7fa..4448301244 100644 --- a/src/components/__tests__/TouchableRipple.test.tsx +++ b/src/components/__tests__/TouchableRipple.test.tsx @@ -1,6 +1,7 @@ -import React from 'react'; import { Platform, Text } from 'react-native'; +import type { GestureResponderEvent } from 'react-native'; +import { describe, expect, it, jest } from '@jest/globals'; import { fireEvent } from '@testing-library/react-native'; import { render } from '../../test-utils'; @@ -18,7 +19,7 @@ describe('TouchableRipple', () => { }); it('calls onPress when pressed', () => { - const onPress = jest.fn(); + const onPress = jest.fn<(event: GestureResponderEvent) => void>(); const { getByText } = render( Button @@ -31,7 +32,7 @@ describe('TouchableRipple', () => { }); it('disables the button when disabled prop is true', () => { - const onPress = jest.fn(); + const onPress = jest.fn<(event: GestureResponderEvent) => void>(); const { getByText } = render( Button diff --git a/src/components/__tests__/Typography/Text.test.tsx b/src/components/__tests__/Typography/Text.test.tsx index 3fc4b37361..c861343135 100644 --- a/src/components/__tests__/Typography/Text.test.tsx +++ b/src/components/__tests__/Typography/Text.test.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { expect, it, jest } from '@jest/globals'; import PaperProvider from '../../../core/PaperProvider'; import { render } from '../../../test-utils'; diff --git a/src/core/PaperProvider.tsx b/src/core/PaperProvider.tsx index 67cd77d1b7..8c1343dbd9 100644 --- a/src/core/PaperProvider.tsx +++ b/src/core/PaperProvider.tsx @@ -2,7 +2,8 @@ import * as React from 'react'; import { getDefaultDirection, LocaleProvider, type Direction } from './locale'; import SafeAreaProviderCompat from './SafeAreaProviderCompat'; -import { Provider as SettingsProvider, Settings } from './settings'; +import { Provider as SettingsProvider } from './settings'; +import type { Settings } from './settings'; import { defaultThemes, ThemeProvider } from './theming'; import { useResolvedReduceMotion, diff --git a/src/core/SafeAreaProviderCompat.tsx b/src/core/SafeAreaProviderCompat.tsx index a258f297cb..7fed0523e5 100644 --- a/src/core/SafeAreaProviderCompat.tsx +++ b/src/core/SafeAreaProviderCompat.tsx @@ -2,14 +2,8 @@ * Ported from @react-navigation https://github.com/react-navigation/react-navigation/blob/main/packages/elements/src/SafeAreaProviderCompat.tsx */ import * as React from 'react'; -import { - Dimensions, - Platform, - StyleProp, - StyleSheet, - View, - ViewStyle, -} from 'react-native'; +import { Dimensions, Platform, StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; import { initialWindowMetrics, diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 4d88e679dd..70d90a3ecf 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -1,12 +1,13 @@ -import * as React from 'react'; -import { - Appearance, - AccessibilityInfo, - Platform, - View, - ColorSchemeName, -} from 'react-native'; +import { Appearance, AccessibilityInfo, Platform, View } from 'react-native'; +import type { ColorSchemeName } from 'react-native'; +import { + beforeEach, + describe, + expect, + it, + jest as mockJest, +} from '@jest/globals'; import { render, act } from '@testing-library/react-native'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; @@ -43,20 +44,28 @@ declare module 'react-native' { } const mockAppearance = () => { - jest.mock('react-native/Libraries/Utilities/Appearance', () => { - const realApp = jest.requireActual( - 'react-native/Libraries/Utilities/Appearance' - ); - const listeners: Function[] = []; + mockJest.mock('react-native/Libraries/Utilities/Appearance', () => { + const realApp = mockJest.requireActual< + typeof import('react-native/Libraries/Utilities/Appearance') + >('react-native/Libraries/Utilities/Appearance'); + + const listeners: Array< + (options: { colorScheme: ColorSchemeName }) => void + > = []; + return { ...realApp, - addChangeListener: jest.fn((cb) => { - listeners.push(cb); - }), - removeChangeListener: jest.fn((cb) => { - listeners.push(cb); - }), - getColorScheme: jest.fn(() => { + addChangeListener: mockJest.fn( + (cb: (options: { colorScheme: ColorSchemeName }) => void) => { + listeners.push(cb); + } + ), + removeChangeListener: mockJest.fn( + (cb: (options: { colorScheme: ColorSchemeName }) => void) => { + listeners.push(cb); + } + ), + getColorScheme: mockJest.fn(() => { return 'light'; }), __internalListeners: listeners, @@ -65,25 +74,29 @@ const mockAppearance = () => { }; const mockAccessibilityInfo = () => { - jest.mock( + mockJest.mock( 'react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo', () => { - const realApp = jest.requireActual( + const realApp = mockJest.requireActual<{ + default: typeof AccessibilityInfo; + }>( 'react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo' ); - const listeners: Function[] = []; + const listeners: Array<(enabled: boolean) => void> = []; return { __esModule: true, default: { realApp, - addEventListener: jest.fn((_event, cb) => { - listeners.push(cb); - }), - removeEventListener: jest.fn((cb) => { + addEventListener: mockJest.fn( + (_event, cb: (enabled: boolean) => void) => { + listeners.push(cb); + } + ), + removeEventListener: mockJest.fn((cb: (enabled: boolean) => void) => { listeners.push(cb); }), - isReduceMotionEnabled: jest.fn(() => Promise.resolve(false)), + isReduceMotionEnabled: mockJest.fn(() => Promise.resolve(false)), __internalListeners: listeners, }, }; @@ -111,7 +124,7 @@ const defaultPlatform = Platform.OS; describe('PaperProvider', () => { beforeEach(() => { - jest.resetModules(); + mockJest.resetModules(); Platform.OS = defaultPlatform; }); @@ -245,7 +258,7 @@ describe('PaperProvider', () => { }); it('uses default theme, if Appearance module is not defined', async () => { - jest.mock('react-native/Libraries/Utilities/Appearance', () => { + mockJest.mock('react-native/Libraries/Utilities/Appearance', () => { return null; }); const { getByTestId } = render(createProvider()); @@ -255,15 +268,26 @@ describe('PaperProvider', () => { ); }); - it.each` - label | theme | colorScheme - ${'default theme'} | ${ExtendedLightTheme} | ${'light'} - ${'dark theme'} | ${ExtendedDarkTheme} | ${'dark'} - `( + it.each([ + { + label: 'default theme', + theme: ExtendedLightTheme, + colorScheme: 'light', + }, + { + label: 'dark theme', + theme: ExtendedDarkTheme, + colorScheme: 'dark', + }, + ] satisfies Array<{ + label: string; + theme: ThemeProp; + colorScheme: ColorSchemeName; + }>)( 'provides $label for $colorScheme color scheme', async ({ theme, colorScheme }) => { mockAppearance(); - (Appearance.getColorScheme as jest.Mock).mockReturnValue(colorScheme); + mockJest.mocked(Appearance.getColorScheme).mockReturnValue(colorScheme); const { getByTestId } = render(createProvider()); expect(getByTestId('provider-child-view').props.theme).toStrictEqual( theme diff --git a/src/core/__tests__/theming.test.tsx b/src/core/__tests__/theming.test.tsx index cfca524f6f..cb61aef20d 100644 --- a/src/core/__tests__/theming.test.tsx +++ b/src/core/__tests__/theming.test.tsx @@ -1,3 +1,5 @@ +import { describe, expect, it } from '@jest/globals'; + import { DarkTheme, LightTheme } from '../../theme/schemes'; import { adaptNavigationTheme } from '../theming'; diff --git a/src/core/settings.tsx b/src/core/settings.tsx index 446aec36a3..4082db3b62 100644 --- a/src/core/settings.tsx +++ b/src/core/settings.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; -import MaterialCommunityIcon, { - IconProps, -} from '../components/MaterialCommunityIcon'; +import MaterialCommunityIcon from '../components/MaterialCommunityIcon'; +import type { IconProps } from '../components/MaterialCommunityIcon'; export type Settings = { icon?: ({ diff --git a/src/core/useSystemColorScheme.ts b/src/core/useSystemColorScheme.ts index 11501db016..3dd7a50821 100644 --- a/src/core/useSystemColorScheme.ts +++ b/src/core/useSystemColorScheme.ts @@ -1,5 +1,6 @@ import * as React from 'react'; -import { Appearance, ColorSchemeName } from 'react-native'; +import { Appearance } from 'react-native'; +import type { ColorSchemeName } from 'react-native'; /** * Subscribes to the OS color-scheme setting via `Appearance.addChangeListener` diff --git a/src/theme/__tests__/provider.test.ts b/src/theme/__tests__/provider.test.ts index bf4f162418..e8635dfe90 100644 --- a/src/theme/__tests__/provider.test.ts +++ b/src/theme/__tests__/provider.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from '@jest/globals'; + import { isPlatformColorSentinel, safeMerge } from '../provider'; describe('isPlatformColorSentinel', () => { diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index 6017c6a6da..a8ecf34c34 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import type { ComponentType } from 'react'; -import { $DeepPartial, createTheming } from '@callstack/react-theme-provider'; +import { createTheming } from '@callstack/react-theme-provider'; +import type { $DeepPartial } from '@callstack/react-theme-provider'; import { DarkTheme, LightTheme } from './schemes'; import type { Theme, NavigationTheme } from './types'; diff --git a/src/utils/__tests__/hasTouchHandler.test.ts b/src/utils/__tests__/hasTouchHandler.test.ts index c876493621..5e17811d39 100644 --- a/src/utils/__tests__/hasTouchHandler.test.ts +++ b/src/utils/__tests__/hasTouchHandler.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it, jest } from '@jest/globals'; + import hasTouchHandler from '../hasTouchHandler'; describe('hasTouchHandler', () => { diff --git a/src/utils/__tests__/splitStyles.test.ts b/src/utils/__tests__/splitStyles.test.ts index 7019159325..c27a53d940 100644 --- a/src/utils/__tests__/splitStyles.test.ts +++ b/src/utils/__tests__/splitStyles.test.ts @@ -1,5 +1,7 @@ import type { ViewStyle } from 'react-native'; +import { describe, expect, it } from '@jest/globals'; + import { splitStyles } from '../splitStyles'; describe('splitStyles', () => { diff --git a/src/utils/forwardRef.tsx b/src/utils/forwardRef.tsx deleted file mode 100644 index 0761712d2a..0000000000 --- a/src/utils/forwardRef.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from 'react'; -import type { - ForwardRefRenderFunction, - PropsWithoutRef, - RefAttributes, - ForwardRefExoticComponent, -} from 'react'; - -export type ForwardRefComponent = ForwardRefExoticComponent< - PropsWithoutRef

& RefAttributes ->; - -/** - * TypeScript generated a large union of props from `ViewProps` in - * `d.ts` files when using `React.forwardRef`. To prevent this - * `ForwardRefComponent` was created and exported. Use this - * `forwardRef` instead of `React.forwardRef` so you don't have to - * import `ForwardRefComponent`. - * More info: https://github.com/callstack/react-native-paper/pull/3603 - */ -export const forwardRef: ( - render: ForwardRefRenderFunction> -) => ForwardRefComponent = React.forwardRef; diff --git a/src/utils/useIsKeyboardShown.tsx b/src/utils/useIsKeyboardShown.tsx index 49cc3db9ac..cefe10cc72 100644 --- a/src/utils/useIsKeyboardShown.tsx +++ b/src/utils/useIsKeyboardShown.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { Keyboard, NativeEventSubscription, Platform } from 'react-native'; +import { Keyboard, Platform } from 'react-native'; +import type { NativeEventSubscription } from 'react-native'; type Props = { onShow: () => void; diff --git a/tsconfig.json b/tsconfig.json index e2ff899f74..a8ae392553 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,18 +9,21 @@ "./src/react-navigation" ] }, + "noEmit": true, + "customConditions": ["react-native-paper-source"], "allowUnreachableCode": false, "allowUnusedLabels": false, + "erasableSyntaxOnly": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "jsx": "react", + "jsx": "react-jsx", "lib": [ "esnext", "dom" ], "moduleSuffixes": [".ios", ".native", ".android", ""], - "module": "esnext", - "moduleResolution": "node", + "module": "preserve", + "moduleResolution": "bundler", "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, "noImplicitUseStrict": false, @@ -28,10 +31,12 @@ "noUnusedLocals": true, "noUnusedParameters": true, "resolveJsonModule": true, + "resolvePackageJsonImports": false, "skipLibCheck": true, "strict": true, "target": "esnext", - "types": ["jest", "node", "@testing-library/jest-native"] + "types": ["node"], + "verbatimModuleSyntax": true }, "exclude": [ "lib/**/*" diff --git a/yarn.lock b/yarn.lock index 07931713e5..cdea0b5acd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10811,6 +10811,7 @@ __metadata: "@docusaurus/preset-classic": "npm:^2.3.6" "@docusaurus/remark-plugin-npm2yarn": "npm:^2.3.6" "@easyops-cn/docusaurus-search-local": "npm:^0.33.4" + "@jest/globals": "npm:^29.7.0" "@material/material-color-utilities": "npm:0.2.4" "@mdx-js/react": "npm:^1.6.22" "@react-native-vector-icons/material-design-icons": "npm:^12.0.0" @@ -11869,7 +11870,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^4.0.2, execa@npm:^4.0.3": +"execa@npm:^4.0.2": version: 4.1.0 resolution: "execa@npm:4.1.0" dependencies: @@ -14665,16 +14666,6 @@ __metadata: languageName: node linkType: hard -"is-absolute@npm:^1.0.0": - version: 1.0.0 - resolution: "is-absolute@npm:1.0.0" - dependencies: - is-relative: "npm:^1.0.0" - is-windows: "npm:^1.0.1" - checksum: 10c0/422302ce879d4f3ca6848499b6f3ddcc8fd2dc9f3e9cad3f6bcedff58cdfbbbd7f4c28600fffa7c59a858f1b15c27fb6cfe1d5275e58a36d2bf098a44ef5abc4 - languageName: node - linkType: hard - "is-accessor-descriptor@npm:^1.0.1": version: 1.0.1 resolution: "is-accessor-descriptor@npm:1.0.1" @@ -14986,26 +14977,6 @@ __metadata: languageName: node linkType: hard -"is-git-dirty@npm:^2.0.2": - version: 2.0.2 - resolution: "is-git-dirty@npm:2.0.2" - dependencies: - execa: "npm:^4.0.3" - is-git-repository: "npm:^2.0.0" - checksum: 10c0/e7c223fb26416af52e0f3aba97b0147394fd96314d0a620cc4b44543538d62951ebaf049919a7f5df9bab270679dfde277b848d087a2733e00ca8481ee2fe984 - languageName: node - linkType: hard - -"is-git-repository@npm:^2.0.0": - version: 2.0.0 - resolution: "is-git-repository@npm:2.0.0" - dependencies: - execa: "npm:^4.0.3" - is-absolute: "npm:^1.0.0" - checksum: 10c0/522f41a205dab4b6bad43e69b4ea9e29612b9a2ff82281425632a4c6c7c106cf65eabaaa4967bec0b468331660cde10d88101962f913e3efa59f4fffdfb4154f - languageName: node - linkType: hard - "is-glob@npm:^2.0.0, is-glob@npm:^2.0.1": version: 2.0.1 resolution: "is-glob@npm:2.0.1" @@ -15256,15 +15227,6 @@ __metadata: languageName: node linkType: hard -"is-relative@npm:^1.0.0": - version: 1.0.0 - resolution: "is-relative@npm:1.0.0" - dependencies: - is-unc-path: "npm:^1.0.0" - checksum: 10c0/61157c4be8594dd25ac6f0ef29b1218c36667259ea26698367a4d9f39ff9018368bc365c490b3c79be92dfb1e389e43c4b865c95709e7b3bc72c5932f751fb60 - languageName: node - linkType: hard - "is-root@npm:^2.1.0": version: 2.1.0 resolution: "is-root@npm:2.1.0" @@ -15357,15 +15319,6 @@ __metadata: languageName: node linkType: hard -"is-unc-path@npm:^1.0.0": - version: 1.0.0 - resolution: "is-unc-path@npm:1.0.0" - dependencies: - unc-path-regex: "npm:^0.1.2" - checksum: 10c0/ac1b78f9b748196e3be3d0e722cd4b0f98639247a130a8f2473a58b29baf63fdb1b1c5a12c830660c5ee6ef0279c5418ca8e346f98cbe1a29e433d7ae531d42e - languageName: node - linkType: hard - "is-unicode-supported@npm:^0.1.0": version: 0.1.0 resolution: "is-unicode-supported@npm:0.1.0" @@ -15415,7 +15368,7 @@ __metadata: languageName: node linkType: hard -"is-windows@npm:^1.0.1, is-windows@npm:^1.0.2": +"is-windows@npm:^1.0.2": version: 1.0.2 resolution: "is-windows@npm:1.0.2" checksum: 10c0/b32f418ab3385604a66f1b7a3ce39d25e8881dee0bd30816dc8344ef6ff9df473a732bcc1ec4e84fe99b2f229ae474f7133e8e93f9241686cfcf7eebe53ba7a5 @@ -20476,9 +20429,9 @@ __metadata: languageName: node linkType: hard -"react-native-builder-bob@npm:^0.41.0": - version: 0.41.0 - resolution: "react-native-builder-bob@npm:0.41.0" +"react-native-builder-bob@npm:^0.42.1": + version: 0.42.1 + resolution: "react-native-builder-bob@npm:0.42.1" dependencies: "@babel/core": "npm:^7.29.0" "@babel/plugin-transform-flow-strip-types": "npm:^7.27.1" @@ -20486,6 +20439,7 @@ __metadata: "@babel/preset-env": "npm:^7.29.2" "@babel/preset-react": "npm:^7.28.5" "@babel/preset-typescript": "npm:^7.28.5" + "@jridgewell/sourcemap-codec": "npm:^1.5.5" arktype: "npm:^2.2.0" babel-plugin-syntax-hermes-parser: "npm:^0.34.0" browserslist: "npm:^4.28.2" @@ -20495,16 +20449,16 @@ __metadata: escape-string-regexp: "npm:^5.0.0" fs-extra: "npm:^11.3.4" glob: "npm:^13.0.6" - is-git-dirty: "npm:^2.0.2" json5: "npm:^2.2.3" kleur: "npm:^4.1.5" prompts: "npm:^2.4.2" react-native-monorepo-config: "npm:^0.3.3" + typescript: "npm:^6.0.3" which: "npm:^6.0.1" yargs: "npm:^18.0.0" bin: bob: bin/bob - checksum: 10c0/51add65da65b1bd1a2c1e4e07886e33bec50a927db5adcc95d9002e65cf7ddaa45f58883d5009df94058c8a4c6f5d2e17bf891a1f0977e2d6afafa988fa872e5 + checksum: 10c0/9d4eb9097eef8e204ffb1ab8c861e90c8b8b6bfc5f4cb86388d1015322fb205c3c8a08db19cd6803f2505ebba9311a7186949e2023f434d097c6e026e7fd013f languageName: node linkType: hard @@ -20548,7 +20502,7 @@ __metadata: languageName: node linkType: hard -"react-native-monorepo-config@npm:^0.3.3, react-native-monorepo-config@npm:^0.3.4": +"react-native-monorepo-config@npm:^0.3.3": version: 0.3.4 resolution: "react-native-monorepo-config@npm:0.3.4" dependencies: @@ -20558,6 +20512,16 @@ __metadata: languageName: node linkType: hard +"react-native-monorepo-config@npm:^0.4.0": + version: 0.4.0 + resolution: "react-native-monorepo-config@npm:0.4.0" + dependencies: + escape-string-regexp: "npm:^5.0.0" + fast-glob: "npm:^3.3.3" + checksum: 10c0/62e6e2ab859a2e0e5a4028b9f018043a803bfd35c62017a0ca834fc0ff389b5f9cca82c3bf5332d30644e9488e6144ebb0a94965c918d64fce5dbabe094163f8 + languageName: node + linkType: hard + "react-native-paper-example@workspace:example": version: 0.0.0-use.local resolution: "react-native-paper-example@workspace:example" @@ -20581,9 +20545,9 @@ __metadata: react: "npm:19.2.3" react-dom: "npm:19.2.3" react-native: "npm:0.85.3" - react-native-builder-bob: "npm:^0.41.0" + react-native-builder-bob: "npm:^0.42.1" react-native-gesture-handler: "npm:~2.31.1" - react-native-monorepo-config: "npm:^0.3.4" + react-native-monorepo-config: "npm:^0.4.0" react-native-reanimated: "npm:4.3.1" react-native-safe-area-context: "npm:~5.7.0" react-native-screens: "npm:4.25.1" @@ -20602,6 +20566,7 @@ __metadata: "@callstack/eslint-config": "npm:^13.0.2" "@callstack/react-theme-provider": "npm:^3.0.9" "@commitlint/config-conventional": "npm:^8.3.4" + "@jest/globals": "npm:^29.7.0" "@react-native-vector-icons/material-design-icons": "npm:^12.0.0" "@react-native/babel-preset": "npm:^0.85.3" "@react-native/jest-preset": "npm:^0.85.3" @@ -20638,7 +20603,7 @@ __metadata: react: "npm:19.2.3" react-dom: "npm:19.2.3" react-native: "npm:0.85.3" - react-native-builder-bob: "npm:^0.41.0" + react-native-builder-bob: "npm:^0.42.1" react-native-reanimated: "npm:4.3.1" react-native-safe-area-context: "npm:5.7.0" react-native-worklets: "npm:0.8.3" @@ -23951,6 +23916,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^6.0.3": + version: 6.0.3 + resolution: "typescript@npm:6.0.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/4a25ff5045b984370f48f196b3a0120779b1b343d40b9a68d114ea5e5fff099809b2bb777576991a63a5cd59cf7bffd96ff6fe10afcefbcb8bd6fb96ad4b6606 + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A5.8.3#optional!builtin": version: 5.8.3 resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" @@ -23971,6 +23946,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A^6.0.3#optional!builtin": + version: 6.0.3 + resolution: "typescript@patch:typescript@npm%3A6.0.3#optional!builtin::version=6.0.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/2f25c74e65663c248fa1ade2b8459d9ce5372ff9dad07067310f132966ebec1d93f6c42f0baf77a6b6a7a91460463f708e6887013aaade22111037457c6b25df + languageName: node + linkType: hard + "ua-parser-js@npm:^1.0.35": version: 1.0.41 resolution: "ua-parser-js@npm:1.0.41" @@ -24001,13 +23986,6 @@ __metadata: languageName: node linkType: hard -"unc-path-regex@npm:^0.1.2": - version: 0.1.2 - resolution: "unc-path-regex@npm:0.1.2" - checksum: 10c0/bf9c781c4e2f38e6613ea17a51072e4b416840fbe6eeb244597ce9b028fac2fb6cfd3dde1f14111b02c245e665dc461aab8168ecc30b14364d02caa37f812996 - languageName: node - linkType: hard - "undici-types@npm:~7.16.0": version: 7.16.0 resolution: "undici-types@npm:7.16.0" From 1c4c114dfdcf541625277bf309998b046c2f6580 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 15 Jun 2026 16:04:40 +0200 Subject: [PATCH 40/94] chore: remove unused react-navigation entry --- react-navigation/package.json | 6 ------ tsconfig.json | 3 --- 2 files changed, 9 deletions(-) delete mode 100644 react-navigation/package.json diff --git a/react-navigation/package.json b/react-navigation/package.json deleted file mode 100644 index e21ec26893..0000000000 --- a/react-navigation/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "main": "../lib/commonjs/react-navigation/index", - "module": "../lib/module/react-navigation/index", - "react-native": "../src/react-navigation/index", - "types": "../lib/typescript/react-navigation/index" -} diff --git a/tsconfig.json b/tsconfig.json index a8ae392553..9241305a80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,9 +4,6 @@ "paths": { "react-native-paper": [ "./src/index" - ], - "react-native-paper/react-navigation": [ - "./src/react-navigation" ] }, "noEmit": true, From b3af986240fe397dd033a1ef72d14896a4240805 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 15 Jun 2026 17:34:27 +0200 Subject: [PATCH 41/94] chore: setup versioned docs with docusaurus (#5001) --- .github/workflows/deploy-docs.yml | 5 + docs/component-docs-plugin/useDocs.tsx | 23 +- docs/docs/guides/07-contributing.mdx | 7 - docs/docusaurus.config.js | 34 +- docs/src/data/componentDocs5x.json | 12185 ++++++++++++++++ .../components/ActivityIndicator.mdx | 96 + .../version-5.x/components/Appbar/Appbar.mdx | 174 + .../components/Appbar/AppbarAction.mdx | 142 + .../components/Appbar/AppbarBackAction.mdx | 101 + .../components/Appbar/AppbarContent.mdx | 145 + .../components/Appbar/AppbarHeader.mdx | 125 + .../components/Avatar/AvatarIcon.mdx | 81 + .../components/Avatar/AvatarImage.mdx | 122 + .../components/Avatar/AvatarText.mdx | 97 + .../version-5.x/components/Badge.mdx | 96 + .../version-5.x/components/Banner.mdx | 172 + .../BottomNavigation/BottomNavigation.mdx | 316 + .../BottomNavigation/BottomNavigationBar.mdx | 293 + .../version-5.x/components/Button/Button.mdx | 281 + .../version-5.x/components/Card/Card.mdx | 172 + .../components/Card/CardActions.mdx | 72 + .../components/Card/CardContent.mdx | 64 + .../version-5.x/components/Card/CardCover.mdx | 63 + .../version-5.x/components/Card/CardTitle.mdx | 176 + .../components/Checkbox/Checkbox.mdx | 108 + .../components/Checkbox/CheckboxAndroid.mdx | 91 + .../components/Checkbox/CheckboxIOS.mdx | 83 + .../components/Checkbox/CheckboxItem.mdx | 196 + .../version-5.x/components/Chip/Chip.mdx | 291 + .../components/DataTable/DataTable.mdx | 127 + .../components/DataTable/DataTableCell.mdx | 109 + .../components/DataTable/DataTableHeader.mdx | 77 + .../DataTable/DataTablePagination.mdx | 211 + .../components/DataTable/DataTableRow.mdx | 90 + .../components/DataTable/DataTableTitle.mdx | 125 + .../version-5.x/components/Dialog/Dialog.mdx | 136 + .../components/Dialog/DialogActions.mdx | 80 + .../components/Dialog/DialogContent.mdx | 71 + .../components/Dialog/DialogIcon.mdx | 101 + .../components/Dialog/DialogScrollArea.mdx | 87 + .../components/Dialog/DialogTitle.mdx | 80 + .../version-5.x/components/Divider.mdx | 93 + .../components/Drawer/DrawerCollapsedItem.mdx | 149 + .../components/Drawer/DrawerItem.mdx | 155 + .../components/Drawer/DrawerSection.mdx | 110 + .../components/FAB/AnimatedFAB.mdx | 284 + .../version-5.x/components/FAB/FAB.mdx | 261 + .../version-5.x/components/FAB/FABGroup.mdx | 244 + .../components/HelperText/HelperText.mdx | 127 + .../version-5.x/components/Icon.mdx | 95 + .../components/IconButton/IconButton.mdx | 190 + .../components/List/ListAccordion.mdx | 273 + .../components/List/ListAccordionGroup.mdx | 85 + .../version-5.x/components/List/ListIcon.mdx | 79 + .../version-5.x/components/List/ListItem.mdx | 193 + .../components/List/ListSection.mdx | 90 + .../components/List/ListSubheader.mdx | 63 + .../version-5.x/components/Menu/Menu.mdx | 190 + .../version-5.x/components/Menu/MenuItem.mdx | 198 + .../version-5.x/components/Modal.mdx | 146 + .../version-5.x/components/Portal/Portal.mdx | 74 + .../components/Portal/PortalHost.mdx | 56 + .../version-5.x/components/ProgressBar.mdx | 119 + .../components/RadioButton/RadioButton.mdx | 123 + .../RadioButton/RadioButtonAndroid.mdx | 99 + .../RadioButton/RadioButtonGroup.mdx | 81 + .../components/RadioButton/RadioButtonIOS.mdx | 91 + .../RadioButton/RadioButtonItem.mdx | 210 + .../version-5.x/components/Searchbar.mdx | 256 + .../SegmentedButtons/SegmentedButtons.mdx | 107 + .../version-5.x/components/Snackbar.mdx | 226 + .../version-5.x/components/Surface.mdx | 119 + .../version-5.x/components/Switch/Switch.mdx | 95 + .../version-5.x/components/Text/Text.mdx | 101 + .../components/TextInput/TextInput.mdx | 301 + .../components/TextInput/TextInputAffix.mdx | 100 + .../components/TextInput/TextInputIcon.mdx | 107 + .../components/ToggleButton/ToggleButton.mdx | 162 + .../ToggleButton/ToggleButtonGroup.mdx | 78 + .../ToggleButton/ToggleButtonRow.mdx | 83 + .../components/Tooltip/Tooltip.mdx | 99 + .../TouchableRipple/TouchableRipple.mdx | 157 + .../version-5.x/components/_category_.json | 3 + .../version-5.x/guides/01-getting-started.md | 171 + .../version-5.x/guides/02-theming.mdx | 639 + .../version-5.x/guides/03-icons.mdx | 169 + .../version-5.x/guides/04-fonts.md | 527 + .../version-5.x/guides/05-react-native-web.md | 282 + .../guides/06-recommended-libraries.md | 40 + .../version-5.x/guides/07-contributing.mdx | 7 + .../08-theming-with-react-navigation.md | 356 + .../guides/09-bottom-navigation.md | 248 + .../version-5.x/guides/09-react-navigation.md | 281 + .../guides/10-migration-guide-to-5.0.md | 656 + .../version-5.x}/guides/11-ripple-effect.md | 0 .../version-5.x}/guides/_category_.json | 0 .../version-5.x}/showcase.mdx | 2 +- .../components/ActivityIndicator.mdx | 96 + .../version-6.x/components/Appbar/Appbar.mdx | 174 + .../components/Appbar/AppbarAction.mdx | 134 + .../components/Appbar/AppbarBackAction.mdx | 101 + .../components/Appbar/AppbarContent.mdx | 129 + .../components/Appbar/AppbarHeader.mdx | 125 + .../components/Avatar/AvatarIcon.mdx | 81 + .../components/Avatar/AvatarImage.mdx | 122 + .../components/Avatar/AvatarText.mdx | 97 + .../version-6.x/components/Badge.mdx | 96 + .../version-6.x/components/Banner.mdx | 172 + .../BottomNavigation/BottomNavigation.mdx | 308 + .../BottomNavigation/BottomNavigationBar.mdx | 285 + .../version-6.x/components/Button/Button.mdx | 273 + .../version-6.x/components/Card/Card.mdx | 172 + .../components/Card/CardActions.mdx | 72 + .../components/Card/CardContent.mdx | 64 + .../version-6.x/components/Card/CardCover.mdx | 63 + .../version-6.x/components/Card/CardTitle.mdx | 176 + .../components/Checkbox/Checkbox.mdx | 124 + .../components/Checkbox/CheckboxItem.mdx | 180 + .../version-6.x/components/Chip/Chip.mdx | 283 + .../components/DataTable/DataTable.mdx | 127 + .../components/DataTable/DataTableCell.mdx | 109 + .../components/DataTable/DataTableHeader.mdx | 77 + .../DataTable/DataTablePagination.mdx | 187 + .../components/DataTable/DataTableRow.mdx | 90 + .../components/DataTable/DataTableTitle.mdx | 125 + .../version-6.x/components/Dialog/Dialog.mdx | 136 + .../components/Dialog/DialogActions.mdx | 80 + .../components/Dialog/DialogContent.mdx | 71 + .../components/Dialog/DialogIcon.mdx | 101 + .../components/Dialog/DialogScrollArea.mdx | 87 + .../components/Dialog/DialogTitle.mdx | 80 + .../version-6.x/components/Divider.mdx | 93 + .../components/Drawer/DrawerCollapsedItem.mdx | 149 + .../components/Drawer/DrawerItem.mdx | 147 + .../components/Drawer/DrawerSection.mdx | 110 + .../version-6.x/components/FAB/FAB.mdx | 173 + .../components/FAB/FABExtended.mdx | 199 + .../version-6.x/components/FAB/FABMenu.mdx | 122 + .../version-6.x/components/Icon.mdx | 95 + .../components/IconButton/IconButton.mdx | 182 + .../components/List/ListAccordion.mdx | 265 + .../components/List/ListAccordionGroup.mdx | 85 + .../version-6.x/components/List/ListIcon.mdx | 79 + .../version-6.x/components/List/ListItem.mdx | 201 + .../components/List/ListSection.mdx | 90 + .../components/List/ListSubheader.mdx | 63 + .../version-6.x/components/Menu/Menu.mdx | 190 + .../version-6.x/components/Menu/MenuItem.mdx | 190 + .../version-6.x/components/Modal.mdx | 146 + .../version-6.x/components/Portal/Portal.mdx | 74 + .../components/Portal/PortalHost.mdx | 56 + .../version-6.x/components/ProgressBar.mdx | 119 + .../components/RadioButton/RadioButton.mdx | 123 + .../RadioButton/RadioButtonAndroid.mdx | 99 + .../RadioButton/RadioButtonGroup.mdx | 81 + .../components/RadioButton/RadioButtonIOS.mdx | 91 + .../RadioButton/RadioButtonItem.mdx | 202 + .../version-6.x/components/Searchbar.mdx | 248 + .../SegmentedButtons/SegmentedButtons.mdx | 107 + .../version-6.x/components/Snackbar.mdx | 218 + .../version-6.x/components/Surface.mdx | 119 + .../version-6.x/components/Switch/Switch.mdx | 125 + .../version-6.x/components/Text/Text.mdx | 109 + .../components/TextInput/TextInput.mdx | 177 + .../components/TextInput/TextInputIcon.mdx | 95 + .../components/ToggleButton/ToggleButton.mdx | 154 + .../ToggleButton/ToggleButtonGroup.mdx | 78 + .../ToggleButton/ToggleButtonRow.mdx | 83 + .../components/Tooltip/Tooltip.mdx | 99 + .../TouchableRipple/TouchableRipple.mdx | 165 + .../version-6.x/components/_category_.json | 3 + .../guides/01-getting-started.mdx | 0 .../version-6.x}/guides/02-theming.mdx | 2 +- .../version-6.x}/guides/03-icons.mdx | 0 .../version-6.x}/guides/04-fonts.md | 0 .../guides/05-react-native-web.md | 0 .../guides/06-recommended-libraries.md | 0 .../version-6.x/guides/07-contributing.mdx | 7 + .../08-theming-with-react-navigation.md | 2 +- .../guides/09-bottom-navigation.md | 0 .../guides/09-react-navigation.md | 0 .../version-6.x}/guides/10-rtl.md | 0 .../version-6.x/guides/11-ripple-effect.md | 45 + .../version-6.x}/guides/12-migration.md | 0 .../version-6.x/guides/_category_.json | 4 + docs/versioned_docs/version-6.x/showcase.mdx | 11 + .../version-5.x-sidebars.json | 14 + .../version-6.x-sidebars.json | 14 + docs/versions.json | 4 + scripts/generate-component-docs.ts | 168 + 190 files changed, 36403 insertions(+), 24 deletions(-) delete mode 100644 docs/docs/guides/07-contributing.mdx create mode 100644 docs/src/data/componentDocs5x.json create mode 100644 docs/versioned_docs/version-5.x/components/ActivityIndicator.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Appbar/Appbar.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Appbar/AppbarAction.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Appbar/AppbarBackAction.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Appbar/AppbarContent.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Appbar/AppbarHeader.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Avatar/AvatarIcon.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Avatar/AvatarImage.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Avatar/AvatarText.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Badge.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Banner.mdx create mode 100644 docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigation.mdx create mode 100644 docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigationBar.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Button/Button.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Card/Card.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Card/CardActions.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Card/CardContent.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Card/CardCover.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Card/CardTitle.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Checkbox/Checkbox.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Checkbox/CheckboxAndroid.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Checkbox/CheckboxIOS.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Checkbox/CheckboxItem.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Chip/Chip.mdx create mode 100644 docs/versioned_docs/version-5.x/components/DataTable/DataTable.mdx create mode 100644 docs/versioned_docs/version-5.x/components/DataTable/DataTableCell.mdx create mode 100644 docs/versioned_docs/version-5.x/components/DataTable/DataTableHeader.mdx create mode 100644 docs/versioned_docs/version-5.x/components/DataTable/DataTablePagination.mdx create mode 100644 docs/versioned_docs/version-5.x/components/DataTable/DataTableRow.mdx create mode 100644 docs/versioned_docs/version-5.x/components/DataTable/DataTableTitle.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Dialog/Dialog.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Dialog/DialogActions.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Dialog/DialogContent.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Dialog/DialogIcon.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Dialog/DialogScrollArea.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Dialog/DialogTitle.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Divider.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Drawer/DrawerCollapsedItem.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Drawer/DrawerItem.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Drawer/DrawerSection.mdx create mode 100644 docs/versioned_docs/version-5.x/components/FAB/AnimatedFAB.mdx create mode 100644 docs/versioned_docs/version-5.x/components/FAB/FAB.mdx create mode 100644 docs/versioned_docs/version-5.x/components/FAB/FABGroup.mdx create mode 100644 docs/versioned_docs/version-5.x/components/HelperText/HelperText.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Icon.mdx create mode 100644 docs/versioned_docs/version-5.x/components/IconButton/IconButton.mdx create mode 100644 docs/versioned_docs/version-5.x/components/List/ListAccordion.mdx create mode 100644 docs/versioned_docs/version-5.x/components/List/ListAccordionGroup.mdx create mode 100644 docs/versioned_docs/version-5.x/components/List/ListIcon.mdx create mode 100644 docs/versioned_docs/version-5.x/components/List/ListItem.mdx create mode 100644 docs/versioned_docs/version-5.x/components/List/ListSection.mdx create mode 100644 docs/versioned_docs/version-5.x/components/List/ListSubheader.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Menu/Menu.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Menu/MenuItem.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Modal.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Portal/Portal.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Portal/PortalHost.mdx create mode 100644 docs/versioned_docs/version-5.x/components/ProgressBar.mdx create mode 100644 docs/versioned_docs/version-5.x/components/RadioButton/RadioButton.mdx create mode 100644 docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonAndroid.mdx create mode 100644 docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonGroup.mdx create mode 100644 docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonIOS.mdx create mode 100644 docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonItem.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Searchbar.mdx create mode 100644 docs/versioned_docs/version-5.x/components/SegmentedButtons/SegmentedButtons.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Snackbar.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Surface.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Switch/Switch.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Text/Text.mdx create mode 100644 docs/versioned_docs/version-5.x/components/TextInput/TextInput.mdx create mode 100644 docs/versioned_docs/version-5.x/components/TextInput/TextInputAffix.mdx create mode 100644 docs/versioned_docs/version-5.x/components/TextInput/TextInputIcon.mdx create mode 100644 docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButton.mdx create mode 100644 docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonGroup.mdx create mode 100644 docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonRow.mdx create mode 100644 docs/versioned_docs/version-5.x/components/Tooltip/Tooltip.mdx create mode 100644 docs/versioned_docs/version-5.x/components/TouchableRipple/TouchableRipple.mdx create mode 100644 docs/versioned_docs/version-5.x/components/_category_.json create mode 100644 docs/versioned_docs/version-5.x/guides/01-getting-started.md create mode 100644 docs/versioned_docs/version-5.x/guides/02-theming.mdx create mode 100644 docs/versioned_docs/version-5.x/guides/03-icons.mdx create mode 100644 docs/versioned_docs/version-5.x/guides/04-fonts.md create mode 100644 docs/versioned_docs/version-5.x/guides/05-react-native-web.md create mode 100644 docs/versioned_docs/version-5.x/guides/06-recommended-libraries.md create mode 100644 docs/versioned_docs/version-5.x/guides/07-contributing.mdx create mode 100644 docs/versioned_docs/version-5.x/guides/08-theming-with-react-navigation.md create mode 100644 docs/versioned_docs/version-5.x/guides/09-bottom-navigation.md create mode 100644 docs/versioned_docs/version-5.x/guides/09-react-navigation.md create mode 100644 docs/versioned_docs/version-5.x/guides/10-migration-guide-to-5.0.md rename docs/{docs => versioned_docs/version-5.x}/guides/11-ripple-effect.md (100%) rename docs/{docs => versioned_docs/version-5.x}/guides/_category_.json (100%) rename docs/{docs => versioned_docs/version-5.x}/showcase.mdx (78%) create mode 100644 docs/versioned_docs/version-6.x/components/ActivityIndicator.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Appbar/Appbar.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Appbar/AppbarAction.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Appbar/AppbarBackAction.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Appbar/AppbarContent.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Appbar/AppbarHeader.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Avatar/AvatarIcon.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Avatar/AvatarImage.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Avatar/AvatarText.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Badge.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Banner.mdx create mode 100644 docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigation.mdx create mode 100644 docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigationBar.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Button/Button.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Card/Card.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Card/CardActions.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Card/CardContent.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Card/CardCover.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Card/CardTitle.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Checkbox/Checkbox.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Checkbox/CheckboxItem.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Chip/Chip.mdx create mode 100644 docs/versioned_docs/version-6.x/components/DataTable/DataTable.mdx create mode 100644 docs/versioned_docs/version-6.x/components/DataTable/DataTableCell.mdx create mode 100644 docs/versioned_docs/version-6.x/components/DataTable/DataTableHeader.mdx create mode 100644 docs/versioned_docs/version-6.x/components/DataTable/DataTablePagination.mdx create mode 100644 docs/versioned_docs/version-6.x/components/DataTable/DataTableRow.mdx create mode 100644 docs/versioned_docs/version-6.x/components/DataTable/DataTableTitle.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Dialog/Dialog.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Dialog/DialogActions.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Dialog/DialogContent.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Dialog/DialogIcon.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Dialog/DialogScrollArea.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Dialog/DialogTitle.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Divider.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Drawer/DrawerCollapsedItem.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Drawer/DrawerItem.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Drawer/DrawerSection.mdx create mode 100644 docs/versioned_docs/version-6.x/components/FAB/FAB.mdx create mode 100644 docs/versioned_docs/version-6.x/components/FAB/FABExtended.mdx create mode 100644 docs/versioned_docs/version-6.x/components/FAB/FABMenu.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Icon.mdx create mode 100644 docs/versioned_docs/version-6.x/components/IconButton/IconButton.mdx create mode 100644 docs/versioned_docs/version-6.x/components/List/ListAccordion.mdx create mode 100644 docs/versioned_docs/version-6.x/components/List/ListAccordionGroup.mdx create mode 100644 docs/versioned_docs/version-6.x/components/List/ListIcon.mdx create mode 100644 docs/versioned_docs/version-6.x/components/List/ListItem.mdx create mode 100644 docs/versioned_docs/version-6.x/components/List/ListSection.mdx create mode 100644 docs/versioned_docs/version-6.x/components/List/ListSubheader.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Menu/Menu.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Menu/MenuItem.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Modal.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Portal/Portal.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Portal/PortalHost.mdx create mode 100644 docs/versioned_docs/version-6.x/components/ProgressBar.mdx create mode 100644 docs/versioned_docs/version-6.x/components/RadioButton/RadioButton.mdx create mode 100644 docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonAndroid.mdx create mode 100644 docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonGroup.mdx create mode 100644 docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonIOS.mdx create mode 100644 docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonItem.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Searchbar.mdx create mode 100644 docs/versioned_docs/version-6.x/components/SegmentedButtons/SegmentedButtons.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Snackbar.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Surface.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Switch/Switch.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Text/Text.mdx create mode 100644 docs/versioned_docs/version-6.x/components/TextInput/TextInput.mdx create mode 100644 docs/versioned_docs/version-6.x/components/TextInput/TextInputIcon.mdx create mode 100644 docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButton.mdx create mode 100644 docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonGroup.mdx create mode 100644 docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonRow.mdx create mode 100644 docs/versioned_docs/version-6.x/components/Tooltip/Tooltip.mdx create mode 100644 docs/versioned_docs/version-6.x/components/TouchableRipple/TouchableRipple.mdx create mode 100644 docs/versioned_docs/version-6.x/components/_category_.json rename docs/{docs => versioned_docs/version-6.x}/guides/01-getting-started.mdx (100%) rename docs/{docs => versioned_docs/version-6.x}/guides/02-theming.mdx (99%) rename docs/{docs => versioned_docs/version-6.x}/guides/03-icons.mdx (100%) rename docs/{docs => versioned_docs/version-6.x}/guides/04-fonts.md (100%) rename docs/{docs => versioned_docs/version-6.x}/guides/05-react-native-web.md (100%) rename docs/{docs => versioned_docs/version-6.x}/guides/06-recommended-libraries.md (100%) create mode 100644 docs/versioned_docs/version-6.x/guides/07-contributing.mdx rename docs/{docs => versioned_docs/version-6.x}/guides/08-theming-with-react-navigation.md (99%) rename docs/{docs => versioned_docs/version-6.x}/guides/09-bottom-navigation.md (100%) rename docs/{docs => versioned_docs/version-6.x}/guides/09-react-navigation.md (100%) rename docs/{docs => versioned_docs/version-6.x}/guides/10-rtl.md (100%) create mode 100644 docs/versioned_docs/version-6.x/guides/11-ripple-effect.md rename docs/{docs => versioned_docs/version-6.x}/guides/12-migration.md (100%) create mode 100644 docs/versioned_docs/version-6.x/guides/_category_.json create mode 100644 docs/versioned_docs/version-6.x/showcase.mdx create mode 100644 docs/versioned_sidebars/version-5.x-sidebars.json create mode 100644 docs/versioned_sidebars/version-6.x-sidebars.json create mode 100644 docs/versions.json create mode 100644 scripts/generate-component-docs.ts diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index d6eb089477..4aa9b15e2d 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -24,10 +24,15 @@ jobs: steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 - name: Setup uses: ./.github/actions/setup + - name: Generate 5.x component docs + run: node scripts/generate-component-docs.ts origin/5.0 docs/src/data/componentDocs5x.json + - name: Build docs run: yarn --cwd docs build diff --git a/docs/component-docs-plugin/useDocs.tsx b/docs/component-docs-plugin/useDocs.tsx index 9c6fcda0f8..5747fafb5c 100644 --- a/docs/component-docs-plugin/useDocs.tsx +++ b/docs/component-docs-plugin/useDocs.tsx @@ -1,7 +1,10 @@ +import { useActiveDocContext } from '@docusaurus/plugin-content-docs/client'; // @ts-ignore // eslint-disable-next-line import/no-unresolved import { usePluginData } from '@docusaurus/useGlobalData'; +import componentDocs5x from '../src/data/componentDocs5x.json'; + const pluginName = 'component-docs-plugin'; export interface ComponentDocsPluginData { @@ -27,19 +30,29 @@ export interface Data { export interface Prop { required: boolean; - tsType: { + tsType?: { name: string; raw?: string; - }; + } | null; description: string; - defaultValue: { + defaultValue?: { value: string; - }; + } | null; } +const versionedDocs: { [versionName: string]: ComponentDocsPluginData } = { + '5.x': componentDocs5x, +}; + function useDoc(withPath: string) { + const activeDocContext = useActiveDocContext(undefined); const pluginData = usePluginData(pluginName) as ComponentDocsPluginData; - return pluginData?.docs?.[withPath]; + const versionName = activeDocContext.activeVersion?.name; + const versionedDoc = versionName + ? versionedDocs[versionName]?.docs?.[withPath] + : undefined; + + return versionedDoc ?? pluginData?.docs?.[withPath]; } export default useDoc; diff --git a/docs/docs/guides/07-contributing.mdx b/docs/docs/guides/07-contributing.mdx deleted file mode 100644 index 211e8f4b8c..0000000000 --- a/docs/docs/guides/07-contributing.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Contributing ---- - -import Contributing from '../../../CONTRIBUTING.md'; - - diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index a4b672640c..ee54135042 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -60,7 +60,12 @@ const config = { [ './component-docs-plugin', { - docsRootDir: path.join(__dirname, 'docs', 'components'), + docsRootDir: path.join( + __dirname, + 'versioned_docs', + 'version-6.x', + 'components' + ), libsRootDir: path.join(__dirname, '..', 'src', 'components'), pages: { ActivityIndicator: 'ActivityIndicator', @@ -195,12 +200,24 @@ const config = { ({ docs: { sidebarPath: require.resolve('./sidebars.js'), + includeCurrentVersion: false, + lastVersion: '5.x', + versions: { + '6.x': { + label: '6.x', + banner: 'unreleased', + }, + '5.x': { + label: '5.x', + banner: 'none', + }, + }, editUrl: (params) => { const urlToMain = 'https://github.com/callstack/react-native-paper/tree/main'; if (params.docPath.includes('guides')) { - return `${urlToMain}/docs/docs/${params.docPath}`; + return `${urlToMain}/docs/${params.versionDocsDirPath}/${params.docPath}`; } const customUrls = { @@ -260,24 +277,23 @@ const config = { label: 'Showcase', }, { - type: 'dropdown', - label: 'v5.x', + type: 'docsVersionDropdown', position: 'right', - items: [ + dropdownItemsAfter: [ { - label: 'v4.x', + label: '4.x', href: `${publicUrl}4.0/`, }, { - label: 'v3.x', + label: '3.x', href: `${publicUrl}3.0/`, }, { - label: 'v2.x', + label: '2.x', href: `${publicUrl}2.0/`, }, { - label: 'v1.x', + label: '1.x', href: `${publicUrl}1.0/`, }, ], diff --git a/docs/src/data/componentDocs5x.json b/docs/src/data/componentDocs5x.json new file mode 100644 index 0000000000..5aa299568c --- /dev/null +++ b/docs/src/data/componentDocs5x.json @@ -0,0 +1,12185 @@ +{ + "docs": { + "ActivityIndicator": { + "filepath": "ActivityIndicator.tsx", + "title": "ActivityIndicator", + "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, MD2Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "activity-indicator", + "data": { + "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, MD2Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "ActivityIndicator", + "methods": [], + "statics": [], + "props": { + "animating": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show the indicator or hide it.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "The color of the spinner." + }, + "size": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'large' | number", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "number" + } + ] + }, + "description": "Size of the indicator.", + "defaultValue": { + "value": "'small'", + "computed": false + } + }, + "hidesWhenStopped": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the indicator should hide when not animating.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ActivityIndicator.tsx" + ] + }, + "Appbar/Appbar": { + "filepath": "Appbar/Appbar.tsx", + "title": "Appbar", + "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", + "link": "appbar", + "data": { + "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "Appbar", + "methods": [], + "statics": [], + "props": { + "dark": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the background color is a dark color. A dark appbar will render light text and vice-versa." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Appbar`." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'medium' | 'large' | 'center-aligned'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "literal", + "value": "'center-aligned'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", + "defaultValue": { + "value": "'small'", + "computed": false + } + }, + "elevated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment." + }, + "safeAreaInsets": { + "required": false, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n bottom?: number;\n top?: number;\n left?: number;\n right?: number;\n}", + "signature": { + "properties": [ + { + "key": "bottom", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "top", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "left", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "right", + "value": { + "name": "number", + "required": false + } + } + ] + } + }, + "description": "Safe area insets for the Appbar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/Appbar.tsx" + ] + }, + "Appbar/AppbarAction": { + "filepath": "Appbar/AppbarAction.tsx", + "title": "Appbar.Action", + "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", + "link": "appbar-action", + "data": { + "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Appbar.Action", + "methods": [], + "statics": [], + "props": { + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for action icon." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Name of the icon to show." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Optional icon size.", + "defaultValue": { + "value": "24", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "isLeading": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n\nWhether it's the leading button. Note: If `Appbar.BackAction` is present, it will be rendered before any `isLeading` icons." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarAction.tsx" + ], + "group": "Appbar" + }, + "Appbar/AppbarBackAction": { + "filepath": "Appbar/AppbarBackAction.tsx", + "title": "Appbar.BackAction", + "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "link": "appbar-back-action", + "data": { + "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Appbar.BackAction", + "methods": [], + "statics": [], + "props": { + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for back icon." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Optional icon size." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'Back'", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarBackAction.tsx" + ], + "group": "Appbar" + }, + "Appbar/AppbarContent": { + "filepath": "Appbar/AppbarContent.tsx", + "title": "Appbar.Content", + "description": "A component used to display a title and optional subtitle in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "appbar-content", + "data": { + "description": "A component used to display a title and optional subtitle in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Appbar.Content", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text or component for the title." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the title, if `title` is a string." + }, + "titleRef": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "TextRef" + } + ] + }, + "description": "Reference for the title." + }, + "subtitle": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "@deprecated Deprecated in v5.x\nText for the subtitle." + }, + "subtitleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "@deprecated Deprecated in v5.x\nStyle for the subtitle." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "If true, disable all interactions for this component." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the text." + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'medium' | 'large' | 'center-aligned'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "literal", + "value": "'center-aligned'" + } + ] + }, + "description": "@internal", + "defaultValue": { + "value": "'small'", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'appbar-content'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarContent.tsx" + ], + "group": "Appbar" + }, + "Appbar/AppbarHeader": { + "filepath": "Appbar/AppbarHeader.tsx", + "title": "Appbar.Header", + "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "appbar-header", + "data": { + "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Appbar.Header", + "methods": [], + "statics": [], + "props": { + "dark": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the background color is a dark color. A dark header will render light text and vice-versa." + }, + "statusBarHeight": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Extra padding to add at the top of header to account for translucent status bar.\nThis is automatically handled on iOS >= 11 including iPhone X using `SafeAreaView`.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to disable the default behaviour, and customize the height." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the header." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'medium' | 'large' | 'center-aligned'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "literal", + "value": "'center-aligned'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", + "defaultValue": { + "value": "Platform.OS === 'ios' ? 'center-aligned' : 'small'", + "computed": false + } + }, + "elevated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "testID": { + "defaultValue": { + "value": "'appbar-header'", + "computed": false + }, + "required": false, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarHeader.tsx" + ], + "group": "Appbar" + }, + "Avatar/AvatarIcon": { + "filepath": "Avatar/AvatarIcon.tsx", + "title": "Avatar.Icon", + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "link": "avatar-icon", + "data": { + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "displayName": "Avatar.Icon", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Avatar`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the avatar.", + "defaultValue": { + "value": "64", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the icon." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Avatar/AvatarIcon.tsx" + ], + "group": "Avatar" + }, + "Avatar/AvatarImage": { + "filepath": "Avatar/AvatarImage.tsx", + "title": "Avatar.Image", + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", + "link": "avatar-image", + "data": { + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", + "displayName": "Avatar.Image", + "methods": [], + "statics": [], + "props": { + "source": { + "required": true, + "tsType": { + "name": "union", + "raw": "| ImageSourcePropType\n| ((props: { size: number }) => React.ReactNode)", + "elements": [ + { + "name": "ImageSourcePropType" + }, + { + "name": "unknown" + } + ] + }, + "description": "Image to display for the `Avatar`.\nIt accepts a standard React Native Image `source` prop\nOr a function that returns an `Image`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the avatar.", + "defaultValue": { + "value": "64", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "onError": { + "required": false, + "tsType": { + "name": "ImageProps['onError']", + "raw": "ImageProps['onError']" + }, + "description": "Invoked on load error." + }, + "onLayout": { + "required": false, + "tsType": { + "name": "ImageProps['onLayout']", + "raw": "ImageProps['onLayout']" + }, + "description": "Invoked on mount and on layout changes." + }, + "onLoad": { + "required": false, + "tsType": { + "name": "ImageProps['onLoad']", + "raw": "ImageProps['onLoad']" + }, + "description": "Invoked when load completes successfully." + }, + "onLoadEnd": { + "required": false, + "tsType": { + "name": "ImageProps['onLoadEnd']", + "raw": "ImageProps['onLoadEnd']" + }, + "description": "Invoked when load either succeeds or fails." + }, + "onLoadStart": { + "required": false, + "tsType": { + "name": "ImageProps['onLoadStart']", + "raw": "ImageProps['onLoadStart']" + }, + "description": "Invoked on load start." + }, + "onProgress": { + "required": false, + "tsType": { + "name": "ImageProps['onProgress']", + "raw": "ImageProps['onProgress']" + }, + "description": "Invoked on download progress." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Avatar/AvatarImage.tsx" + ], + "group": "Avatar" + }, + "Avatar/AvatarText": { + "filepath": "Avatar/AvatarText.tsx", + "title": "Avatar.Text", + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "link": "avatar-text", + "data": { + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "displayName": "Avatar.Text", + "methods": [], + "statics": [], + "props": { + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Initials to show as the text in the `Avatar`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the avatar.", + "defaultValue": { + "value": "64", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the text." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for text container" + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the title." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Avatar/AvatarText.tsx" + ], + "group": "Avatar" + }, + "Badge": { + "filepath": "Badge.tsx", + "title": "Badge", + "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", + "link": "badge", + "data": { + "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", + "displayName": "Badge", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the badge is visible", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "children": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + }, + "description": "Content of the `Badge`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the `Badge`.", + "defaultValue": { + "value": "20", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + {} + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Badge.tsx" + ] + }, + "Banner": { + "filepath": "Banner.tsx", + "title": "Banner", + "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "banner", + "data": { + "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Banner", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether banner is currently visible." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content that will be displayed inside banner." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Banner`. Can be an image." + }, + "actions": { + "required": false, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "intersection", + "raw": "{\n label: string;\n} & $RemoveChildren", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n label: string;\n}", + "signature": { + "properties": [ + { + "key": "label", + "value": { + "name": "string", + "required": true + } + } + ] + } + }, + { + "name": "$RemoveChildren", + "elements": [ + { + "name": "Button" + } + ], + "raw": "$RemoveChildren" + } + ] + } + ], + "raw": "Array<\n {\n label: string;\n } & $RemoveChildren\n>" + }, + "description": "Action items to shown in the banner.\nAn action item should contain the following properties:\n\n- `label`: label of the action button (required)\n- `onPress`: callback that is called when button is pressed (required)\n\nTo customize button you can pass other props that button component takes.", + "defaultValue": { + "value": "[]", + "computed": false + } + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of banner's inner content.\nUse this prop to apply custom width for wide layouts." + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges Banner shadow and background on iOS and Android.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "onShowAnimationFinished": { + "required": false, + "tsType": { + "name": "Animated.EndCallback" + }, + "description": "\nOptional callback that will be called after the opening animation finished running normally", + "defaultValue": { + "value": "() => {}", + "computed": false + } + }, + "onHideAnimationFinished": { + "required": false, + "tsType": { + "name": "Animated.EndCallback" + }, + "description": "\nOptional callback that will be called after the closing animation finished running normally", + "defaultValue": { + "value": "() => {}", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Banner.tsx" + ] + }, + "BottomNavigation/BottomNavigation": { + "filepath": "BottomNavigation/BottomNavigation.tsx", + "title": "BottomNavigation", + "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "bottom-navigation", + "data": { + "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "BottomNavigation", + "methods": [ + { + "name": "SceneMap", + "docblock": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance.", + "modifiers": [ + "static" + ], + "params": [ + { + "name": "scenes", + "optional": false, + "type": { + "name": "signature", + "type": "object", + "raw": "{\n [key: string]: React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n }>;\n}", + "signature": { + "properties": [ + { + "key": { + "name": "string" + }, + "value": { + "name": "ReactComponentType", + "raw": "React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n}>", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "jumpTo", + "value": { + "name": "signature", + "type": "function", + "raw": "(key: string) => void", + "signature": { + "arguments": [ + { + "name": "key", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + } + ] + } + } + ], + "required": true + } + } + ] + } + } + } + ], + "returns": null, + "description": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance." + } + ], + "statics": [], + "props": { + "shifting": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." + }, + "labeled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether tabs should be spread across the entire width." + }, + "navigationState": { + "required": true, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n index: number;\n routes: Route[];\n}", + "signature": { + "properties": [ + { + "key": "index", + "value": { + "name": "number", + "required": true + } + }, + { + "key": "routes", + "value": { + "name": "Array", + "elements": [ + { + "name": "Route" + } + ], + "raw": "Route[]", + "required": true + } + } + ] + } + }, + "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `color`: color to use as background color for shifting bottom navigation @deprecatedProperty In v5.x works only with theme version 2.\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation` is a controlled component, which means the `index` needs to be updated via the `onIndexChange` callback." + }, + "onIndexChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(index: number) => void", + "signature": { + "arguments": [ + { + "name": "index", + "type": { + "name": "number" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback which is called on tab change, receives the index of the new tab as argument.\nThe navigation state needs to be updated when it's called, otherwise the change is dropped." + }, + "renderScene": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n jumpTo: (key: string) => void;\n}) => React.ReactNode | null", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "jumpTo", + "value": { + "name": "signature", + "type": "function", + "raw": "(key: string) => void", + "signature": { + "arguments": [ + { + "name": "key", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "React.ReactNode | null", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "null" + } + ] + } + } + }, + "description": "Callback which returns a react element to render as the page for the tab. Receives an object containing the route as the argument:\n\n```js\nrenderScene = ({ route, jumpTo }) => {\n switch (route.key) {\n case 'music':\n return ;\n case 'albums':\n return ;\n }\n}\n```\n\nPages are lazily rendered, which means that a page will be rendered the first time you navigate to it.\nAfter initial render, all the pages stay rendered to preserve their state.\n\nYou need to make sure that your individual routes implement a `shouldComponentUpdate` to improve the performance.\nTo make it easier to specify the components, you can use the `SceneMap` helper:\n\n```js\nrenderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n});\n```\n\nSpecifying the components this way is easier and takes care of implementing a `shouldComponentUpdate` method.\nEach component will receive the current route and a `jumpTo` method as it's props.\nThe `jumpTo` method can be used to navigate to other tabs programmatically:\n\n```js\nthis.props.jumpTo('albums')\n```" + }, + "renderIcon": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React Element to be used as tab icon." + }, + "renderLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which React Element to be used as tab label." + }, + "renderTouchable": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: TouchableProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "elements": [ + { + "name": "TouchableRippleProps" + }, + { + "name": "signature", + "type": "object", + "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "key", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "children", + "value": { + "name": "ReactReactNode", + "raw": "React.ReactNode", + "required": true + } + }, + { + "key": "borderless", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "centered", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "rippleColor", + "value": { + "name": "ColorValue", + "required": false + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS." + }, + "getAccessibilityLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default." + }, + "getBadge": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => boolean | number | string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "boolean | number | string | undefined", + "elements": [ + { + "name": "boolean" + }, + { + "name": "number" + }, + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get badge for the tab, uses `route.badge` by default." + }, + "getColor": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get color for the tab, uses `route.color` by default." + }, + "getLabelText": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component." + }, + "getLazy": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => boolean | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "boolean | undefined", + "elements": [ + { + "name": "boolean" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get lazy for the current screen. Uses true by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.lazy", + "computed": false + } + }, + "getTestID": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get the id to locate this tab button in tests, uses `route.testID` by default." + }, + "onTabPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab press. It receives the route for the pressed tab, useful for things like scroll to top." + }, + "onTabLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab long press. It receives the route for the pressed tab, useful for things like custom action when longed pressed." + }, + "activeColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the active tab." + }, + "inactiveColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the inactive tab." + }, + "sceneAnimationEnabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether animation is enabled for scenes transitions in `shifting` mode.\nBy default, the scenes cross-fade during tab change when `shifting` is enabled.\nSpecify `sceneAnimationEnabled` as `false` to disable the animation.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "sceneAnimationType": { + "required": false, + "tsType": { + "name": "union", + "raw": "'opacity' | 'shifting'", + "elements": [ + { + "name": "literal", + "value": "'opacity'" + }, + { + "name": "literal", + "value": "'shifting'" + } + ] + }, + "description": "The scene animation effect. Specify `'shifting'` for a different effect.\nBy default, 'opacity' will be used.", + "defaultValue": { + "value": "'opacity'", + "computed": false + } + }, + "sceneAnimationEasing": { + "required": false, + "tsType": { + "name": "union", + "raw": "EasingFunction | undefined", + "elements": [ + { + "name": "EasingFunction" + }, + { + "name": "undefined" + } + ] + }, + "description": "The scene animation Easing." + }, + "keyboardHidesNavigationBar": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", + "defaultValue": { + "value": "Platform.OS === 'android'", + "computed": false + } + }, + "safeAreaInsets": { + "required": false, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", + "signature": { + "properties": [ + { + "key": "top", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "right", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "bottom", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "left", + "value": { + "name": "number", + "required": false + } + } + ] + } + }, + "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." + }, + "barStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style for the bottom navigation bar. You can pass a custom background color here:\n\n```js\nbarStyle={{ backgroundColor: '#694fad' }}\n```" + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "activeIndicatorStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'bottom-navigation'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/BottomNavigation/BottomNavigation.tsx" + ] + }, + "BottomNavigation/BottomNavigationBar": { + "filepath": "BottomNavigation/BottomNavigationBar.tsx", + "title": "BottomNavigation.Bar", + "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", + "link": "bottom-navigation-bar", + "data": { + "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", + "displayName": "BottomNavigation.Bar", + "methods": [], + "statics": [], + "props": { + "shifting": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." + }, + "labeled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether tabs should be spread across the entire width." + }, + "navigationState": { + "required": true, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n index: number;\n routes: Route[];\n}", + "signature": { + "properties": [ + { + "key": "index", + "value": { + "name": "number", + "required": true + } + }, + { + "key": "routes", + "value": { + "name": "Array", + "elements": [ + { + "name": "Route" + } + ], + "raw": "Route[]", + "required": true + } + } + ] + } + }, + "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `color`: color to use as background color for shifting bottom navigation @deprecatedProperty In v5.x works only with theme version 2.\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation.Bar` is a controlled component, which means the `index` needs to be updated via the `onTabPress` callback." + }, + "renderIcon": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React Element to be used as tab icon." + }, + "renderLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which React Element to be used as tab label." + }, + "renderTouchable": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: TouchableProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "elements": [ + { + "name": "TouchableRippleProps" + }, + { + "name": "signature", + "type": "object", + "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "key", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "children", + "value": { + "name": "ReactReactNode", + "raw": "React.ReactNode", + "required": true + } + }, + { + "key": "borderless", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "centered", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "rippleColor", + "value": { + "name": "ColorValue", + "required": false + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS.", + "defaultValue": { + "value": "({ key, ...props }: TouchableProps) => (\n \n)", + "computed": false + } + }, + "getAccessibilityLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) =>\nroute.accessibilityLabel", + "computed": false + } + }, + "getBadge": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => boolean | number | string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "boolean | number | string | undefined", + "elements": [ + { + "name": "boolean" + }, + { + "name": "number" + }, + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get badge for the tab, uses `route.badge` by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.badge", + "computed": false + } + }, + "getColor": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get color for the tab, uses `route.color` by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.color", + "computed": false + } + }, + "getLabelText": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.title", + "computed": false + } + }, + "getTestID": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get the id to locate this tab button in tests, uses `route.testID` by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.testID", + "computed": false + } + }, + "onTabPress": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab press. It receives the route for the pressed tab. Use this to update the navigation state." + }, + "onTabLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab long press. It receives the route for the pressed tab" + }, + "activeColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the active tab." + }, + "inactiveColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the inactive tab." + }, + "animationEasing": { + "required": false, + "tsType": { + "name": "union", + "raw": "EasingFunction | undefined", + "elements": [ + { + "name": "EasingFunction" + }, + { + "name": "undefined" + } + ] + }, + "description": "The scene animation Easing." + }, + "keyboardHidesNavigationBar": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", + "defaultValue": { + "value": "Platform.OS === 'android'", + "computed": false + } + }, + "safeAreaInsets": { + "required": false, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", + "signature": { + "properties": [ + { + "key": "top", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "right", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "bottom", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "left", + "value": { + "name": "number", + "required": false + } + } + ] + } + }, + "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "activeIndicatorStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'bottom-navigation-bar'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/BottomNavigation/BottomNavigationBar.tsx" + ], + "group": "BottomNavigation" + }, + "Button/Button": { + "filepath": "Button/Button.tsx", + "title": "Button", + "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "button", + "data": { + "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Button", + "methods": [], + "statics": [], + "props": { + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal'", + "elements": [ + { + "name": "literal", + "value": "'text'" + }, + { + "name": "literal", + "value": "'outlined'" + }, + { + "name": "literal", + "value": "'contained'" + }, + { + "name": "literal", + "value": "'elevated'" + }, + { + "name": "literal", + "value": "'contained-tonal'" + } + ] + }, + "description": "Mode of the button. You can change the mode to adjust the styling to give it desired emphasis.\n- `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options.\n- `outlined` - button with an outline without background, typically used for important, but not primary action – represents medium emphasis.\n- `contained` - button with a background color, used for important action, have the most visual impact and high emphasis.\n- `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background. @supported Available in v5.x with theme version 3\n- `contained-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. @supported Available in v5.x with theme version 3", + "defaultValue": { + "value": "'text'", + "computed": false + } + }, + "dark": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the color is a dark color. A dark button will render light text and vice-versa. Only applicable for:\n * `contained` mode for theme version 2\n * `contained`, `contained-tonal` and `elevated` modes for theme version 3." + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Use a compact look, useful for `text` buttons in a row." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "@deprecated Deprecated in v5.x - use `buttonColor` or `textColor` instead.\nCustom text color for flat button, or background color for contained button." + }, + "buttonColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom button's background color." + }, + "textColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom button's text color." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "loading": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show a loading indicator." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Button`." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Label text of the button." + }, + "uppercase": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Make the label text uppercased. Note that this won't work if you pass React elements as children." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "accessibilityHint": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility hint for the button. This is read by the screen reader when the user taps the button." + }, + "accessibilityRole": { + "required": false, + "tsType": { + "name": "AccessibilityRole" + }, + "description": "Accessibility role for the button. The \"button\" role is set by default.", + "defaultValue": { + "value": "'button'", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touch is released even before onPress." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of button's inner content.\nUse this prop to apply custom height and width, to set a custom padding or to set the icon on the right with `flexDirection: 'row-reverse'`." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the button text." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "touchableRef": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "Reference for the touchable" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'button'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Button/Button.tsx" + ] + }, + "Card/Card": { + "filepath": "Card/Card.tsx", + "title": "Card", + "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "card", + "data": { + "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Card", + "methods": [], + "statics": [], + "props": { + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'elevated' | 'outlined' | 'contained'", + "elements": [ + { + "name": "literal", + "value": "'elevated'" + }, + { + "name": "literal", + "value": "'outlined'" + }, + { + "name": "literal", + "value": "'contained'" + } + ] + }, + "description": "Mode of the Card.\n- `elevated` - Card with elevation.\n- `contained` - Card without outline and elevation @supported Available in v5.x with theme version 3\n- `outlined` - Card with an outline.", + "defaultValue": { + "value": "'elevated'", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Card`." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touch is released even before onPress." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "If true, disable all interactions for this component." + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "Changes Card shadow and background on iOS and Android.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of card's inner content." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Pass down testID from card props to touchable", + "defaultValue": { + "value": "'card'", + "computed": false + } + }, + "accessible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Pass down accessible from card props to touchable" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/Card.tsx" + ] + }, + "Card/CardActions": { + "filepath": "Card/CardActions.tsx", + "title": "Card.Actions", + "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "card-actions", + "data": { + "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Card.Actions", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Items inside the `CardActions`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardActions.tsx" + ], + "group": "Card" + }, + "Card/CardContent": { + "filepath": "Card/CardContent.tsx", + "title": "Card.Content", + "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", + "link": "card-content", + "data": { + "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Card.Content", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Items inside the `Card.Content`." + }, + "index": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "total": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "siblings": { + "required": false, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "string" + } + ], + "raw": "Array" + }, + "description": "@internal" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardContent.tsx" + ], + "group": "Card" + }, + "Card/CardCover": { + "filepath": "Card/CardCover.tsx", + "title": "Card.Cover", + "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", + "link": "card-cover", + "data": { + "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", + "displayName": "Card.Cover", + "methods": [], + "statics": [], + "props": { + "index": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "total": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardCover.tsx" + ], + "group": "Card" + }, + "Card/CardTitle": { + "filepath": "Card/CardTitle.tsx", + "title": "Card.Title", + "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", + "link": "card-title", + "data": { + "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", + "displayName": "Card.Title", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text for the title. Note that this will only accept a string or ``-based node." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the title." + }, + "titleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Number of lines for the title.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "titleVariant": { + "required": false, + "tsType": { + "name": "unknown" + }, + "description": "@supported Available in v5.x with theme version 3\n\nTitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyLarge'", + "computed": false + } + }, + "subtitle": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text for the subtitle. Note that this will only accept a string or ``-based node." + }, + "subtitleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the subtitle." + }, + "subtitleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Number of lines for the subtitle.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "subtitleVariant": { + "required": false, + "tsType": { + "name": "unknown" + }, + "description": "@supported Available in v5.x with theme version 3\n\nSubtitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyMedium'", + "computed": false + } + }, + "left": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { size: number }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ size: number }", + "signature": { + "properties": [ + { + "key": "size", + "value": { + "name": "number", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the left side." + }, + "leftStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the left element wrapper." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { size: number }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ size: number }", + "signature": { + "properties": [ + { + "key": "size", + "value": { + "name": "number", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side." + }, + "rightStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the right element wrapper." + }, + "index": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "total": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "subtitleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a subtitle font can reach." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardTitle.tsx" + ], + "group": "Card" + }, + "Checkbox/Checkbox": { + "filepath": "Checkbox/Checkbox.tsx", + "title": "Checkbox", + "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", + "link": "checkbox", + "data": { + "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Checkbox", + "methods": [], + "statics": [], + "props": { + "status": { + "required": true, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked' | 'indeterminate'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + }, + { + "name": "literal", + "value": "'indeterminate'" + } + ] + }, + "description": "Status of checkbox." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether checkbox is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked checkbox." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for checkbox." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Checkbox/Checkbox.tsx" + ] + }, + "Checkbox/CheckboxAndroid": { + "filepath": "Checkbox/CheckboxAndroid.tsx", + "title": "Checkbox.Android", + "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "checkbox-android", + "data": { + "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "Checkbox.Android", + "methods": [], + "statics": [], + "props": { + "status": { + "required": true, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked' | 'indeterminate'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + }, + { + "name": "literal", + "value": "'indeterminate'" + } + ] + }, + "description": "Status of checkbox." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether checkbox is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked checkbox." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for checkbox." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Checkbox/CheckboxAndroid.tsx" + ], + "group": "Checkbox" + }, + "Checkbox/CheckboxIOS": { + "filepath": "Checkbox/CheckboxIOS.tsx", + "title": "Checkbox.IOS", + "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "checkbox-ios", + "data": { + "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "Checkbox.IOS", + "methods": [], + "statics": [], + "props": { + "status": { + "required": true, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked' | 'indeterminate'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + }, + { + "name": "literal", + "value": "'indeterminate'" + } + ] + }, + "description": "Status of checkbox." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether checkbox is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for checkbox." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Checkbox/CheckboxIOS.tsx" + ], + "group": "Checkbox" + }, + "Checkbox/CheckboxItem": { + "filepath": "Checkbox/CheckboxItem.tsx", + "title": "Checkbox.Item", + "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "checkbox-item", + "data": { + "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Checkbox.Item", + "methods": [], + "statics": [], + "props": { + "status": { + "required": true, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked' | 'indeterminate'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + }, + { + "name": "literal", + "value": "'indeterminate'" + } + ] + }, + "description": "Status of checkbox." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether checkbox is disabled." + }, + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Label to be displayed on the item." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", + "defaultValue": { + "value": "label", + "computed": true + } + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked checkbox." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for checkbox." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Additional styles for container View." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach.", + "defaultValue": { + "value": "1.5", + "computed": false + } + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Label element." + }, + "labelVariant": { + "required": false, + "tsType": { + "name": "unknown" + }, + "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyLarge'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + }, + "position": { + "required": false, + "tsType": { + "name": "union", + "raw": "'leading' | 'trailing'", + "elements": [ + { + "name": "literal", + "value": "'leading'" + }, + { + "name": "literal", + "value": "'trailing'" + } + ] + }, + "description": "Checkbox control position.", + "defaultValue": { + "value": "'trailing'", + "computed": false + } + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'android' | 'ios'", + "elements": [ + { + "name": "literal", + "value": "'android'" + }, + { + "name": "literal", + "value": "'ios'" + } + ] + }, + "description": "Whether `` or `` should be used.\nLeft undefined `` will be used." + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Checkbox/CheckboxItem.tsx" + ], + "group": "Checkbox" + }, + "Chip/Chip": { + "filepath": "Chip/Chip.tsx", + "title": "Chip", + "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n

    \n
  • Present multiple options
  • \n
  • Represent attributes active or chosen
  • \n
  • Present filter options
  • \n
  • Trigger actions related to primary content
  • \n
\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", + "link": "chip", + "data": { + "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n
    \n
  • Present multiple options
  • \n
  • Represent attributes active or chosen
  • \n
  • Present filter options
  • \n
  • Trigger actions related to primary content
  • \n
\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", + "displayName": "Chip", + "methods": [], + "statics": [], + "props": { + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'outlined'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'outlined'" + } + ] + }, + "description": "Mode of the chip.\n- `flat` - flat chip without outline.\n- `outlined` - chip with an outline.", + "defaultValue": { + "value": "'flat'", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text content of the `Chip`." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Chip`. Both icon and avatar cannot be specified." + }, + "avatar": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Avatar to display for the `Chip`. Both icon and avatar cannot be specified." + }, + "closeIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display as the close button for the `Chip`. The icon appears only when the onClose prop is specified." + }, + "selected": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether chip is selected.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "selectedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Whether to style the chip color as selected.\nNote: With theme version 3 `selectedColor` doesn't apply to the `icon`.\n If you want specify custom color for the `icon`, render your own `Icon` component." + }, + "showSelectedOverlay": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether to display overlay on selected chip", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "showSelectedCheck": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to display default check icon on selected chip.\nNote: Check will not be shown if `icon` is specified. If specified, `icon` will be shown regardless of `selected`.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the chip is disabled. A disabled chip is greyed out and `onPress` is not called on touch.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the chip. This is read by the screen reader when the user taps the chip." + }, + "closeIconAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon.", + "defaultValue": { + "value": "'Close'", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touch is released even before onPress." + }, + "onClose": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on close button press. The close button appears only when this prop is specified." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nSets smaller horizontal paddings `12dp` around label, when there is only label." + }, + "elevated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether chip should have the elevation.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "textStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of chip's text" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Pass down testID from chip props to touchable for Detox tests.", + "defaultValue": { + "value": "'chip'", + "computed": false + } + }, + "ellipsizeMode": { + "required": false, + "tsType": { + "name": "EllipsizeProp" + }, + "description": "Ellipsize Mode for the children text" + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "accessibilityRole": { + "defaultValue": { + "value": "'button'", + "computed": false + }, + "required": false, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Chip/Chip.tsx" + ] + }, + "DataTable/DataTable": { + "filepath": "DataTable/DataTable.tsx", + "title": "DataTable", + "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "data-table", + "data": { + "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "DataTable", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTable`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTable.tsx" + ] + }, + "DataTable/DataTableCell": { + "filepath": "DataTable/DataTableCell.tsx", + "title": "DataTable.Cell", + "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "data-table-cell", + "data": { + "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "DataTable.Cell", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTableCell`." + }, + "numeric": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Align the text to the right. Generally monetary or number fields are aligned to right." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "textStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Text content style of the `DataTableCell`." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableCell.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTableHeader": { + "filepath": "DataTable/DataTableHeader.tsx", + "title": "DataTable.Header", + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "link": "data-table-header", + "data": { + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "DataTable.Header", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTableHeader`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableHeader.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTablePagination": { + "filepath": "DataTable/DataTablePagination.tsx", + "title": "DataTable.Pagination", + "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "data-table-pagination", + "data": { + "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "DataTable.Pagination", + "methods": [], + "statics": [], + "props": { + "page": { + "required": true, + "tsType": { + "name": "number" + }, + "description": "The currently visible page (starting with 0)." + }, + "numberOfPages": { + "required": true, + "tsType": { + "name": "number" + }, + "description": "The total number of pages." + }, + "onPageChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(page: number) => void", + "signature": { + "arguments": [ + { + "name": "page", + "type": { + "name": "number" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on page change." + }, + "showFastPaginationControls": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show fast forward and fast rewind buttons in pagination. False by default.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "paginationControlRippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the pagination control ripple effect." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "numberOfItemsPerPage": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The current number of rows per page." + }, + "numberOfItemsPerPageList": { + "required": false, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "number" + } + ], + "raw": "Array" + }, + "description": "Options for a number of rows per page to choose from." + }, + "onItemsPerPageChange": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(numberOfItemsPerPage: number) => void", + "signature": { + "arguments": [ + { + "name": "numberOfItemsPerPage", + "type": { + "name": "number" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "The function to set the number of rows per page." + }, + "dropdownItemRippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the dropdown item ripple effect." + }, + "selectPageDropdownRippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the select page dropdown ripple effect." + }, + "selectPageDropdownLabel": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Label text for select page dropdown to display." + }, + "selectPageDropdownAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "AccessibilityLabel for `selectPageDropdownLabel`." + }, + "label": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Label text to display which indicates current pagination." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "AccessibilityLabel for `label`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTablePagination.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTableRow": { + "filepath": "DataTable/DataTableRow.tsx", + "title": "DataTable.Row", + "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "data-table-row", + "data": { + "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "DataTable.Row", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTableRow`." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "pointerEvents": { + "required": false, + "tsType": { + "name": "ViewProps['pointerEvents']", + "raw": "ViewProps['pointerEvents']" + }, + "description": "`pointerEvents` passed to the `View` container, which is wrapping children within `TouchableRipple`." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableRow.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTableTitle": { + "filepath": "DataTable/DataTableTitle.tsx", + "title": "DataTable.Title", + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "link": "data-table-title", + "data": { + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "DataTable.Title", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text content of the `DataTableTitle`." + }, + "numeric": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Align the text to the right. Generally monetary or number fields are aligned to right." + }, + "sortDirection": { + "required": false, + "tsType": { + "name": "union", + "raw": "'ascending' | 'descending'", + "elements": [ + { + "name": "literal", + "value": "'ascending'" + }, + { + "name": "literal", + "value": "'descending'" + } + ] + }, + "description": "Direction of sorting. An arrow indicating the direction is displayed when this is given." + }, + "numberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of lines to show.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "textStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Text content style of the `DataTableTitle`." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableTitle.tsx" + ], + "group": "DataTable" + }, + "Dialog/Dialog": { + "filepath": "Dialog/Dialog.tsx", + "title": "Dialog", + "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog", + "data": { + "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog", + "methods": [], + "statics": [], + "props": { + "dismissable": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking outside the dialog dismiss it.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "dismissableBackButton": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking Android hardware back button dismiss dialog.", + "defaultValue": { + "value": "dismissable", + "computed": true + } + }, + "onDismiss": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the user dismisses the dialog." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines Whether the dialog is visible.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Dialog`." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/Dialog.tsx" + ] + }, + "Dialog/DialogActions": { + "filepath": "Dialog/DialogActions.tsx", + "title": "Dialog.Actions", + "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-actions", + "data": { + "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Actions", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DialogActions`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogActions.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogContent": { + "filepath": "Dialog/DialogContent.tsx", + "title": "Dialog.Content", + "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-content", + "data": { + "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Content", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DialogContent`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogContent.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogIcon": { + "filepath": "Dialog/DialogIcon.tsx", + "title": "Dialog.Icon", + "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", + "link": "dialog-icon", + "data": { + "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Icon", + "methods": [], + "statics": [], + "props": { + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for action icon." + }, + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Name of the icon to show." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Optional icon size.", + "defaultValue": { + "value": "24", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogIcon.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogScrollArea": { + "filepath": "Dialog/DialogScrollArea.tsx", + "title": "Dialog.ScrollArea", + "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-scroll-area", + "data": { + "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.ScrollArea", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DialogScrollArea`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogScrollArea.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogTitle": { + "filepath": "Dialog/DialogTitle.tsx", + "title": "Dialog.Title", + "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-title", + "data": { + "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Title", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Title text for the `DialogTitle`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogTitle.tsx" + ], + "group": "Dialog" + }, + "Divider": { + "filepath": "Divider.tsx", + "title": "Divider", + "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", + "link": "divider", + "data": { + "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Divider", + "methods": [], + "statics": [], + "props": { + "leftInset": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@renamed Renamed from 'inset' to 'leftInset` in v5.x\nWhether divider has a left inset." + }, + "horizontalInset": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n Whether divider has a horizontal inset on both sides.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "bold": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n Whether divider should be bolded.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Divider.tsx" + ] + }, + "Drawer/DrawerCollapsedItem": { + "filepath": "Drawer/DrawerCollapsedItem.tsx", + "title": "Drawer.CollapsedItem", + "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "drawer-collapsed-item", + "data": { + "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Drawer.CollapsedItem", + "methods": [], + "statics": [], + "props": { + "label": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "The label text of the item." + }, + "badge": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number | boolean", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + }, + { + "name": "boolean" + } + ] + }, + "description": "Badge to show on the icon, can be `true` to show a dot, `string` or `number` to show text.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the item is disabled." + }, + "focusedIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the focused destination icon, can be a string, an image source or a react component" + }, + "unfocusedIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the unfocused destination icon, can be a string, an image source or a react component" + }, + "active": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to highlight the drawer item as active." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'drawer-collapsed-item'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Drawer/DrawerCollapsedItem.tsx" + ], + "group": "Drawer" + }, + "Drawer/DrawerItem": { + "filepath": "Drawer/DrawerItem.tsx", + "title": "Drawer.Item", + "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "drawer-item", + "data": { + "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Drawer.Item", + "methods": [], + "statics": [], + "props": { + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "The label text of the item." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `DrawerItem`." + }, + "active": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to highlight the drawer item as active." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the item is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: string }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: string }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side. For instance a Badge." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Drawer/DrawerItem.tsx" + ], + "group": "Drawer" + }, + "Drawer/DrawerSection": { + "filepath": "Drawer/DrawerSection.tsx", + "title": "Drawer.Section", + "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "drawer-section", + "data": { + "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Drawer.Section", + "methods": [], + "statics": [], + "props": { + "title": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Title to show as the header for the section." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Drawer.Section`." + }, + "showDivider": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show `Divider` at the end of the section. True by default.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Drawer/DrawerSection.tsx" + ], + "group": "Drawer" + }, + "FAB/FAB": { + "filepath": "FAB/FAB.tsx", + "title": "FAB", + "description": "A floating action button represents the primary action on a screen. It appears in front of all screen content.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n})\n\nexport default MyComponent;\n```", + "link": "fab", + "data": { + "description": "A floating action button represents the primary action on a screen. It appears in front of all screen content.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n})\n\nexport default MyComponent;\n```", + "displayName": "FAB", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `FAB`. It's optional only if `label` is defined." + }, + "label": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Optional label for extended `FAB`. It's optional only if `icon` is defined." + }, + "uppercase": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Make the label text uppercased." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB.\nUses `label` by default if specified.", + "defaultValue": { + "value": "label", + "computed": true + } + }, + "accessibilityState": { + "required": false, + "tsType": { + "name": "AccessibilityState" + }, + "description": "Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB." + }, + "animated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether an icon change is animated.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "small": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@deprecated Deprecated in v.5x - use prop size=\"small\".\n\n Whether FAB is mini-sized, used to create visual continuity with other elements. This has no effect if `label` is specified." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the icon and label of the `FAB`." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether `FAB` is currently visible.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "loading": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show a loading indicator." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "size": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'medium' | 'large'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'large'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nSize of the `FAB`.\n- `small` - FAB with small height (40).\n- `medium` - FAB with default medium height (56).\n- `large` - FAB with large height (96).", + "defaultValue": { + "value": "'medium'", + "computed": false + } + }, + "customSize": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Custom size for the `FAB`. This prop takes precedence over size prop" + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'elevated'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'elevated'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nMode of the `FAB`. You can change the mode to adjust the the shadow:\n- `flat` - button without a shadow.\n- `elevated` - button with a shadow.", + "defaultValue": { + "value": "'elevated'", + "computed": false + } + }, + "variant": { + "required": false, + "tsType": { + "name": "union", + "raw": "'primary' | 'secondary' | 'tertiary' | 'surface'", + "elements": [ + { + "name": "literal", + "value": "'primary'" + }, + { + "name": "literal", + "value": "'secondary'" + }, + { + "name": "literal", + "value": "'tertiary'" + }, + { + "name": "literal", + "value": "'surface'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nColor mappings variant for combinations of container and icon colors.", + "defaultValue": { + "value": "'primary'", + "computed": false + } + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'fab'", + "computed": false + } + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/FAB/FAB.tsx" + ] + }, + "FAB/AnimatedFAB": { + "filepath": "FAB/AnimatedFAB.tsx", + "title": "AnimatedFAB", + "description": "An animated, extending horizontally floating action button represents the primary action in an application.\n\n## Usage\n```js\nimport React from 'react';\nimport {\n StyleProp,\n ViewStyle,\n Animated,\n StyleSheet,\n Platform,\n ScrollView,\n Text,\n SafeAreaView,\n I18nManager,\n} from 'react-native';\nimport { AnimatedFAB } from 'react-native-paper';\n\nconst MyComponent = ({\n animatedValue,\n visible,\n extended,\n label,\n animateFrom,\n style,\n iconMode,\n}) => {\n const [isExtended, setIsExtended] = React.useState(true);\n\n const isIOS = Platform.OS === 'ios';\n\n const onScroll = ({ nativeEvent }) => {\n const currentScrollPosition =\n Math.floor(nativeEvent?.contentOffset?.y) ?? 0;\n\n setIsExtended(currentScrollPosition <= 0);\n };\n\n const fabStyle = { [animateFrom]: 16 };\n\n return (\n \n \n {[...new Array(100).keys()].map((_, i) => (\n {i}\n ))}\n \n console.log('Pressed')}\n visible={visible}\n animateFrom={'right'}\n iconMode={'static'}\n style={[styles.fabStyle, style, fabStyle]}\n />\n \n );\n};\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n container: {\n flexGrow: 1,\n },\n fabStyle: {\n bottom: 16,\n right: 16,\n position: 'absolute',\n },\n});\n```", + "link": "animated-fab", + "data": { + "description": "An animated, extending horizontally floating action button represents the primary action in an application.\n\n## Usage\n```js\nimport React from 'react';\nimport {\n StyleProp,\n ViewStyle,\n Animated,\n StyleSheet,\n Platform,\n ScrollView,\n Text,\n SafeAreaView,\n I18nManager,\n} from 'react-native';\nimport { AnimatedFAB } from 'react-native-paper';\n\nconst MyComponent = ({\n animatedValue,\n visible,\n extended,\n label,\n animateFrom,\n style,\n iconMode,\n}) => {\n const [isExtended, setIsExtended] = React.useState(true);\n\n const isIOS = Platform.OS === 'ios';\n\n const onScroll = ({ nativeEvent }) => {\n const currentScrollPosition =\n Math.floor(nativeEvent?.contentOffset?.y) ?? 0;\n\n setIsExtended(currentScrollPosition <= 0);\n };\n\n const fabStyle = { [animateFrom]: 16 };\n\n return (\n \n \n {[...new Array(100).keys()].map((_, i) => (\n {i}\n ))}\n \n console.log('Pressed')}\n visible={visible}\n animateFrom={'right'}\n iconMode={'static'}\n style={[styles.fabStyle, style, fabStyle]}\n />\n \n );\n};\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n container: {\n flexGrow: 1,\n },\n fabStyle: {\n bottom: 16,\n right: 16,\n position: 'absolute',\n },\n});\n```", + "displayName": "AnimatedFAB", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `FAB`." + }, + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Label for extended `FAB`." + }, + "uppercase": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Make the label text uppercased." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB.\nUses `label` by default if specified.", + "defaultValue": { + "value": "label", + "computed": true + } + }, + "accessibilityState": { + "required": false, + "tsType": { + "name": "AccessibilityState" + }, + "description": "Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the icon and label of the `FAB`." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether `FAB` is currently visible.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "iconMode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'static' | 'dynamic'", + "elements": [ + { + "name": "literal", + "value": "'static'" + }, + { + "name": "literal", + "value": "'dynamic'" + } + ] + }, + "description": "Whether icon should be translated to the end of extended `FAB` or be static and stay in the same place. The default value is `dynamic`.", + "defaultValue": { + "value": "'dynamic'", + "computed": false + } + }, + "animateFrom": { + "required": false, + "tsType": { + "name": "union", + "raw": "'left' | 'right'", + "elements": [ + { + "name": "literal", + "value": "'left'" + }, + { + "name": "literal", + "value": "'right'" + } + ] + }, + "description": "Indicates from which direction animation should be performed. The default value is `right`.", + "defaultValue": { + "value": "'right'", + "computed": false + } + }, + "extended": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether `FAB` should start animation to extend.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "variant": { + "required": false, + "tsType": { + "name": "union", + "raw": "'primary' | 'secondary' | 'tertiary' | 'surface'", + "elements": [ + { + "name": "literal", + "value": "'primary'" + }, + { + "name": "literal", + "value": "'secondary'" + }, + { + "name": "literal", + "value": "'tertiary'" + }, + { + "name": "literal", + "value": "'surface'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nColor mappings variant for combinations of container and icon colors.", + "defaultValue": { + "value": "'primary'", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'animated-fab'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/FAB/AnimatedFAB.tsx" + ] + }, + "FAB/FABGroup": { + "filepath": "FAB/FABGroup.tsx", + "title": "FAB.Group", + "description": "A component to display a stack of FABs with related actions in a speed dial.\nTo render the group above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { FAB, Portal, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [state, setState] = React.useState({ open: false });\n\n const onStateChange = ({ open }) => setState({ open });\n\n const { open } = state;\n\n return (\n \n \n console.log('Pressed add') },\n {\n icon: 'star',\n label: 'Star',\n onPress: () => console.log('Pressed star'),\n },\n {\n icon: 'email',\n label: 'Email',\n onPress: () => console.log('Pressed email'),\n },\n {\n icon: 'bell',\n label: 'Remind',\n onPress: () => console.log('Pressed notifications'),\n },\n ]}\n onStateChange={onStateChange}\n onPress={() => {\n if (open) {\n // do something if the speed dial is open\n }\n }}\n />\n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "fab-group", + "data": { + "description": "A component to display a stack of FABs with related actions in a speed dial.\nTo render the group above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { FAB, Portal, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [state, setState] = React.useState({ open: false });\n\n const onStateChange = ({ open }) => setState({ open });\n\n const { open } = state;\n\n return (\n \n \n console.log('Pressed add') },\n {\n icon: 'star',\n label: 'Star',\n onPress: () => console.log('Pressed star'),\n },\n {\n icon: 'email',\n label: 'Email',\n onPress: () => console.log('Pressed email'),\n },\n {\n icon: 'bell',\n label: 'Remind',\n onPress: () => console.log('Pressed notifications'),\n },\n ]}\n onStateChange={onStateChange}\n onPress={() => {\n if (open) {\n // do something if the speed dial is open\n }\n }}\n />\n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "FAB.Group", + "methods": [], + "statics": [], + "props": { + "actions": { + "required": true, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n icon: IconSource;\n label?: string;\n color?: string;\n labelTextColor?: string;\n accessibilityLabel?: string;\n accessibilityHint?: string;\n style?: Animated.WithAnimatedValue>;\n containerStyle?: Animated.WithAnimatedValue>;\n wrapperStyle?: StyleProp;\n labelStyle?: StyleProp;\n labelMaxFontSizeMultiplier?: number;\n onPress: (e: GestureResponderEvent) => void;\n size?: 'small' | 'medium';\n testID?: string;\n rippleColor?: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": true + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "color", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "labelTextColor", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "accessibilityHint", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "style", + "value": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>", + "required": false + } + }, + { + "key": "containerStyle", + "value": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>", + "required": false + } + }, + { + "key": "wrapperStyle", + "value": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp", + "required": false + } + }, + { + "key": "labelStyle", + "value": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp", + "required": false + } + }, + { + "key": "labelMaxFontSizeMultiplier", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "size", + "value": { + "name": "union", + "raw": "'small' | 'medium'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + } + ], + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "rippleColor", + "value": { + "name": "ColorValue", + "required": false + } + } + ] + } + } + ], + "raw": "Array<{\n icon: IconSource;\n label?: string;\n color?: string;\n labelTextColor?: string;\n accessibilityLabel?: string;\n accessibilityHint?: string;\n style?: Animated.WithAnimatedValue>;\n containerStyle?: Animated.WithAnimatedValue>;\n wrapperStyle?: StyleProp;\n labelStyle?: StyleProp;\n labelMaxFontSizeMultiplier?: number;\n onPress: (e: GestureResponderEvent) => void;\n size?: 'small' | 'medium';\n testID?: string;\n rippleColor?: ColorValue;\n}>" + }, + "description": "Action items to display in the form of a speed dial.\nAn action item should contain the following properties:\n- `icon`: icon to display (required)\n- `label`: optional label text\n- `color`: custom icon color of the action item\n- `labelTextColor`: custom label text color of the action item\n- `accessibilityLabel`: accessibility label for the action, uses label by default if specified\n- `accessibilityHint`: accessibility hint for the action\n- `style`: pass additional styles for the fab item, for example, `backgroundColor`\n- `containerStyle`: pass additional styles for the fab item label container, for example, `backgroundColor` @supported Available in 5.x\n- `wrapperStyle`: pass additional styles for the wrapper of the action item.\n- `labelStyle`: pass additional styles for the fab item label, for example, `fontSize`\n- `labelMaxFontSizeMultiplier`: specifies the largest possible scale a title font can reach.\n- `onPress`: callback that is called when `FAB` is pressed (required)\n- `onLongPress`: callback that is called when `FAB` is long pressed\n- `toggleStackOnLongPress`: callback that is called when `FAB` is long pressed\n- `size`: size of action item. Defaults to `small`. @supported Available in v5.x\n- `testID`: testID to be used on tests\n- `rippleColor`: color of the ripple effect." + }, + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `FAB`.\nYou can toggle it based on whether the speed dial is open to display a different icon." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the `FAB`." + }, + "backdropColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom backdrop color for opened speed dial background." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on pressing the `FAB`." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long pressing the `FAB`." + }, + "toggleStackOnLongPress": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Makes actions stack appear on long press instead of on press." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Changes the delay for long press reaction.", + "defaultValue": { + "value": "200", + "computed": false + } + }, + "enableLongPressWhenStackOpened": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Allows for onLongPress when stack is opened.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "open": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether the speed dial is open." + }, + "onStateChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(state: { open: boolean }) => void", + "signature": { + "arguments": [ + { + "name": "state", + "type": { + "name": "signature", + "type": "object", + "raw": "{ open: boolean }", + "signature": { + "properties": [ + { + "key": "open", + "value": { + "name": "boolean", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback which is called on opening and closing the speed dial.\nThe open state needs to be updated when it's called, otherwise the change is dropped." + }, + "visible": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether `FAB` is currently visible." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the group. You can use it to pass additional styles if you need.\nFor example, you can set an additional padding if you have a tab bar at the bottom." + }, + "fabStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style for the FAB. It allows to pass the FAB button styles, such as backgroundColor." + }, + "variant": { + "required": false, + "tsType": { + "name": "union", + "raw": "'primary' | 'secondary' | 'tertiary' | 'surface'", + "elements": [ + { + "name": "literal", + "value": "'primary'" + }, + { + "name": "literal", + "value": "'secondary'" + }, + { + "name": "literal", + "value": "'tertiary'" + }, + { + "name": "literal", + "value": "'surface'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nColor mappings variant for combinations of container and icon colors.", + "defaultValue": { + "value": "'primary'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "label": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Optional label for `FAB`." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Pass down testID from Group props to FAB." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/FAB/FABGroup.tsx" + ], + "group": "FAB" + }, + "HelperText/HelperText": { + "filepath": "HelperText/HelperText.tsx", + "title": "HelperText", + "description": "Helper text is used in conjuction with input elements to provide additional hints for the user.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { HelperText, TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const onChangeText = text => setText(text);\n\n const hasErrors = () => {\n return !text.includes('@');\n };\n\n return (\n \n \n \n Email address is invalid!\n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "helper-text", + "data": { + "description": "Helper text is used in conjuction with input elements to provide additional hints for the user.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { HelperText, TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const onChangeText = text => setText(text);\n\n const hasErrors = () => {\n return !text.includes('@');\n };\n\n return (\n \n \n \n Email address is invalid!\n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "HelperText", + "methods": [], + "statics": [], + "props": { + "type": { + "required": false, + "tsType": { + "name": "union", + "raw": "'error' | 'info'", + "elements": [ + { + "name": "literal", + "value": "'error'" + }, + { + "name": "literal", + "value": "'info'" + } + ] + }, + "description": "Type of the helper text.", + "defaultValue": { + "value": "'info'", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text content of the HelperText." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to display the helper text.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "padding": { + "required": false, + "tsType": { + "name": "union", + "raw": "'none' | 'normal'", + "elements": [ + { + "name": "literal", + "value": "'none'" + }, + { + "name": "literal", + "value": "'normal'" + } + ] + }, + "description": "Whether to apply padding to the helper text.", + "defaultValue": { + "value": "'normal'", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the text input tied with helper text is disabled." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/HelperText/HelperText.tsx" + ] + }, + "IconButton/IconButton": { + "filepath": "IconButton/IconButton.tsx", + "title": "IconButton", + "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "icon-button", + "data": { + "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "IconButton", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'outlined' | 'contained' | 'contained-tonal'", + "elements": [ + { + "name": "literal", + "value": "'outlined'" + }, + { + "name": "literal", + "value": "'contained'" + }, + { + "name": "literal", + "value": "'contained-tonal'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nMode of the icon button. By default there is no specified mode - only pressable icon will be rendered." + }, + "iconColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "@renamed Renamed from 'color' to 'iconColor' in v5.x\nColor of the icon." + }, + "containerColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Background color of the icon container." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "selected": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether icon button is selected. A selected button receives alternative combination of icon and container colors.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the icon.", + "defaultValue": { + "value": "24", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "animated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether an icon change is animated.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of button's inner content.\nUse this prop to apply custom height and width or to set a custom padding`." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'icon-button'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "loading": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show a loading indicator.", + "defaultValue": { + "value": "false", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/IconButton/IconButton.tsx" + ] + }, + "Icon": { + "filepath": "Icon.tsx", + "title": "Icon", + "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "icon", + "data": { + "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Icon", + "methods": [], + "statics": [], + "props": { + "size": { + "required": true, + "tsType": { + "name": "number" + }, + "description": "Size of icon." + }, + "allowFontScaling": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "" + }, + "source": { + "required": true, + "tsType": { + "name": "any" + }, + "description": "Icon to display." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Color of the icon." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Icon.tsx" + ] + }, + "List/ListAccordion": { + "filepath": "List/ListAccordion.tsx", + "title": "List.Accordion", + "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "list-accordion", + "data": { + "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "List.Accordion", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Title text for the list accordion." + }, + "description": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Description text for the list accordion." + }, + "left": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: string; style: Style }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: string; style: Style }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the left side." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { isExpanded: boolean }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ isExpanded: boolean }", + "signature": { + "properties": [ + { + "key": "isExpanded", + "value": { + "name": "boolean", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side." + }, + "expanded": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the accordion is expanded\nIf this prop is provided, the accordion will behave as a \"controlled component\".\nYou'll need to update this prop when you want to toggle the component or on `onPress`." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the section." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the root TouchableRipple container." + }, + "containerStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the content container, which wraps the title and description." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Title element." + }, + "descriptionStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Description element." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "titleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "descriptionNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "descriptionMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a description font can reach." + }, + "id": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + }, + "description": "Id is used for distinguishing specific accordion when using List.AccordionGroup. Property is required when using List.AccordionGroup and has no impact on behavior when using standalone List.Accordion." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the TouchableRipple. This is read by the screen reader when the user taps the touchable." + }, + "pointerEvents": { + "required": false, + "tsType": { + "name": "ViewProps['pointerEvents']", + "raw": "ViewProps['pointerEvents']" + }, + "description": "`pointerEvents` passed to the `View` container", + "defaultValue": { + "value": "'none'", + "computed": false + } + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Amount of space between the touchable area and the edge of the component.\nThis can be used to enlarge the touchable area beyond the visible component." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListAccordion.tsx" + ], + "group": "List" + }, + "List/ListAccordionGroup": { + "filepath": "List/ListAccordionGroup.tsx", + "title": "List.AccordionGroup", + "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "list-accordion-group", + "data": { + "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "List.AccordionGroup", + "methods": [], + "statics": [], + "props": { + "onAccordionPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(expandedId: string | number) => void", + "signature": { + "arguments": [ + { + "name": "expandedId", + "type": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "expandedId": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + }, + "description": "Id of the currently expanded list accordion" + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing list accordions" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListAccordionGroup.tsx" + ], + "group": "List" + }, + "List/ListIcon": { + "filepath": "List/ListIcon.tsx", + "title": "List.Icon", + "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "list-icon", + "data": { + "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "List.Icon", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to show." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Color for the icon." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListIcon.tsx" + ], + "group": "List" + }, + "List/ListItem": { + "filepath": "List/ListItem.tsx", + "title": "List.Item", + "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "list-item", + "data": { + "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "List.Item", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "union", + "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: string;\n fontSize: number;\n }) => React.ReactNode)", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "unknown" + } + ] + }, + "description": "Title text for the list item." + }, + "description": { + "required": false, + "tsType": { + "name": "union", + "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: string;\n fontSize: number;\n }) => React.ReactNode)", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "unknown" + } + ] + }, + "description": "Description text for the list item or callback which returns a React element to display the description." + }, + "left": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: string; style: Style }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: string; style: Style }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the left side." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: string; style?: Style }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: string; style?: Style }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": false + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the root TouchableRipple container." + }, + "containerStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the content container, which wraps the title and description." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Title element." + }, + "descriptionStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Description element." + }, + "titleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "descriptionNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "titleEllipsizeMode": { + "required": false, + "tsType": { + "name": "EllipsizeProp" + }, + "description": "Ellipsize Mode for the Title. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" + }, + "descriptionEllipsizeMode": { + "required": false, + "tsType": { + "name": "EllipsizeProp" + }, + "description": "Ellipsize Mode for the Description. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "descriptionMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a description font can reach." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListItem.tsx" + ], + "group": "List" + }, + "List/ListSection": { + "filepath": "List/ListSection.tsx", + "title": "List.Section", + "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", + "link": "list-section", + "data": { + "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "List.Section", + "methods": [], + "statics": [], + "props": { + "title": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Title text for the section." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the section." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Title element." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListSection.tsx" + ], + "group": "List" + }, + "List/ListSubheader": { + "filepath": "List/ListSubheader.tsx", + "title": "List.Subheader", + "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", + "link": "list-subheader", + "data": { + "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", + "displayName": "List.Subheader", + "methods": [], + "statics": [], + "props": { + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Text element." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListSubheader.tsx" + ], + "group": "List" + }, + "Menu/Menu": { + "filepath": "Menu/Menu.tsx", + "title": "Menu", + "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n

\n
\n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", + "link": "menu", + "data": { + "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n \n
\n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", + "displayName": "Menu", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether the Menu is currently visible." + }, + "anchor": { + "required": true, + "tsType": { + "name": "union", + "raw": "React.ReactNode | { x: number; y: number }", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "signature", + "type": "object", + "raw": "{ x: number; y: number }", + "signature": { + "properties": [ + { + "key": "x", + "value": { + "name": "number", + "required": true + } + }, + { + "key": "y", + "value": { + "name": "number", + "required": true + } + } + ] + } + } + ] + }, + "description": "The anchor to open the menu from. In most cases, it will be a button that opens the menu." + }, + "anchorPosition": { + "required": false, + "tsType": { + "name": "union", + "raw": "'top' | 'bottom'", + "elements": [ + { + "name": "literal", + "value": "'top'" + }, + { + "name": "literal", + "value": "'bottom'" + } + ] + }, + "description": "Whether the menu should open at the top of the anchor or at its bottom.\nApplied only when anchor is a node, not an x/y position." + }, + "statusBarHeight": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Extra margin to add at the top of the menu to account for translucent status bar on Android.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to and customize it.\nThis is automatically handled on iOS." + }, + "onDismiss": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback called when Menu is dismissed. The `visible` prop needs to be updated when this is called." + }, + "overlayAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the menu.", + "defaultValue": { + "value": "'Close menu'", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Menu`." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style of menu's inner content." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "elevation": { + "required": false, + "tsType": { + "name": "MD3Elevation" + }, + "description": "Elevation level of the menu's content. Shadow styles are calculated based on this value. Default `backgroundColor` is taken from the corresponding `theme.colors.elevation` property. By default equals `2`.\n@supported Available in v5.x with theme version 3", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'elevated'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'elevated'" + } + ] + }, + "description": "Mode of the menu's content.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.\n\n@supported Available in v5.x with theme version 3", + "defaultValue": { + "value": "'elevated'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "keyboardShouldPersistTaps": { + "required": false, + "tsType": { + "name": "ScrollViewProps['keyboardShouldPersistTaps']", + "raw": "ScrollViewProps['keyboardShouldPersistTaps']" + }, + "description": "Inner ScrollView prop" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'menu'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Menu/Menu.tsx" + ] + }, + "Menu/MenuItem": { + "filepath": "Menu/MenuItem.tsx", + "title": "Menu.Item", + "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", + "link": "menu-item", + "data": { + "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Menu.Item", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Title text for the `MenuItem`." + }, + "leadingIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@renamed Renamed from 'icon' to 'leadingIcon' in v5.x\n\nLeading icon to display for the `MenuItem`." + }, + "trailingIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@supported Available in v5.x with theme version 3\n\nTrailing icon to display for the `MenuItem`." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the 'item' is disabled. A disabled 'item' is greyed out and `onPress` is not called on touch." + }, + "dense": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n\nSets min height with densed layout." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach.", + "defaultValue": { + "value": "1.5", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the root TouchableRipple container.\n" + }, + "containerStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the outermost container that wraps the entire content, including leading and trailing icons and title text." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the content container, which wraps the title text." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the Title element." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'menu-item'", + "computed": false + } + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the Touchable. This is read by the screen reader when the user taps the component." + }, + "accessibilityState": { + "required": false, + "tsType": { + "name": "AccessibilityState" + }, + "description": "Accessibility state for the Touchable. This is read by the screen reader when the user taps the component." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Menu/MenuItem.tsx" + ], + "group": "Menu" + }, + "Modal": { + "filepath": "Modal.tsx", + "title": "Modal", + "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "modal", + "data": { + "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Modal", + "methods": [], + "statics": [], + "props": { + "dismissable": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking outside the modal dismisses it.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "dismissableBackButton": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking Android hardware back button dismisses the dialog.", + "defaultValue": { + "value": "dismissable", + "computed": true + } + }, + "onDismiss": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the user dismisses the modal.", + "defaultValue": { + "value": "() => {}", + "computed": false + } + }, + "overlayAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal.", + "defaultValue": { + "value": "'Close modal'", + "computed": false + } + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines Whether the modal is visible.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Modal`." + }, + "contentContainerStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style for the content of the modal" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the wrapper of the modal.\nUse this prop to change the default wrapper style or to override safe area insets with marginTop and marginBottom." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'modal'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Modal.tsx" + ] + }, + "Portal/Portal": { + "filepath": "Portal/Portal.tsx", + "title": "Portal", + "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", + "link": "portal", + "data": { + "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Portal", + "methods": [], + "statics": [ + { + "name": "Host", + "docblock": null, + "type": { + "name": "static", + "raw": "static" + }, + "link": "portal-host.html" + } + ], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Portal`." + }, + "theme": { + "required": true, + "tsType": { + "name": "InternalTheme" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Portal/Portal.tsx" + ] + }, + "Portal/PortalHost": { + "filepath": "Portal/PortalHost.tsx", + "title": "Portal.Host", + "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", + "link": "portal-host", + "data": { + "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", + "displayName": "Portal.Host", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Portal/PortalHost.tsx" + ], + "group": "Portal" + }, + "ProgressBar": { + "filepath": "ProgressBar.tsx", + "title": "ProgressBar", + "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "progress-bar", + "data": { + "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "ProgressBar", + "methods": [], + "statics": [], + "props": { + "animatedValue": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Animated value (between 0 and 1). This tells the progress bar to rely on this value to animate it.\nNote: It should not be used in parallel with the `progress` prop." + }, + "progress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Progress value (between 0 and 1).\nNote: It should not be used in parallel with the `animatedValue` prop.", + "defaultValue": { + "value": "0", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Color of the progress bar. The background color will be calculated based on this but you can change it by passing `backgroundColor` to `style` prop." + }, + "indeterminate": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "If the progress bar will show indeterminate progress." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show the ProgressBar (true, the default) or hide it (false).", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "fillStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style of filled part of the ProgresBar." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'progress-bar'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ProgressBar.tsx" + ] + }, + "RadioButton/RadioButton": { + "filepath": "RadioButton/RadioButton.tsx", + "title": "RadioButton", + "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "radio-button", + "data": { + "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "RadioButton", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button" + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked radio." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButton.tsx" + ] + }, + "RadioButton/RadioButtonAndroid": { + "filepath": "RadioButton/RadioButtonAndroid.tsx", + "title": "RadioButton.Android", + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "radio-button-android", + "data": { + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "RadioButton.Android", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button" + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(param?: any) => void", + "signature": { + "arguments": [ + { + "name": "param", + "type": { + "name": "any" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked radio." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonAndroid.tsx" + ], + "group": "RadioButton" + }, + "RadioButton/RadioButtonGroup": { + "filepath": "RadioButton/RadioButtonGroup.tsx", + "title": "RadioButton.Group", + "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "radio-button-group", + "data": { + "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "RadioButton.Group", + "methods": [], + "statics": [], + "props": { + "onValueChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: string) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the currently selected radio button." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing radio buttons." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonGroup.tsx" + ], + "group": "RadioButton" + }, + "RadioButton/RadioButtonIOS": { + "filepath": "RadioButton/RadioButtonIOS.tsx", + "title": "RadioButton.IOS", + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "radio-button-ios", + "data": { + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "RadioButton.IOS", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button" + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonIOS.tsx" + ], + "group": "RadioButton" + }, + "RadioButton/RadioButtonItem": { + "filepath": "RadioButton/RadioButtonItem.tsx", + "title": "RadioButton.Item", + "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "radio-button-item", + "data": { + "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "RadioButton.Item", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button." + }, + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Label to be displayed on the item." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", + "defaultValue": { + "value": "label", + "computed": true + } + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked radio." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Additional styles for container View." + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Label element." + }, + "labelVariant": { + "required": false, + "tsType": { + "name": "unknown" + }, + "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyLarge'", + "computed": false + } + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'android' | 'ios'", + "elements": [ + { + "name": "literal", + "value": "'android'" + }, + { + "name": "literal", + "value": "'ios'" + } + ] + }, + "description": "Whether `` or `` should be used.\nLeft undefined `` will be used." + }, + "position": { + "required": false, + "tsType": { + "name": "union", + "raw": "'leading' | 'trailing'", + "elements": [ + { + "name": "literal", + "value": "'leading'" + }, + { + "name": "literal", + "value": "'trailing'" + } + ] + }, + "description": "Radio button control position.", + "defaultValue": { + "value": "'trailing'", + "computed": false + } + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonItem.tsx" + ], + "group": "RadioButton" + }, + "Searchbar": { + "filepath": "Searchbar.tsx", + "title": "Searchbar", + "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", + "link": "searchbar", + "data": { + "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", + "displayName": "Searchbar", + "methods": [], + "statics": [], + "props": { + "placeholder": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Hint text shown when the input is empty." + }, + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "The value of the text input." + }, + "onChangeText": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(query: string) => void", + "signature": { + "arguments": [ + { + "name": "query", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the text input's text changes." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'bar' | 'view'", + "elements": [ + { + "name": "literal", + "value": "'bar'" + }, + { + "name": "literal", + "value": "'view'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nSearch layout mode, the default value is \"bar\".", + "defaultValue": { + "value": "'bar'", + "computed": false + } + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon name for the left icon button (see `onIconPress`)." + }, + "iconColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon, default will be derived from theme" + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "onIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback to execute if we want the left icon to act as button." + }, + "onClearIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback to execute if we want to add custom behaviour to close icon button." + }, + "searchAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'search'", + "computed": false + } + }, + "clearIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Custom icon for clear button, default will be icon close. It's visible when `loading` is set to `false`.\nIn v5.x with theme version 3, `clearIcon` is visible only if `right` prop is not defined." + }, + "clearAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'clear'", + "computed": false + } + }, + "traileringIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@supported Available in v5.x with theme version 3\nIcon name for the right trailering icon button.\nWorks only when `mode` is set to \"bar\". It won't be displayed if `loading` is set to `true`." + }, + "traileringIconColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "@supported Available in v5.x with theme version 3\nCustom color for the right trailering icon, default will be derived from theme" + }, + "traileringRippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "@supported Available in v5.x with theme version 3\nColor of the trailering icon ripple effect." + }, + "onTraileringIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "@supported Available in v5.x with theme version 3\nCallback to execute on the right trailering icon button press." + }, + "traileringIconAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the right trailering icon button. This is read by the screen reader when the user taps the button." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n color: string;\n style: Style;\n testID: string;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n color: string;\n style: Style;\n testID: string;\n}", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": true + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "@supported Available in v5.x with theme version 3\nCallback which returns a React element to display on the right side.\nWorks only when `mode` is set to \"bar\"." + }, + "showDivider": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether to show `Divider` at the bottom of the search.\nWorks only when `mode` is set to \"view\". True by default.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges Searchbar shadow and background on iOS and Android.", + "defaultValue": { + "value": "0", + "computed": false + } + }, + "inputStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Set style of the TextInput component inside the searchbar" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "loading": { + "required": false, + "tsType": { + "name": "Boolean" + }, + "description": "Custom flag for replacing clear button with activity indicator.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'search-bar'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Searchbar.tsx" + ] + }, + "SegmentedButtons/SegmentedButtons": { + "filepath": "SegmentedButtons/SegmentedButtons.tsx", + "title": "SegmentedButtons", + "description": "Segmented buttons can be used to select options, switch views or sort elements.
\n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", + "link": "segmented-buttons", + "data": { + "description": "Segmented buttons can be used to select options, switch views or sort elements.
\n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "SegmentedButtons", + "methods": [], + "statics": [], + "props": { + "buttons": { + "required": true, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "value", + "value": { + "name": "T", + "required": true + } + }, + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "disabled", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "checkedColor", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "uncheckedColor", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(event: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "event", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "showSelectedCheck", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "style", + "value": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp", + "required": false + } + }, + { + "key": "labelStyle", + "value": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + } + ], + "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}[]" + }, + "description": "Buttons to display as options in toggle button.\nButton should contain the following properties:\n- `value`: value of button (required)\n- `icon`: icon to display for the item\n- `disabled`: whether the button is disabled\n- `accessibilityLabel`: acccessibility label for the button. This is read by the screen reader when the user taps the button.\n- `checkedColor`: custom color for checked Text and Icon\n- `uncheckedColor`: custom color for unchecked Text and Icon\n- `onPress`: callback that is called when button is pressed\n- `label`: label text of the button\n- `showSelectedCheck`: show optional check icon to indicate selected state\n- `style`: pass additional styles for the button\n- `testID`: testID to be used on tests" + }, + "density": { + "required": false, + "tsType": { + "name": "union", + "raw": "'regular' | 'small' | 'medium' | 'high'", + "elements": [ + { + "name": "literal", + "value": "'regular'" + }, + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'high'" + } + ] + }, + "description": "Density is applied to the height, to allow usage in denser UIs" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/SegmentedButtons/SegmentedButtons.tsx" + ] + }, + "Snackbar": { + "filepath": "Snackbar.tsx", + "title": "Snackbar", + "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", + "link": "snackbar", + "data": { + "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "Snackbar", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether the Snackbar is currently visible." + }, + "action": { + "required": false, + "tsType": { + "name": "intersection", + "raw": "$RemoveChildren & {\n label: string;\n}", + "elements": [ + { + "name": "$RemoveChildren", + "elements": [ + { + "name": "Button" + } + ], + "raw": "$RemoveChildren" + }, + { + "name": "signature", + "type": "object", + "raw": "{\n label: string;\n}", + "signature": { + "properties": [ + { + "key": "label", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + ] + }, + "description": "Label and press callback for the action button. It should contain the following properties:\n- `label` - Label of the action button\n- `onPress` - Callback that is called when action button is pressed." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@supported Available in v5.x with theme version 3\nIcon to display when `onIconPress` is defined. Default will be `close` icon." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "@supported Available in v5.x with theme version 3\nColor of the ripple effect." + }, + "onIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "@supported Available in v5.x with theme version 3\nFunction to execute on icon button press. The icon button appears only when this prop is specified." + }, + "iconAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "@supported Available in v5.x with theme version 3\nAccessibility label for the icon button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'Close icon'", + "computed": false + } + }, + "duration": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The duration for which the Snackbar is shown.", + "defaultValue": { + "value": "7000", + "computed": false + } + }, + "onDismiss": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback called when Snackbar is dismissed. The `visible` prop needs to be updated when this is called." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text content of the Snackbar." + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges Snackbar shadow and background on iOS and Android.", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "wrapperStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the wrapper of the snackbar" + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the content of the snackbar" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Snackbar.tsx" + ] + }, + "Surface": { + "filepath": "Surface.tsx", + "title": "Surface", + "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", + "link": "surface", + "data": { + "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", + "displayName": "Surface", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Surface`." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges shadows and background on iOS and Android.\nUsed to create UI hierarchy between components.\n\nNote: If `mode` is set to `flat`, Surface doesn't have a shadow.\n\nNote: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`.\nIt's no longer supported with theme version 3 and you should use `elevation` property instead.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'elevated'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'elevated'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nMode of the Surface.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.", + "defaultValue": { + "value": "'elevated'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'surface'", + "computed": false + } + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "container": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@internal" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Surface.tsx" + ] + }, + "Switch/Switch": { + "filepath": "Switch/Switch.tsx", + "title": "Switch", + "description": "Switch is a visual toggle between two mutually exclusive states — on and off.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [isSwitchOn, setIsSwitchOn] = React.useState(false);\n\n const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn);\n\n return ;\n};\n\nexport default MyComponent;\n```", + "link": "switch", + "data": { + "description": "Switch is a visual toggle between two mutually exclusive states — on and off.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [isSwitchOn, setIsSwitchOn] = React.useState(false);\n\n const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn);\n\n return ;\n};\n\nexport default MyComponent;\n```", + "displayName": "Switch", + "methods": [], + "statics": [], + "props": { + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Disable toggling the switch." + }, + "value": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Value of the switch, true means 'on', false means 'off'." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for switch." + }, + "onValueChange": { + "required": false, + "tsType": { + "name": "Function" + }, + "description": "Callback called with the new value when it changes." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Switch/Switch.tsx" + ] + }, + "TextInput/TextInput": { + "filepath": "TextInput/TextInput.tsx", + "title": "TextInput", + "description": "A component to allow users to input text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState(\"\");\n\n return (\n setText(text)}\n />\n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", + "link": "text-input", + "data": { + "description": "A component to allow users to input text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState(\"\");\n\n return (\n setText(text)}\n />\n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", + "displayName": "TextInput", + "methods": [], + "statics": [], + "props": { + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'outlined'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'outlined'" + } + ] + }, + "description": "Mode of the TextInput.\n- `flat` - flat input with an underline.\n- `outlined` - input with an outline.\n\nIn `outlined` mode, the background color of the label is derived from `colors?.background` in theme or the `backgroundColor` style.\nThis component render TextInputOutlined or TextInputFlat based on that props", + "defaultValue": { + "value": "'flat'", + "computed": false + } + }, + "left": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "The adornment placed on the left side of the input. It can be either `TextInput.Icon` or `TextInput.Affix`." + }, + "right": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "The adornment placed on the right side of the input. It can be either `TextInput.Icon` or `TextInput.Affix`." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "If true, user won't be able to interact with the component.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "label": { + "required": false, + "tsType": { + "name": "TextInputLabelProp" + }, + "description": "The text or component to use for the floating label." + }, + "placeholder": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Placeholder for the input." + }, + "error": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to style the TextInput with error style.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "onChangeText": { + "required": false, + "tsType": { + "name": "Function" + }, + "description": "Callback that is called when the text input's text changes. Changed text is passed as an argument to the callback handler." + }, + "selectionColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Selection color of the input. On iOS, it sets both the selection color and cursor color.\nOn Android, it sets only the selection color." + }, + "cursorColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "@platform Android only\nCursor (or \"caret\") color of the input on Android.\nThis property has no effect on iOS." + }, + "underlineColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Inactive underline color of the input." + }, + "activeUnderlineColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Active underline color of the input." + }, + "outlineColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Inactive outline color of the input." + }, + "activeOutlineColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Active outline color of the input." + }, + "textColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Color of the text in the input." + }, + "dense": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Sets min height with densed layout. For `TextInput` in `flat` mode\nheight is `64dp` or in dense layout - `52dp` with label or `40dp` without label.\nFor `TextInput` in `outlined` mode\nheight is `56dp` or in dense layout - `40dp` regardless of label.\nWhen you apply `height` prop in style the `dense` prop affects only `paddingVertical` inside `TextInput`", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "multiline": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the input can have multiple lines.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "numberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@platform Android only\nThe number of lines to show in the input (Android only)." + }, + "onFocus": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(args: any) => void", + "signature": { + "arguments": [ + { + "name": "args", + "type": { + "name": "any" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the text input is focused." + }, + "onBlur": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(args: any) => void", + "signature": { + "arguments": [ + { + "name": "args", + "type": { + "name": "any" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the text input is blurred." + }, + "render": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: RenderProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "RenderProps" + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback to render a custom input component such as `react-native-text-input-mask`\ninstead of the default `TextInput` component from `react-native`.\n\nExample:\n```js\n\n \n }\n/>\n```", + "defaultValue": { + "value": "(props: RenderProps) => ", + "computed": false + } + }, + "value": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Value of the text input." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Pass `fontSize` prop to modify the font size inside `TextInput`.\nPass `height` prop to set `TextInput` height. When `height` is passed,\n`dense` prop will affect only input's `paddingVertical`.\nPass `paddingHorizontal` to modify horizontal padding.\nThis can be used to get MD Guidelines v1 TextInput look." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Pass custom style directly to the input itself.\nOverrides input style\nExample: `paddingLeft`, `backgroundColor`" + }, + "outlineStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Pass style to override the default style of outlined wrapper.\nOverrides style when mode is set to `outlined`\nExample: `borderRadius`, `borderColor`" + }, + "underlineStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Pass style to override the default style of underlined wrapper.\nOverrides style when mode is set to `flat`\nExample: `borderRadius`, `borderColor`" + }, + "editable": { + "defaultValue": { + "value": "true", + "computed": false + }, + "required": false, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/TextInput/TextInput.tsx" + ] + }, + "TextInput/Adornment/TextInputAffix": { + "filepath": "TextInput/Adornment/TextInputAffix.tsx", + "title": "TextInput.Affix", + "description": "A component to render a leading / trailing text in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", + "link": "text-input-affix", + "data": { + "description": "A component to render a leading / trailing text in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", + "displayName": "TextInput.Affix", + "methods": [], + "statics": [], + "props": { + "text": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Text to show." + }, + "onLayout": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(event: LayoutChangeEvent) => void", + "signature": { + "arguments": [ + { + "name": "event", + "type": { + "name": "LayoutChangeEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "" + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the affix. This is read by the screen reader when the user taps the affix.", + "defaultValue": { + "value": "text", + "computed": true + } + }, + "textStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the Text element." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/TextInput/Adornment/TextInputAffix.tsx" + ], + "group": "TextInput" + }, + "TextInput/Adornment/TextInputIcon": { + "filepath": "TextInput/Adornment/TextInputIcon.tsx", + "title": "TextInput.Icon", + "description": "A component to render a leading / trailing icon in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", + "link": "text-input-icon", + "data": { + "description": "A component to render a leading / trailing icon in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", + "displayName": "TextInput.Icon", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "@renamed Renamed from 'name' to 'icon` in v5.x\nIcon to show." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "forceTextInputFocus": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the TextInput will focus after onPress.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "union", + "raw": "((isTextInputFocused: boolean) => string | undefined) | string", + "elements": [ + { + "name": "unknown" + }, + { + "name": "string" + } + ] + }, + "description": "Color of the icon or a function receiving a boolean indicating whether the TextInput is focused and returning the color." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/TextInput/Adornment/TextInputIcon.tsx" + ], + "group": "TextInput" + }, + "ToggleButton/ToggleButton": { + "filepath": "ToggleButton/ToggleButton.tsx", + "title": "ToggleButton", + "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", + "link": "toggle-button", + "data": { + "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", + "displayName": "ToggleButton", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `ToggleButton`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the icon." + }, + "iconColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom text color for button." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value?: GestureResponderEvent | string) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "union", + "raw": "GestureResponderEvent | string", + "elements": [ + { + "name": "GestureResponderEvent" + }, + { + "name": "string" + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "value": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Value of button." + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of button." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ToggleButton/ToggleButton.tsx" + ] + }, + "ToggleButton/ToggleButtonGroup": { + "filepath": "ToggleButton/ToggleButtonGroup.tsx", + "title": "ToggleButton.Group", + "description": "Toggle group allows to control a group of toggle buttons.
\nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "toggle-button-group", + "data": { + "description": "Toggle group allows to control a group of toggle buttons.
\nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "ToggleButton.Group", + "methods": [], + "statics": [], + "props": { + "onValueChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: Value) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "Value" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "value": { + "required": true, + "tsType": { + "name": "union", + "raw": "Value | null", + "elements": [ + { + "name": "Value" + }, + { + "name": "null" + } + ] + }, + "description": "Value of the currently selected toggle button." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing toggle buttons." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ToggleButton/ToggleButtonGroup.tsx" + ], + "group": "ToggleButton" + }, + "ToggleButton/ToggleButtonRow": { + "filepath": "ToggleButton/ToggleButtonRow.tsx", + "title": "ToggleButton.Row", + "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", + "link": "toggle-button-row", + "data": { + "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", + "displayName": "ToggleButton.Row", + "methods": [], + "statics": [], + "props": { + "onValueChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: string) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the currently selected toggle button." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing toggle buttons." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ToggleButton/ToggleButtonRow.tsx" + ], + "group": "ToggleButton" + }, + "Tooltip/Tooltip": { + "filepath": "Tooltip/Tooltip.tsx", + "title": "Tooltip", + "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "link": "tooltip", + "data": { + "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Tooltip", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactElement", + "raw": "React.ReactElement" + }, + "description": "Tooltip reference element. Needs to be able to hold a ref." + }, + "enterTouchDelay": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before showing the tooltip.", + "defaultValue": { + "value": "500", + "computed": false + } + }, + "leaveTouchDelay": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds after the user stops touching an element before hiding the tooltip.", + "defaultValue": { + "value": "1500", + "computed": false + } + }, + "title": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Tooltip title" + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Tooltip/Tooltip.tsx" + ] + }, + "TouchableRipple/TouchableRipple": { + "filepath": "TouchableRipple/TouchableRipple.tsx", + "title": "TouchableRipple", + "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n
\n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", + "link": "touchable-ripple", + "data": { + "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n
\n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", + "displayName": "TouchableRipple", + "methods": [], + "statics": [], + "props": { + "borderless": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to render the ripple outside the view bounds.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "background": { + "required": false, + "tsType": { + "name": "Object" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "centered": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to start the ripple at the center (Web)." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to prevent interaction with the touchable." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press. If not set, will cause the touchable to be disabled." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute immediately when a touch is engaged, before `onPressOut` and `onPress`." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute when a touch is released." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect (Android >= 5.0 and Web)." + }, + "underlayColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Color of the underlay for the highlight effect (Android < 5.0 and iOS)." + }, + "children": { + "required": true, + "tsType": { + "name": "union", + "raw": "| ((state: PressableStateCallbackType) => React.ReactNode)\n| React.ReactNode", + "elements": [ + { + "name": "unknown" + }, + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + ] + }, + "description": "Content of the `TouchableRipple`." + }, + "style": { + "required": false, + "tsType": { + "name": "union", + "raw": "| StyleProp\n| ((state: PressableStateCallbackType) => StyleProp)\n| undefined", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + { + "name": "unknown" + }, + { + "name": "undefined" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/TouchableRipple/TouchableRipple.tsx" + ] + }, + "Typography/Text": { + "filepath": "Typography/Text.tsx", + "title": "Text", + "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", + "link": "text", + "data": { + "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", + "displayName": "Text", + "methods": [], + "statics": [], + "props": { + "variant": { + "required": false, + "tsType": { + "name": "VariantProp", + "elements": [ + { + "name": "T" + } + ], + "raw": "VariantProp" + }, + "description": "@supported Available in v5.x with theme version 3\n\nVariant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`" + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Typography/Text.tsx" + ] + } + } +} diff --git a/docs/versioned_docs/version-5.x/components/ActivityIndicator.mdx b/docs/versioned_docs/version-5.x/components/ActivityIndicator.mdx new file mode 100644 index 0000000000..8dcc3efb3c --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/ActivityIndicator.mdx @@ -0,0 +1,96 @@ +--- +title: ActivityIndicator +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Activity indicator is used to present progress of some activity in the app. +It can be used as a drop-in replacement for the ActivityIndicator shipped with React Native. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ActivityIndicator, MD2Colors } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### animating + +
+ + + +
+ +### color + +
+ + + +
+ +### size + +
+ + + +
+ +### hidesWhenStopped + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Appbar/Appbar.mdx b/docs/versioned_docs/version-5.x/components/Appbar/Appbar.mdx new file mode 100644 index 0000000000..fdc1adc079 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Appbar/Appbar.mdx @@ -0,0 +1,174 @@ +--- +title: Appbar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to display action items in a bar. It can be placed at the top or bottom. +The top bar usually contains the screen title, controls such as navigation buttons, menu button etc. +The bottom bar usually provides access to a drawer and up to four actions. + +By default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. +See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations + + + + + + + +## Usage +### Top bar +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} /> + + {}} /> + {}} /> + +); + +export default MyComponent; +``` + +### Bottom bar +```js +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { Appbar, FAB, useTheme } from 'react-native-paper'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +const BOTTOM_APPBAR_HEIGHT = 80; +const MEDIUM_FAB_HEIGHT = 56; + +const MyComponent = () => { + const { bottom } = useSafeAreaInsets(); + const theme = useTheme(); + + return ( + + {}} /> + {}} /> + {}} /> + {}} /> + {}} + style={[ + styles.fab, + { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 }, + ]} + /> + + ); +}; + +const styles = StyleSheet.create({ + bottom: { + backgroundColor: 'aquamarine', + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + }, + fab: { + position: 'absolute', + right: 16, + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dark + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### elevated Available in v5.x with theme version 3 + +
+ + + +
+ +### safeAreaInsets + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Appbar/AppbarAction.mdx b/docs/versioned_docs/version-5.x/components/Appbar/AppbarAction.mdx new file mode 100644 index 0000000000..4bece9c7d2 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Appbar/AppbarAction.mdx @@ -0,0 +1,142 @@ +--- +title: Appbar.Action +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display an action item in the appbar. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; +import { Platform } from 'react-native'; + +const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; + +const MyComponent = () => ( + + + {}} /> + {}} /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### color + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### disabled + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### isLeading Available in v5.x with theme version 3 + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Appbar/AppbarBackAction.mdx b/docs/versioned_docs/version-5.x/components/Appbar/AppbarBackAction.mdx new file mode 100644 index 0000000000..be22f972e8 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Appbar/AppbarBackAction.mdx @@ -0,0 +1,101 @@ +--- +title: Appbar.BackAction +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display a back button in the appbar. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### color + +
+ + + +
+ +### size + +
+ + + +
+ +### disabled + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Appbar/AppbarContent.mdx b/docs/versioned_docs/version-5.x/components/Appbar/AppbarContent.mdx new file mode 100644 index 0000000000..599f95020c --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Appbar/AppbarContent.mdx @@ -0,0 +1,145 @@ +--- +title: Appbar.Content +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display a title and optional subtitle in an appbar. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => ( + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### titleRef + +
+ + + +
+ +### subtitle Deprecated in v5.x + +
+ + + +
+ +### subtitleStyle Deprecated in v5.x + +
+ + + +
+ +### onPress + +
+ + + +
+ +### disabled + +
+ + + +
+ +### color + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Appbar/AppbarHeader.mdx b/docs/versioned_docs/version-5.x/components/Appbar/AppbarHeader.mdx new file mode 100644 index 0000000000..632bc299c8 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Appbar/AppbarHeader.mdx @@ -0,0 +1,125 @@ +--- +title: Appbar.Header +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to use as a header at the top of the screen. +It can contain the screen title, controls such as navigation buttons, menu button etc. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => { + const _goBack = () => console.log('Went back'); + + const _handleSearch = () => console.log('Searching'); + + const _handleMore = () => console.log('Shown more'); + + return ( + + + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dark + +
+ + + +
+ +### statusBarHeight + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### elevated Available in v5.x with theme version 3 + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Avatar/AvatarIcon.mdx b/docs/versioned_docs/version-5.x/components/Avatar/AvatarIcon.mdx new file mode 100644 index 0000000000..722ce6929b --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Avatar/AvatarIcon.mdx @@ -0,0 +1,81 @@ +--- +title: Avatar.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Avatars can be used to represent people in a graphical way. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar } from 'react-native-paper'; + +const MyComponent = () => ( + +); +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### color + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Avatar/AvatarImage.mdx b/docs/versioned_docs/version-5.x/components/Avatar/AvatarImage.mdx new file mode 100644 index 0000000000..a0e58a6c6d --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Avatar/AvatarImage.mdx @@ -0,0 +1,122 @@ +--- +title: Avatar.Image +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Avatars can be used to represent people in a graphical way. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar } from 'react-native-paper'; + +const MyComponent = () => ( + +); +export default MyComponent +``` + + + ## Props + + + + +
+ +### source (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### style + +
+ + + +
+ +### onError + +
+ + + +
+ +### onLayout + +
+ + + +
+ +### onLoad + +
+ + + +
+ +### onLoadEnd + +
+ + + +
+ +### onLoadStart + +
+ + + +
+ +### onProgress + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Avatar/AvatarText.mdx b/docs/versioned_docs/version-5.x/components/Avatar/AvatarText.mdx new file mode 100644 index 0000000000..372e41d230 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Avatar/AvatarText.mdx @@ -0,0 +1,97 @@ +--- +title: Avatar.Text +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Avatars can be used to represent people in a graphical way. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar } from 'react-native-paper'; + +const MyComponent = () => ( + +); +``` + + + ## Props + + + + +
+ +### label (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### color + +
+ + + +
+ +### style + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Badge.mdx b/docs/versioned_docs/version-5.x/components/Badge.mdx new file mode 100644 index 0000000000..564777604c --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Badge.mdx @@ -0,0 +1,96 @@ +--- +title: Badge +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Badges are small status descriptors for UI elements. +A badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Badge } from 'react-native-paper'; + +const MyComponent = () => ( + 3 +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### visible + +
+ + + +
+ +### children + +
+ + + +
+ +### size + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Banner.mdx b/docs/versioned_docs/version-5.x/components/Banner.mdx new file mode 100644 index 0000000000..08e79c42a3 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Banner.mdx @@ -0,0 +1,172 @@ +--- +title: Banner +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Banner displays a prominent message and related actions. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Image } from 'react-native'; +import { Banner } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(true); + + return ( + setVisible(false), + }, + { + label: 'Learn more', + onPress: () => setVisible(false), + }, + ]} + icon={({size}) => ( + + )}> + There was a problem processing a transaction on your credit card. + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### visible (required) + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### icon + +
+ + + +
+ +### actions + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + +
+ +### onShowAnimationFinished + +
+ + + +
+ +### onHideAnimationFinished + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigation.mdx b/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigation.mdx new file mode 100644 index 0000000000..600ae74fe5 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigation.mdx @@ -0,0 +1,316 @@ +--- +title: BottomNavigation +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar. +It is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar). + +By default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. +See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { BottomNavigation, Text } from 'react-native-paper'; + +const MusicRoute = () => Music; + +const AlbumsRoute = () => Albums; + +const RecentsRoute = () => Recents; + +const NotificationsRoute = () => Notifications; + +const MyComponent = () => { + const [index, setIndex] = React.useState(0); + const [routes] = React.useState([ + { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'}, + { key: 'albums', title: 'Albums', focusedIcon: 'album' }, + { key: 'recents', title: 'Recents', focusedIcon: 'history' }, + { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' }, + ]); + + const renderScene = BottomNavigation.SceneMap({ + music: MusicRoute, + albums: AlbumsRoute, + recents: RecentsRoute, + notifications: NotificationsRoute, + }); + + return ( + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### shifting + +
+ + + +
+ +### labeled + +
+ + + +
+ +### compact + +
+ + + +
+ +### navigationState (required) + +
+ + + +
+ +### onIndexChange (required) + +
+ + + +
+ +### renderScene (required) + +
+ + + +
+ +### renderIcon + +
+ + + +
+ +### renderLabel + +
+ + + +
+ +### renderTouchable + +
+ + + +
+ +### getAccessibilityLabel + +
+ + + +
+ +### getBadge + +
+ + + +
+ +### getColor + +
+ + + +
+ +### getLabelText + +
+ + + +
+ +### getLazy + +
+ + + +
+ +### getTestID + +
+ + + +
+ +### onTabPress + +
+ + + +
+ +### onTabLongPress + +
+ + + +
+ +### activeColor + +
+ + + +
+ +### inactiveColor + +
+ + + +
+ +### sceneAnimationEnabled + +
+ + + +
+ +### sceneAnimationType + +
+ + + +
+ +### sceneAnimationEasing + +
+ + + +
+ +### keyboardHidesNavigationBar + +
+ + + +
+ +### safeAreaInsets + +
+ + + +
+ +### barStyle + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### activeIndicatorStyle + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigationBar.mdx b/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigationBar.mdx new file mode 100644 index 0000000000..8859b3cfec --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigationBar.mdx @@ -0,0 +1,293 @@ +--- +title: BottomNavigation.Bar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/). + + + + + + + + + ## Usage +### without React Navigation +```js +import React from 'react'; +import { useState } from 'react'; +import { View } from 'react-native'; +import { BottomNavigation, Text, Provider } from 'react-native-paper'; +import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'; + +function HomeScreen() { + return ( + + Home! + + ); +} + +function SettingsScreen() { + return ( + + Settings! + + ); +} + +export default function MyComponent() { + const [index, setIndex] = useState(0); + + const routes = [ + { key: 'home', title: 'Home', icon: 'home' }, + { key: 'settings', title: 'Settings', icon: 'cog' }, + ]; + + const renderScene = ({ route }) => { + switch (route.key) { + case 'home': + return ; + case 'settings': + return ; + default: + return null; + } + }; + + return ( + + {renderScene({ route: routes[index] })} + { + const newIndex = routes.findIndex((r) => r.key === route.key); + if (newIndex !== -1) { + setIndex(newIndex); + } + }} + renderIcon={({ route, color }) => ( + + )} + getLabelText={({ route }) => route.title} + /> + + ); +} +``` + + ### with React Navigation + \n Home!\n
\n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst MyTabs = createBottomTabNavigator({\n screenOptions: {\n animation: 'shift',\n },\n tabBar: ({ navigation, state, descriptors, insets }) => (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n ),\n screens: {\n Home: {\n screen: HomeScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n Settings: {\n screen: SettingsScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n },\n});\n\nconst Navigation = createStaticNavigation(MyTabs);\n\nexport default function App() {\n return (\n \n \n \n \n \n );\n}","dynamic":"import { Text, View } from 'react-native';\nimport { NavigationContainer, CommonActions } from '@react-navigation/native';\nimport { createBottomTabNavigator } from '@react-navigation/bottom-tabs';\nimport { Provider, BottomNavigation } from 'react-native-paper';\nimport MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst Tab = createBottomTabNavigator();\n\nexport default function App() {\n return (\n \n \n (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n )}>\n (\n \n ),\n }}\n />\n (\n \n ),\n }}\n />\n \n \n \n );\n}\n"}}} /> + + + + ## Props + + + + +
+ +### shifting + +
+ + + +
+ +### labeled + +
+ + + +
+ +### compact + +
+ + + +
+ +### navigationState (required) + +
+ + + +
+ +### renderIcon + +
+ + + +
+ +### renderLabel + +
+ + + +
+ +### renderTouchable + +
+ + + +
+ +### getAccessibilityLabel + +
+ + + +
+ +### getBadge + +
+ + + +
+ +### getColor + +
+ + + +
+ +### getLabelText + +
+ + + +
+ +### getTestID + +
+ + + +
+ +### onTabPress (required) + +
+ + + +
+ +### onTabLongPress + +
+ + + +
+ +### activeColor + +
+ + + +
+ +### inactiveColor + +
+ + + +
+ +### animationEasing + +
+ + + +
+ +### keyboardHidesNavigationBar + +
+ + + +
+ +### safeAreaInsets + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### activeIndicatorStyle + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Button/Button.mdx b/docs/versioned_docs/version-5.x/components/Button/Button.mdx new file mode 100644 index 0000000000..8d9f7b8be0 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Button/Button.mdx @@ -0,0 +1,281 @@ +--- +title: Button +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A button is component that the user can press to trigger an action. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Button } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### mode + +
+ + + +
+ +### dark + +
+ + + +
+ +### compact + +
+ + + +
+ +### color Deprecated in v5.x - use `buttonColor` or `textColor` instead. + +
+ + + +
+ +### buttonColor + +
+ + + +
+ +### textColor + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### loading + +
+ + + +
+ +### icon + +
+ + + +
+ +### disabled + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### uppercase + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityHint + +
+ + + +
+ +### accessibilityRole + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### style + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### theme + +
+ + + +
+ +### touchableRef + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Card/Card.mdx b/docs/versioned_docs/version-5.x/components/Card/Card.mdx new file mode 100644 index 0000000000..b71067b67f --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Card/Card.mdx @@ -0,0 +1,172 @@ +--- +title: Card +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A card is a sheet of material that serves as an entry point to more detailed information. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar, Button, Card, Text } from 'react-native-paper'; + +const LeftContent = props => + +const MyComponent = () => ( + + + + Card title + Card content + + + + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### mode + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### disabled + +
+ + + +
+ +### elevation + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### accessible + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Card/CardActions.mdx b/docs/versioned_docs/version-5.x/components/Card/CardActions.mdx new file mode 100644 index 0000000000..c4e45b3053 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Card/CardActions.mdx @@ -0,0 +1,72 @@ +--- +title: Card.Actions +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a list of actions inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Card, Button } from 'react-native-paper'; + +const MyComponent = () => ( + + + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Card/CardContent.mdx b/docs/versioned_docs/version-5.x/components/Card/CardContent.mdx new file mode 100644 index 0000000000..360f352698 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Card/CardContent.mdx @@ -0,0 +1,64 @@ +--- +title: Card.Content +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show content inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Card, Text } from 'react-native-paper'; + +const MyComponent = () => ( + + + Card title + Card content + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Card/CardCover.mdx b/docs/versioned_docs/version-5.x/components/Card/CardCover.mdx new file mode 100644 index 0000000000..01c0f5cd08 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Card/CardCover.mdx @@ -0,0 +1,63 @@ +--- +title: Card.Cover +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a cover image inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Card } from 'react-native-paper'; + +const MyComponent = () => ( + + + +); + +export default MyComponent; +``` + + + + + ## Props + ### Image props + + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Card/CardTitle.mdx b/docs/versioned_docs/version-5.x/components/Card/CardTitle.mdx new file mode 100644 index 0000000000..a345dfc22b --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Card/CardTitle.mdx @@ -0,0 +1,176 @@ +--- +title: Card.Title +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a title, subtitle and an avatar inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar, Card, IconButton } from 'react-native-paper'; + +const MyComponent = () => ( + } + right={(props) => {}} />} + /> +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### titleNumberOfLines + +
+ + + +
+ +### titleVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### subtitle + +
+ + + +
+ +### subtitleStyle + +
+ + + +
+ +### subtitleNumberOfLines + +
+ + + +
+ +### subtitleVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### left + +
+ + + +
+ +### leftStyle + +
+ + + +
+ +### right + +
+ + + +
+ +### rightStyle + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### subtitleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Checkbox/Checkbox.mdx b/docs/versioned_docs/version-5.x/components/Checkbox/Checkbox.mdx new file mode 100644 index 0000000000..717b374aae --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Checkbox/Checkbox.mdx @@ -0,0 +1,108 @@ +--- +title: Checkbox +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Checkboxes allow the selection of multiple options from a set. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Checkbox } from 'react-native-paper'; + +const MyComponent = () => { + const [checked, setChecked] = React.useState(false); + + return ( + { + setChecked(!checked); + }} + /> + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### status (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxAndroid.mdx b/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxAndroid.mdx new file mode 100644 index 0000000000..e6ae65e251 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxAndroid.mdx @@ -0,0 +1,91 @@ +--- +title: Checkbox.Android +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + + + + + + + +Checkboxes allow the selection of multiple options from a set. +This component follows platform guidelines for Android, but can be used +on any platform. + + + + + ## Props + ### TouchableRipple props + + + +
+ +### status (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxIOS.mdx b/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxIOS.mdx new file mode 100644 index 0000000000..f3096f3564 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxIOS.mdx @@ -0,0 +1,83 @@ +--- +title: Checkbox.IOS +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + + + + + + + +Checkboxes allow the selection of multiple options from a set. +This component follows platform guidelines for iOS, but can be used +on any platform. + + + + + ## Props + ### TouchableRipple props + + + +
+ +### status (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxItem.mdx b/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxItem.mdx new file mode 100644 index 0000000000..0ea69cde34 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxItem.mdx @@ -0,0 +1,196 @@ +--- +title: Checkbox.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox. + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Checkbox } from 'react-native-paper'; + +const MyComponent = () => ( + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### status (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### label (required) + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### style + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### labelVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### position + +
+ + + +
+ +### mode + +
+ + + +
+ +### hitSlop + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Chip/Chip.mdx b/docs/versioned_docs/version-5.x/components/Chip/Chip.mdx new file mode 100644 index 0000000000..93d34e3b3f --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Chip/Chip.mdx @@ -0,0 +1,291 @@ +--- +title: Chip +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Chips are compact elements that can represent inputs, attributes, or actions. +They can have an icon or avatar on the left, and a close button icon on the right. +They are typically used to: +
    +
  • Present multiple options
  • +
  • Represent attributes active or chosen
  • +
  • Present filter options
  • +
  • Trigger actions related to primary content
  • +
+ + + + + + + +## Usage +```js +import * as React from 'react'; +import { Chip } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')}>Example Chip +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### mode + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### icon + +
+ + + +
+ +### avatar + +
+ + + +
+ +### closeIcon + +
+ + + +
+ +### selected + +
+ + + +
+ +### selectedColor + +
+ + + +
+ +### showSelectedOverlay Available in v5.x with theme version 3 + +
+ + + +
+ +### showSelectedCheck + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### disabled + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### closeIconAccessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### onClose + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### compact Available in v5.x with theme version 3 + +
+ + + +
+ +### elevated Available in v5.x with theme version 3 + +
+ + + +
+ +### textStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### ellipsizeMode + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### accessibilityRole + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/DataTable/DataTable.mdx b/docs/versioned_docs/version-5.x/components/DataTable/DataTable.mdx new file mode 100644 index 0000000000..5a6442ba4e --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/DataTable/DataTable.mdx @@ -0,0 +1,127 @@ +--- +title: DataTable +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Data tables allow displaying sets of data. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => { + const [page, setPage] = React.useState(0); + const [numberOfItemsPerPageList] = React.useState([2, 3, 4]); + const [itemsPerPage, onItemsPerPageChange] = React.useState( + numberOfItemsPerPageList[0] + ); + + const [items] = React.useState([ + { + key: 1, + name: 'Cupcake', + calories: 356, + fat: 16, + }, + { + key: 2, + name: 'Eclair', + calories: 262, + fat: 16, + }, + { + key: 3, + name: 'Frozen yogurt', + calories: 159, + fat: 6, + }, + { + key: 4, + name: 'Gingerbread', + calories: 305, + fat: 3.7, + }, + ]); + + const from = page * itemsPerPage; + const to = Math.min((page + 1) * itemsPerPage, items.length); + + React.useEffect(() => { + setPage(0); + }, [itemsPerPage]); + + return ( + + + Dessert + Calories + Fat + + + {items.slice(from, to).map((item) => ( + + {item.name} + {item.calories} + {item.fat} + + ))} + + setPage(page)} + label={`${from + 1}-${to} of ${items.length}`} + numberOfItemsPerPageList={numberOfItemsPerPageList} + numberOfItemsPerPage={itemsPerPage} + onItemsPerPageChange={onItemsPerPageChange} + showFastPaginationControls + selectPageDropdownLabel={'Rows per page'} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/DataTable/DataTableCell.mdx b/docs/versioned_docs/version-5.x/components/DataTable/DataTableCell.mdx new file mode 100644 index 0000000000..135b52485a --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/DataTable/DataTableCell.mdx @@ -0,0 +1,109 @@ +--- +title: DataTable.Cell +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a single cell inside of a table. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + 1 + 2 + 3 + 4 + +); + +export default MyComponent; +``` + +If you want to support multiline text, please use View instead, as multiline text doesn't comply with +MD Guidelines (https://github.com/callstack/react-native-paper/issues/2381). + + + + + ## Props + ### TouchableRipple props + + + +
+ +### children (required) + +
+ + + +
+ +### numeric + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### textStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/DataTable/DataTableHeader.mdx b/docs/versioned_docs/version-5.x/components/DataTable/DataTableHeader.mdx new file mode 100644 index 0000000000..e7739956e5 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/DataTable/DataTableHeader.mdx @@ -0,0 +1,77 @@ +--- +title: DataTable.Header +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to display title in table header. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + + + Dessert + + Calories + Fat (g) + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/DataTable/DataTablePagination.mdx b/docs/versioned_docs/version-5.x/components/DataTable/DataTablePagination.mdx new file mode 100644 index 0000000000..15f4c611fc --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/DataTable/DataTablePagination.mdx @@ -0,0 +1,211 @@ +--- +title: DataTable.Pagination +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show pagination for data table. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const numberOfItemsPerPageList = [2, 3, 4]; + +const items = [ + { + key: 1, + name: 'Page 1', + }, + { + key: 2, + name: 'Page 2', + }, + { + key: 3, + name: 'Page 3', + }, +]; + +const MyComponent = () => { + const [page, setPage] = React.useState(0); + const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]); + const from = page * numberOfItemsPerPage; + const to = Math.min((page + 1) * numberOfItemsPerPage, items.length); + + React.useEffect(() => { + setPage(0); + }, [numberOfItemsPerPage]); + + return ( + + setPage(page)} + label={`${from + 1}-${to} of ${items.length}`} + showFastPaginationControls + numberOfItemsPerPageList={numberOfItemsPerPageList} + numberOfItemsPerPage={numberOfItemsPerPage} + onItemsPerPageChange={onItemsPerPageChange} + selectPageDropdownLabel={'Rows per page'} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### page (required) + +
+ + + +
+ +### numberOfPages (required) + +
+ + + +
+ +### onPageChange (required) + +
+ + + +
+ +### showFastPaginationControls + +
+ + + +
+ +### paginationControlRippleColor + +
+ + + +
+ +### theme + +
+ + + +
+ +### numberOfItemsPerPage + +
+ + + +
+ +### numberOfItemsPerPageList + +
+ + + +
+ +### onItemsPerPageChange + +
+ + + +
+ +### dropdownItemRippleColor + +
+ + + +
+ +### selectPageDropdownRippleColor + +
+ + + +
+ +### selectPageDropdownLabel + +
+ + + +
+ +### selectPageDropdownAccessibilityLabel + +
+ + + +
+ +### label + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/DataTable/DataTableRow.mdx b/docs/versioned_docs/version-5.x/components/DataTable/DataTableRow.mdx new file mode 100644 index 0000000000..4a16b262d5 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/DataTable/DataTableRow.mdx @@ -0,0 +1,90 @@ +--- +title: DataTable.Row +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a single row inside of a table. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + 1 + 2 + 3 + 4 + +); + +export default MyComponent; +``` + + + + + ## Props + ### TouchableRipple props + + + +
+ +### children (required) + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### pointerEvents + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/DataTable/DataTableTitle.mdx b/docs/versioned_docs/version-5.x/components/DataTable/DataTableTitle.mdx new file mode 100644 index 0000000000..18eb652eae --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/DataTable/DataTableTitle.mdx @@ -0,0 +1,125 @@ +--- +title: DataTable.Title +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to display title in table header. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + + + Dessert + + Calories + Fat (g) + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### numeric + +
+ + + +
+ +### sortDirection + +
+ + + +
+ +### numberOfLines + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### textStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Dialog/Dialog.mdx b/docs/versioned_docs/version-5.x/components/Dialog/Dialog.mdx new file mode 100644 index 0000000000..dd8160a473 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Dialog/Dialog.mdx @@ -0,0 +1,136 @@ +--- +title: Dialog +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. +To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const showDialog = () => setVisible(true); + + const hideDialog = () => setVisible(false); + + return ( + + + + + + Alert + + This is simple dialog + + + + + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dismissable + +
+ + + +
+ +### dismissableBackButton + +
+ + + +
+ +### onDismiss + +
+ + + +
+ +### visible + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Dialog/DialogActions.mdx b/docs/versioned_docs/version-5.x/components/Dialog/DialogActions.mdx new file mode 100644 index 0000000000..3102b99ed5 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Dialog/DialogActions.mdx @@ -0,0 +1,80 @@ +--- +title: Dialog.Actions +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a list of actions in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Button, Dialog, Portal } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Dialog/DialogContent.mdx b/docs/versioned_docs/version-5.x/components/Dialog/DialogContent.mdx new file mode 100644 index 0000000000..4944c2fb83 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Dialog/DialogContent.mdx @@ -0,0 +1,71 @@ +--- +title: Dialog.Content +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show content in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + This is simple dialog + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Dialog/DialogIcon.mdx b/docs/versioned_docs/version-5.x/components/Dialog/DialogIcon.mdx new file mode 100644 index 0000000000..a1133c8dfd --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Dialog/DialogIcon.mdx @@ -0,0 +1,101 @@ +--- +title: Dialog.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +@supported Available in v5.x with theme version 3 +A component to show an icon in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + This is a title + + This is simple dialog + + + + ); +}; + +const styles = StyleSheet.create({ + title: { + textAlign: 'center', + }, +}) + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### color + +
+ + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Dialog/DialogScrollArea.mdx b/docs/versioned_docs/version-5.x/components/Dialog/DialogScrollArea.mdx new file mode 100644 index 0000000000..197c0f21a8 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Dialog/DialogScrollArea.mdx @@ -0,0 +1,87 @@ +--- +title: Dialog.ScrollArea +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a scrollable content in a Dialog. The component only provides appropriate styling. +For the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ScrollView } from 'react-native'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + + This is a scrollable area + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Dialog/DialogTitle.mdx b/docs/versioned_docs/version-5.x/components/Dialog/DialogTitle.mdx new file mode 100644 index 0000000000..ce5816a1d2 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Dialog/DialogTitle.mdx @@ -0,0 +1,80 @@ +--- +title: Dialog.Title +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a title in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + This is a title + + This is simple dialog + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Divider.mdx b/docs/versioned_docs/version-5.x/components/Divider.mdx new file mode 100644 index 0000000000..ffe25229b4 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Divider.mdx @@ -0,0 +1,93 @@ +--- +title: Divider +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A divider is a thin, lightweight separator that groups content in lists and page layouts. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Divider, Text } from 'react-native-paper'; + +const MyComponent = () => ( + + Lemon + + Mango + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### leftInset Renamed from 'inset' to 'leftInset` in v5.x + +
+ + + +
+ +### horizontalInset Available in v5.x with theme version 3 + +
+ + + +
+ +### bold Available in v5.x with theme version 3 + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Drawer/DrawerCollapsedItem.mdx b/docs/versioned_docs/version-5.x/components/Drawer/DrawerCollapsedItem.mdx new file mode 100644 index 0000000000..50cb419c33 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Drawer/DrawerCollapsedItem.mdx @@ -0,0 +1,149 @@ +--- +title: Drawer.CollapsedItem +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Note: Available in v5.x with theme version 3 + +Collapsed component used to show an action item with an icon and optionally label in a navigation drawer. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Drawer } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### label + +
+ + + +
+ +### badge + +
+ + + +
+ +### disabled + +
+ + + +
+ +### focusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x + +
+ + + +
+ +### unfocusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x + +
+ + + +
+ +### active + +
+ + + +
+ +### onPress + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Drawer/DrawerItem.mdx b/docs/versioned_docs/version-5.x/components/Drawer/DrawerItem.mdx new file mode 100644 index 0000000000..4c21ff2b33 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Drawer/DrawerItem.mdx @@ -0,0 +1,155 @@ +--- +title: Drawer.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to show an action item with an icon and a label in a navigation drawer. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Drawer } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### label (required) + +
+ + + +
+ +### icon + +
+ + + +
+ +### active + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### right + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Drawer/DrawerSection.mdx b/docs/versioned_docs/version-5.x/components/Drawer/DrawerSection.mdx new file mode 100644 index 0000000000..351e8bb5c9 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Drawer/DrawerSection.mdx @@ -0,0 +1,110 @@ +--- +title: Drawer.Section +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to group content inside a navigation drawer. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Drawer } from 'react-native-paper'; + +const MyComponent = () => { + const [active, setActive] = React.useState(''); + + return ( + + setActive('first')} + /> + setActive('second')} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### showDivider + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/FAB/AnimatedFAB.mdx b/docs/versioned_docs/version-5.x/components/FAB/AnimatedFAB.mdx new file mode 100644 index 0000000000..9cd723ce16 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/FAB/AnimatedFAB.mdx @@ -0,0 +1,284 @@ +--- +title: AnimatedFAB +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +An animated, extending horizontally floating action button represents the primary action in an application. + + + + + + + +## Usage +```js +import React from 'react'; +import { + StyleProp, + ViewStyle, + Animated, + StyleSheet, + Platform, + ScrollView, + Text, + SafeAreaView, + I18nManager, +} from 'react-native'; +import { AnimatedFAB } from 'react-native-paper'; + +const MyComponent = ({ + animatedValue, + visible, + extended, + label, + animateFrom, + style, + iconMode, +}) => { + const [isExtended, setIsExtended] = React.useState(true); + + const isIOS = Platform.OS === 'ios'; + + const onScroll = ({ nativeEvent }) => { + const currentScrollPosition = + Math.floor(nativeEvent?.contentOffset?.y) ?? 0; + + setIsExtended(currentScrollPosition <= 0); + }; + + const fabStyle = { [animateFrom]: 16 }; + + return ( + + + {[...new Array(100).keys()].map((_, i) => ( + {i} + ))} + + console.log('Pressed')} + visible={visible} + animateFrom={'right'} + iconMode={'static'} + style={[styles.fabStyle, style, fabStyle]} + /> + + ); +}; + +export default MyComponent; + +const styles = StyleSheet.create({ + container: { + flexGrow: 1, + }, + fabStyle: { + bottom: 16, + right: 16, + position: 'absolute', + }, +}); +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### label (required) + +
+ + + +
+ +### uppercase + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityState + +
+ + + +
+ +### color + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### disabled + +
+ + + +
+ +### visible + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### iconMode + +
+ + + +
+ +### animateFrom + +
+ + + +
+ +### extended + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### variant Available in v5.x with theme version 3 + +
+ + + +
+ +### style + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/FAB/FAB.mdx b/docs/versioned_docs/version-5.x/components/FAB/FAB.mdx new file mode 100644 index 0000000000..87844b9b2b --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/FAB/FAB.mdx @@ -0,0 +1,261 @@ +--- +title: FAB +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A floating action button represents the primary action on a screen. It appears in front of all screen content. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { FAB } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')} + /> +); + +const styles = StyleSheet.create({ + fab: { + position: 'absolute', + margin: 16, + right: 0, + bottom: 0, + }, +}) + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### icon + +
+ + + +
+ +### label + +
+ + + +
+ +### uppercase + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityState + +
+ + + +
+ +### animated + +
+ + + +
+ +### small Deprecated in v.5x - use prop size="small". + +
+ + + +
+ +### color + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### disabled + +
+ + + +
+ +### visible + +
+ + + +
+ +### loading + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### size Available in v5.x with theme version 3 + +
+ + + +
+ +### customSize + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### variant Available in v5.x with theme version 3 + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### ref + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/FAB/FABGroup.mdx b/docs/versioned_docs/version-5.x/components/FAB/FABGroup.mdx new file mode 100644 index 0000000000..56cbdc2799 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/FAB/FABGroup.mdx @@ -0,0 +1,244 @@ +--- +title: FAB.Group +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to display a stack of FABs with related actions in a speed dial. +To render the group above other components, you'll need to wrap it with the [`Portal`](../Portal) component. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { FAB, Portal, PaperProvider } from 'react-native-paper'; + +const MyComponent = () => { + const [state, setState] = React.useState({ open: false }); + + const onStateChange = ({ open }) => setState({ open }); + + const { open } = state; + + return ( + + + console.log('Pressed add') }, + { + icon: 'star', + label: 'Star', + onPress: () => console.log('Pressed star'), + }, + { + icon: 'email', + label: 'Email', + onPress: () => console.log('Pressed email'), + }, + { + icon: 'bell', + label: 'Remind', + onPress: () => console.log('Pressed notifications'), + }, + ]} + onStateChange={onStateChange} + onPress={() => { + if (open) { + // do something if the speed dial is open + } + }} + /> + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### actions (required) + +
+ + + +
+ +### icon (required) + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### color + +
+ + + +
+ +### backdropColor + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### toggleStackOnLongPress + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### enableLongPressWhenStackOpened + +
+ + + +
+ +### open (required) + +
+ + + +
+ +### onStateChange (required) + +
+ + + +
+ +### visible (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### fabStyle + +
+ + + +
+ +### variant Available in v5.x with theme version 3 + +
+ + + +
+ +### theme + +
+ + + +
+ +### label + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/HelperText/HelperText.mdx b/docs/versioned_docs/version-5.x/components/HelperText/HelperText.mdx new file mode 100644 index 0000000000..e62893bec8 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/HelperText/HelperText.mdx @@ -0,0 +1,127 @@ +--- +title: HelperText +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Helper text is used in conjuction with input elements to provide additional hints for the user. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { HelperText, TextInput } from 'react-native-paper'; + +const MyComponent = () => { + const [text, setText] = React.useState(''); + + const onChangeText = text => setText(text); + + const hasErrors = () => { + return !text.includes('@'); + }; + + return ( + + + + Email address is invalid! + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### type + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### visible + +
+ + + +
+ +### padding + +
+ + + +
+ +### disabled + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Icon.mdx b/docs/versioned_docs/version-5.x/components/Icon.mdx new file mode 100644 index 0000000000..4e0b46b75a --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Icon.mdx @@ -0,0 +1,95 @@ +--- +title: Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +An icon component which renders icon from vector library. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Icon, MD3Colors } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### size (required) + +
+ + + +
+ +### allowFontScaling + +
+ + + +
+ +### source (required) + +
+ + + +
+ +### color + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/IconButton/IconButton.mdx b/docs/versioned_docs/version-5.x/components/IconButton/IconButton.mdx new file mode 100644 index 0000000000..440a96bc59 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/IconButton/IconButton.mdx @@ -0,0 +1,190 @@ +--- +title: IconButton +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +An icon button is a button which displays only an icon without a label. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { IconButton, MD3Colors } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')} + /> +); + +export default MyComponent; +``` + + + + + ## Props + ### TouchableRipple props + + + +
+ +### icon (required) + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### iconColor Renamed from 'color' to 'iconColor' in v5.x + +
+ + + +
+ +### containerColor + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### selected Available in v5.x with theme version 3 + +
+ + + +
+ +### size + +
+ + + +
+ +### disabled + +
+ + + +
+ +### animated + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + +
+ +### loading + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/List/ListAccordion.mdx b/docs/versioned_docs/version-5.x/components/List/ListAccordion.mdx new file mode 100644 index 0000000000..fe07c5fa13 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/List/ListAccordion.mdx @@ -0,0 +1,273 @@ +--- +title: List.Accordion +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display an expandable list item. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List } from 'react-native-paper'; + +const MyComponent = () => { + const [expanded, setExpanded] = React.useState(true); + + const handlePress = () => setExpanded(!expanded); + + return ( + + }> + + + + + } + expanded={expanded} + onPress={handlePress}> + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### description + +
+ + + +
+ +### left + +
+ + + +
+ +### right + +
+ + + +
+ +### expanded + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### theme + +
+ + + +
+ +### background + +
+ + + +
+ +### style + +
+ + + +
+ +### containerStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### descriptionStyle + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### titleNumberOfLines + +
+ + + +
+ +### descriptionNumberOfLines + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### descriptionMaxFontSizeMultiplier + +
+ + + +
+ +### id + +
+ + + +
+ +### testID + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### pointerEvents + +
+ + + +
+ +### hitSlop + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/List/ListAccordionGroup.mdx b/docs/versioned_docs/version-5.x/components/List/ListAccordionGroup.mdx new file mode 100644 index 0000000000..412999538f --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/List/ListAccordionGroup.mdx @@ -0,0 +1,85 @@ +--- +title: List.AccordionGroup +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work. +List.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version. +At most one Accordion can be expanded at a given time. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { List } from 'react-native-paper'; + +const MyComponent = () => ( + + + + + + + + + + List.Accordion can be wrapped because implementation uses React.Context. + + + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### onAccordionPress + +
+ + + +
+ +### expandedId + +
+ + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/List/ListIcon.mdx b/docs/versioned_docs/version-5.x/components/List/ListIcon.mdx new file mode 100644 index 0000000000..a55b0b6ed4 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/List/ListIcon.mdx @@ -0,0 +1,79 @@ +--- +title: List.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show an icon in a list item. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List, MD3Colors } from 'react-native-paper'; + +const MyComponent = () => ( + <> + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### color + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/List/ListItem.mdx b/docs/versioned_docs/version-5.x/components/List/ListItem.mdx new file mode 100644 index 0000000000..05de6ad884 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/List/ListItem.mdx @@ -0,0 +1,193 @@ +--- +title: List.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show tiles inside a List. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List } from 'react-native-paper'; + +const MyComponent = () => ( + } + /> +); + +export default MyComponent; +``` + + + + + ## Props + ### TouchableRipple props + + + +
+ +### title (required) + +
+ + + +
+ +### description + +
+ + + +
+ +### left + +
+ + + +
+ +### right + +
+ + + +
+ +### onPress + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + +
+ +### containerStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### descriptionStyle + +
+ + + +
+ +### titleNumberOfLines + +
+ + + +
+ +### descriptionNumberOfLines + +
+ + + +
+ +### titleEllipsizeMode + +
+ + + +
+ +### descriptionEllipsizeMode + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### descriptionMaxFontSizeMultiplier + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/List/ListSection.mdx b/docs/versioned_docs/version-5.x/components/List/ListSection.mdx new file mode 100644 index 0000000000..da8154c360 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/List/ListSection.mdx @@ -0,0 +1,90 @@ +--- +title: List.Section +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to group list items. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List, MD3Colors } from 'react-native-paper'; + +const MyComponent = () => ( + + Some title + } /> + } + /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### theme + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/List/ListSubheader.mdx b/docs/versioned_docs/version-5.x/components/List/ListSubheader.mdx new file mode 100644 index 0000000000..a5f507ec34 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/List/ListSubheader.mdx @@ -0,0 +1,63 @@ +--- +title: List.Subheader +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display a header in lists. + + + + + +## Usage +```js +import * as React from 'react'; +import { List } from 'react-native-paper'; + +const MyComponent = () => My List Title; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Menu/Menu.mdx b/docs/versioned_docs/version-5.x/components/Menu/Menu.mdx new file mode 100644 index 0000000000..995ce12f46 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Menu/Menu.mdx @@ -0,0 +1,190 @@ +--- +title: Menu +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Button, Menu, Divider, PaperProvider } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const openMenu = () => setVisible(true); + + const closeMenu = () => setVisible(false); + + return ( + + + Show menu}> + {}} title="Item 1" /> + {}} title="Item 2" /> + + {}} title="Item 3" /> + + + + ); +}; + +export default MyComponent; +``` + +### Note +When using `Menu` within a React Native's `Modal` component, you need to wrap all +`Modal` contents within a `PaperProvider` in order for the menu to show. This +wrapping is not necessary if you use Paper's `Modal` instead. + + + ## Props + + + + +
+ +### visible (required) + +
+ + + +
+ +### anchor (required) + +
+ + + +
+ +### anchorPosition + +
+ + + +
+ +### statusBarHeight + +
+ + + +
+ +### onDismiss + +
+ + + +
+ +### overlayAccessibilityLabel + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### elevation + +
+ + + +
+ +### mode + +
+ + + +
+ +### theme + +
+ + + +
+ +### keyboardShouldPersistTaps + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Menu/MenuItem.mdx b/docs/versioned_docs/version-5.x/components/Menu/MenuItem.mdx new file mode 100644 index 0000000000..4a2cfcec58 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Menu/MenuItem.mdx @@ -0,0 +1,198 @@ +--- +title: Menu.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a single list item inside a Menu. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Menu } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} title="Redo" /> + {}} title="Undo" /> + {}} title="Cut" disabled /> + {}} title="Copy" disabled /> + {}} title="Paste" /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### leadingIcon Renamed from 'icon' to 'leadingIcon' in v5.x + +
+ + + +
+ +### trailingIcon Available in v5.x with theme version 3 + +
+ + + +
+ +### disabled + +
+ + + +
+ +### dense Available in v5.x with theme version 3 + +
+ + + +
+ +### background + +
+ + + +
+ +### onPress + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### containerStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### theme + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### testID + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityState + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Modal.mdx b/docs/versioned_docs/version-5.x/components/Modal.mdx new file mode 100644 index 0000000000..31684b84c0 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Modal.mdx @@ -0,0 +1,146 @@ +--- +title: Modal +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +The Modal component is a simple way to present content above an enclosing view. +To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component. +Note that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const showModal = () => setVisible(true); + const hideModal = () => setVisible(false); + const containerStyle = { backgroundColor: 'white', padding: 20 }; + + return ( + + + + Example Modal. Click outside this area to dismiss. + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dismissable + +
+ + + +
+ +### dismissableBackButton + +
+ + + +
+ +### onDismiss + +
+ + + +
+ +### overlayAccessibilityLabel + +
+ + + +
+ +### visible + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### contentContainerStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Portal/Portal.mdx b/docs/versioned_docs/version-5.x/components/Portal/Portal.mdx new file mode 100644 index 0000000000..9d3d325820 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Portal/Portal.mdx @@ -0,0 +1,74 @@ +--- +title: Portal +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Portal allows rendering a component at a different place in the parent tree. +You can use it to render content which should appear above other elements, similar to `Modal`. +It requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree. +Note that if you're using the `Provider` component, this already includes a `Portal.Host`. + + + + + +## Usage +```js +import * as React from 'react'; +import { Portal, Text } from 'react-native-paper'; + +const MyComponent = () => ( + + This is rendered at a different place + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### theme (required) + +
+ + + + + + + ## More Examples +
+ + + + + diff --git a/docs/versioned_docs/version-5.x/components/Portal/PortalHost.mdx b/docs/versioned_docs/version-5.x/components/Portal/PortalHost.mdx new file mode 100644 index 0000000000..f54c0b8609 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Portal/PortalHost.mdx @@ -0,0 +1,56 @@ +--- +title: Portal.Host +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Portal host renders all of its children `Portal` elements. +For example, you can wrap a screen in `Portal.Host` to render items above the screen. +If you're using the `Provider` component, it already includes `Portal.Host`. + + + + + +## Usage +```js +import * as React from 'react'; +import { Text } from 'react-native'; +import { Portal } from 'react-native-paper'; + +const MyComponent = () => ( + + Content of the app + +); + +export default MyComponent; +``` + +Here any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`. + + + ## Props + + + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/ProgressBar.mdx b/docs/versioned_docs/version-5.x/components/ProgressBar.mdx new file mode 100644 index 0000000000..fb920c538e --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/ProgressBar.mdx @@ -0,0 +1,119 @@ +--- +title: ProgressBar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Progress bar is an indicator used to present progress of some activity in the app. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ProgressBar, MD3Colors } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### animatedValue + +
+ + + +
+ +### progress + +
+ + + +
+ +### color + +
+ + + +
+ +### indeterminate + +
+ + + +
+ +### visible + +
+ + + +
+ +### fillStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/RadioButton/RadioButton.mdx b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButton.mdx new file mode 100644 index 0000000000..fcd29216fa --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButton.mdx @@ -0,0 +1,123 @@ +--- +title: RadioButton +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Radio buttons allow the selection a single option from a set. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { RadioButton } from 'react-native-paper'; + +const MyComponent = () => { + const [checked, setChecked] = React.useState('first'); + + return ( + + setChecked('first')} + /> + setChecked('second')} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### value (required) + +
+ + + +
+ +### status + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonAndroid.mdx b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonAndroid.mdx new file mode 100644 index 0000000000..e7a2fa46d8 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonAndroid.mdx @@ -0,0 +1,99 @@ +--- +title: RadioButton.Android +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + + + + + + + +Radio buttons allow the selection a single option from a set. +This component follows platform guidelines for Android, but can be used +on any platform. + + + + + ## Props + ### TouchableRipple props + + + +
+ +### value (required) + +
+ + + +
+ +### status + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonGroup.mdx b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonGroup.mdx new file mode 100644 index 0000000000..1ee266bf77 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonGroup.mdx @@ -0,0 +1,81 @@ +--- +title: RadioButton.Group +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Radio button group allows to control a group of radio buttons. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { RadioButton, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('first'); + + return ( + setValue(newValue)} value={value}> + + First + + + + Second + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### onValueChange (required) + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonIOS.mdx b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonIOS.mdx new file mode 100644 index 0000000000..25b8985a91 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonIOS.mdx @@ -0,0 +1,91 @@ +--- +title: RadioButton.IOS +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + + + + + + + +Radio buttons allow the selection a single option from a set. +This component follows platform guidelines for iOS, but can be used +on any platform. + + + + + ## Props + ### TouchableRipple props + + + +
+ +### value (required) + +
+ + + +
+ +### status + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonItem.mdx b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonItem.mdx new file mode 100644 index 0000000000..5dbb46c6e3 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/RadioButton/RadioButtonItem.mdx @@ -0,0 +1,210 @@ +--- +title: RadioButton.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { RadioButton } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('first'); + + return ( + setValue(value)} value={value}> + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### value (required) + +
+ + + +
+ +### label (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### background + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### status + +
+ + + +
+ +### style + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### labelVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### mode + +
+ + + +
+ +### position + +
+ + + +
+ +### hitSlop + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Searchbar.mdx b/docs/versioned_docs/version-5.x/components/Searchbar.mdx new file mode 100644 index 0000000000..38942b16ba --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Searchbar.mdx @@ -0,0 +1,256 @@ +--- +title: Searchbar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Searchbar is a simple input box where users can type search queries. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Searchbar } from 'react-native-paper'; + +const MyComponent = () => { + const [searchQuery, setSearchQuery] = React.useState(''); + + return ( + + ); +}; + +export default MyComponent; + +``` + + + ## Props + + + + +
+ +### placeholder + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### onChangeText + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### icon + +
+ + + +
+ +### iconColor + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### onIconPress + +
+ + + +
+ +### onClearIconPress + +
+ + + +
+ +### searchAccessibilityLabel + +
+ + + +
+ +### clearIcon + +
+ + + +
+ +### clearAccessibilityLabel + +
+ + + +
+ +### traileringIcon Available in v5.x with theme version 3 + +
+ + + +
+ +### traileringIconColor Available in v5.x with theme version 3 + +
+ + + +
+ +### traileringRippleColor Available in v5.x with theme version 3 + +
+ + + +
+ +### onTraileringIconPress Available in v5.x with theme version 3 + +
+ + + +
+ +### traileringIconAccessibilityLabel + +
+ + + +
+ +### right Available in v5.x with theme version 3 + +
+ + + +
+ +### showDivider Available in v5.x with theme version 3 + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### inputStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### loading + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/SegmentedButtons/SegmentedButtons.mdx b/docs/versioned_docs/version-5.x/components/SegmentedButtons/SegmentedButtons.mdx new file mode 100644 index 0000000000..0923129bc1 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/SegmentedButtons/SegmentedButtons.mdx @@ -0,0 +1,107 @@ +--- +title: SegmentedButtons +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Segmented buttons can be used to select options, switch views or sort elements. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { SafeAreaView, StyleSheet } from 'react-native'; +import { SegmentedButtons } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState(''); + + return ( + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### buttons (required) + +
+ + + +
+ +### density + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Snackbar.mdx b/docs/versioned_docs/version-5.x/components/Snackbar.mdx new file mode 100644 index 0000000000..779486da75 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Snackbar.mdx @@ -0,0 +1,226 @@ +--- +title: Snackbar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped. + +Note: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the "More Examples` section. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { Button, Snackbar } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const onToggleSnackBar = () => setVisible(!visible); + + const onDismissSnackBar = () => setVisible(false); + + return ( + + + { + // Do something + }, + }}> + Hey there! I'm a Snackbar. + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'space-between', + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### visible (required) + +
+ + + +
+ +### action + +
+ + + +
+ +### icon Available in v5.x with theme version 3 + +
+ + + +
+ +### rippleColor Available in v5.x with theme version 3 + +
+ + + +
+ +### onIconPress Available in v5.x with theme version 3 + +
+ + + +
+ +### iconAccessibilityLabel Available in v5.x with theme version 3 + +
+ + + +
+ +### duration + +
+ + + +
+ +### onDismiss (required) + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### wrapperStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + ## More Examples +
+ Toggle to grab more examples + +
+ + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Surface.mdx b/docs/versioned_docs/version-5.x/components/Surface.mdx new file mode 100644 index 0000000000..f350bf6480 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Surface.mdx @@ -0,0 +1,119 @@ +--- +title: Surface +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Surface is a basic container that can give depth to an element with elevation shadow. +On dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface. +See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. +Overlay and shadow can be applied by specifying the `elevation` property both on Android and iOS. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Surface, Text } from 'react-native-paper'; +import { StyleSheet } from 'react-native'; + +const MyComponent = () => ( + + Surface + +); + +export default MyComponent; + +const styles = StyleSheet.create({ + surface: { + padding: 8, + height: 80, + width: 80, + alignItems: 'center', + justifyContent: 'center', + }, +}); +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### ref + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Switch/Switch.mdx b/docs/versioned_docs/version-5.x/components/Switch/Switch.mdx new file mode 100644 index 0000000000..9040b1f50c --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Switch/Switch.mdx @@ -0,0 +1,95 @@ +--- +title: Switch +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Switch is a visual toggle between two mutually exclusive states — on and off. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Switch } from 'react-native-paper'; + +const MyComponent = () => { + const [isSwitchOn, setIsSwitchOn] = React.useState(false); + + const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn); + + return ; +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### disabled + +
+ + + +
+ +### value + +
+ + + +
+ +### color + +
+ + + +
+ +### onValueChange + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Text/Text.mdx b/docs/versioned_docs/version-5.x/components/Text/Text.mdx new file mode 100644 index 0000000000..b6e28003b6 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Text/Text.mdx @@ -0,0 +1,101 @@ +--- +title: Text +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Typography component showing styles complied with passed `variant` prop and supported by the type system. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Text } from 'react-native-paper'; + +const MyComponent = () => ( + <> + Display Large + Display Medium + Display small + + Headline Large + Headline Medium + Headline Small + + Title Large + Title Medium + Title Small + + Body Large + Body Medium + Body Small + + Label Large + Label Medium + Label Small + +); + +export default MyComponent; +``` + + + + + ## Props + ### Text props + + + +
+ +### variant Available in v5.x with theme version 3 + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/TextInput/TextInput.mdx b/docs/versioned_docs/version-5.x/components/TextInput/TextInput.mdx new file mode 100644 index 0000000000..a02528bdd6 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/TextInput/TextInput.mdx @@ -0,0 +1,301 @@ +--- +title: TextInput +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to allow users to input text. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { TextInput } from 'react-native-paper'; + +const MyComponent = () => { + const [text, setText] = React.useState(""); + + return ( + setText(text)} + /> + ); +}; + +export default MyComponent; +``` + + + + + ## Props + ### TextInput props + + + +
+ +### mode + +
+ + + +
+ +### left + +
+ + + +
+ +### right + +
+ + + +
+ +### disabled + +
+ + + +
+ +### label + +
+ + + +
+ +### placeholder + +
+ + + +
+ +### error + +
+ + + +
+ +### onChangeText + +
+ + + +
+ +### selectionColor + +
+ + + +
+ +### cursorColor Android only + +
+ + + +
+ +### underlineColor + +
+ + + +
+ +### activeUnderlineColor + +
+ + + +
+ +### outlineColor + +
+ + + +
+ +### activeOutlineColor + +
+ + + +
+ +### textColor + +
+ + + +
+ +### dense + +
+ + + +
+ +### multiline + +
+ + + +
+ +### numberOfLines Android only + +
+ + + +
+ +### onFocus + +
+ + + +
+ +### onBlur + +
+ + + +
+ +### render + +
+ + + +
+ +### value + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### outlineStyle + +
+ + + +
+ +### underlineStyle + +
+ + + +
+ +### editable + +
+ + + + + + + + + + + ## Known Issues + +
+ +
+ diff --git a/docs/versioned_docs/version-5.x/components/TextInput/TextInputAffix.mdx b/docs/versioned_docs/version-5.x/components/TextInput/TextInputAffix.mdx new file mode 100644 index 0000000000..74f1fc0434 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/TextInput/TextInputAffix.mdx @@ -0,0 +1,100 @@ +--- +title: TextInput.Affix +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to render a leading / trailing text in the TextInput + + + + + + + +## Usage +```js +import * as React from 'react'; +import { TextInput } from 'react-native-paper'; + +const MyComponent = () => { + const [text, setText] = React.useState(''); + + return ( + } + /> + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### text (required) + +
+ + + +
+ +### onLayout + +
+ + + +
+ +### onPress + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### textStyle + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/TextInput/TextInputIcon.mdx b/docs/versioned_docs/version-5.x/components/TextInput/TextInputIcon.mdx new file mode 100644 index 0000000000..703ffc298d --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/TextInput/TextInputIcon.mdx @@ -0,0 +1,107 @@ +--- +title: TextInput.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to render a leading / trailing icon in the TextInput + + + + + + + +## Usage +```js +import * as React from 'react'; +import { TextInput } from 'react-native-paper'; + +const MyComponent = () => { + const [text, setText] = React.useState(''); + + return ( + } + /> + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### icon (required) Renamed from 'name' to 'icon` in v5.x + +
+ + + +
+ +### onPress + +
+ + + +
+ +### forceTextInputFocus + +
+ + + +
+ +### color + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButton.mdx b/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButton.mdx new file mode 100644 index 0000000000..a2dedc9a06 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButton.mdx @@ -0,0 +1,162 @@ +--- +title: ToggleButton +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons, +a group should share a common container. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ToggleButton } from 'react-native-paper'; + +const ToggleButtonExample = () => { + const [status, setStatus] = React.useState('checked'); + + const onButtonToggle = value => { + setStatus(status === 'checked' ? 'unchecked' : 'checked'); + }; + + return ( + + ); +}; + +export default ToggleButtonExample; + +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### iconColor + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### disabled + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### value + +
+ + + +
+ +### status + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### ref + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonGroup.mdx b/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonGroup.mdx new file mode 100644 index 0000000000..8f5a4ab20f --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonGroup.mdx @@ -0,0 +1,78 @@ +--- +title: ToggleButton.Group +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Toggle group allows to control a group of toggle buttons. +It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow). + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ToggleButton } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('left'); + + return ( + setValue(value)} + value={value} + > + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### onValueChange (required) + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonRow.mdx b/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonRow.mdx new file mode 100644 index 0000000000..b326a1eb73 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/ToggleButton/ToggleButtonRow.mdx @@ -0,0 +1,83 @@ +--- +title: ToggleButton.Row +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Toggle button row renders a group of toggle buttons in a row. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ToggleButton } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('left'); + + return ( + setValue(value)} value={value}> + + + + ); +}; + +export default MyComponent; + +``` + + + ## Props + + + + +
+ +### onValueChange (required) + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/Tooltip/Tooltip.mdx b/docs/versioned_docs/version-5.x/components/Tooltip/Tooltip.mdx new file mode 100644 index 0000000000..18a2f43339 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/Tooltip/Tooltip.mdx @@ -0,0 +1,99 @@ +--- +title: Tooltip +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Tooltips display informative text when users hover over, focus on, or tap an element. + +Plain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { IconButton, Tooltip } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### enterTouchDelay + +
+ + + +
+ +### leaveTouchDelay + +
+ + + +
+ +### title (required) + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-5.x/components/TouchableRipple/TouchableRipple.mdx b/docs/versioned_docs/version-5.x/components/TouchableRipple/TouchableRipple.mdx new file mode 100644 index 0000000000..b53a4ee98c --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/TouchableRipple/TouchableRipple.mdx @@ -0,0 +1,157 @@ +--- +title: TouchableRipple +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A wrapper for views that should respond to touches. +Provides a material "ink ripple" interaction effect for supported platforms (>= Android Lollipop). +On unsupported platforms, it falls back to a highlight effect. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Text, TouchableRipple } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')} + rippleColor="rgba(0, 0, 0, .32)" + > + Press anywhere + +); + +export default MyComponent; +``` + + + + + ## Props + ### Pressable props + + + +
+ +### borderless + +
+ + + +
+ +### background + +
+ + + +
+ +### centered + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### underlayColor + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-5.x/components/_category_.json b/docs/versioned_docs/version-5.x/components/_category_.json new file mode 100644 index 0000000000..c1ea2a2a75 --- /dev/null +++ b/docs/versioned_docs/version-5.x/components/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Components" +} \ No newline at end of file diff --git a/docs/versioned_docs/version-5.x/guides/01-getting-started.md b/docs/versioned_docs/version-5.x/guides/01-getting-started.md new file mode 100644 index 0000000000..68071c4d25 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/01-getting-started.md @@ -0,0 +1,171 @@ +--- +title: Getting Started +--- + +# Getting Started + +## Installation + +- Open a Terminal in your project's folder and run: + +```bash npm2yarn +npm install react-native-paper +``` + +- From `v5` there is a need to install [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) for handling safe area. + +```bash npm2yarn +npm install react-native-safe-area-context +``` + +Additionaly for `iOS` platform there is a requirement to link the native parts of the library: + +```bash +npx pod-install +``` + +- If you're on a vanilla React Native project, you also need to install and link [@react-native-vector-icons/material-design-icons](https://github.com/oblador/react-native-vector-icons). + +Specifically `MaterialDesignIcons` icon pack needs to be included in the project, because some components use those internally (e.g. `AppBar.BackAction` on Android). + +```bash npm2yarn +npm install @react-native-vector-icons/material-design-icons +``` + +:::note +The `react-native-vector-icons` library requires some additional setup steps for each platform. To ensure proper use of icon fonts, please follow their [installation guide](https://github.com/oblador/react-native-vector-icons?tab=readme-ov-file#setup). +::: + +If you use Expo, you don't need to install vector icons - those are the part of the expo package. However, if you have a `babel.config.js` or `.babelrc` file, make sure that it includes `babel-preset-expo`. + +:::info +If you don't want to install vector icons, you can use [babel-plugin-optional-require](https://github.com/satya164/babel-plugin-optional-require) to opt-out. +:::note + +### Bundle size optimization + +To get smaller bundle size by excluding modules you don't use, you can use our optional babel plugin. The plugin automatically rewrites the import statements so that only the modules you use are imported instead of the whole library. Add `react-native-paper/babel` to the `plugins` section in your `babel.config.js` for production environment. It should look like this: + +```js +module.exports = { + presets: ['module:metro-react-native-babel-preset'], + env: { + production: { + plugins: ['react-native-paper/babel'], + }, + }, +}; +``` + +If you created your project using Expo, it'll look something like this: + +```js +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + env: { + production: { + plugins: ['react-native-paper/babel'], + }, + }, + }; +}; +``` + +The plugin only works if you are importing the library using ES2015 import statements and not with `require`. + +:::note +The above examples are for the latest `react-native` using Babel 7. If you have `react-native <= 0.55`, you'll have a `.babelrc` file instead of a `babel.config.js` file and the content of the file will be different. +::: + +If you're using Flow for typechecking your code, you need to add the following under the `[options]` section in your `.flowconfig`: + +```ini +module.file_ext=.js +module.file_ext=.native.js +module.file_ext=.android.js +module.file_ext=.ios.js +``` + +## Usage + +Wrap your root component in `PaperProvider` from `react-native-paper`(if you are using versions prior to 5.8.0 you need to use `Provider`). If you have a vanilla React Native project, it's a good idea to add it in the component which is passed to `AppRegistry.registerComponent`. This will usually be in the `index.js` file. If you have an Expo project, you can do this inside the exported component in the `App.js` file. + +Example: + +```js +import * as React from 'react'; +import { AppRegistry } from 'react-native'; +import { PaperProvider } from 'react-native-paper'; +import { name as appName } from './app.json'; +import App from './src/App'; + +export default function Main() { + return ( + + + + ); +} + +AppRegistry.registerComponent(appName, () => Main); +``` + +The `PaperProvider` component provides the theme to all the components in the framework. It also acts as a portal to components which need to be rendered at the top level. + +If you have another provider (such as `Redux`), wrap it outside `PaperProvider` so that the context is available to components rendered inside a `Modal` from the library: + +```js +import * as React from 'react'; +import { PaperProvider } from 'react-native-paper'; +import { Provider as StoreProvider } from 'react-redux'; +import App from './src/App'; +import store from './store'; + +export default function Main() { + return ( + + + + + + ); +} +``` + +## Customization + +You can provide a custom theme to customize the colors, typescales etc. with the `Provider` component. Check the [Material Design 3 default theme](https://github.com/callstack/react-native-paper/blob/main/src/styles/themes/v3/LightTheme.tsx) to see what customization options are supported. + +Example: + +```js +import * as React from 'react'; +import { + MD3LightTheme as DefaultTheme, + PaperProvider, +} from 'react-native-paper'; +import App from './src/App'; + +const theme = { + ...DefaultTheme, + colors: { + ...DefaultTheme.colors, + primary: 'tomato', + secondary: 'yellow', + }, +}; + +export default function Main() { + return ( + + + + ); +} +``` + +:::note +For MD2 check the following [Material Design 2 default theme](https://github.com/callstack/react-native-paper/blob/main/src/styles/themes/v2/LightTheme.tsx). +::: diff --git a/docs/versioned_docs/version-5.x/guides/02-theming.mdx b/docs/versioned_docs/version-5.x/guides/02-theming.mdx new file mode 100644 index 0000000000..53bb236889 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/02-theming.mdx @@ -0,0 +1,639 @@ +--- +title: Theming +--- + +import DynamicColorTheme from '@site/src/components/DynamicColorTheme.tsx'; + +# Theming + +:::note +To observe changes related to switching between light and dark mode in the app, ensure that the "Override force-dark" feature in the "developer options" settings on your Android device is not overridden. +::: + +## Applying a theme to the whole app + +To support custom themes, paper exports a `PaperProvider` component. You need to wrap your root component with the provider to be able to support themes: + +```js +import * as React from 'react'; +import { PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +export default function Main() { + return ( + + + + ); +} +``` + +By default React Native Paper will apply the [Material You theme (MD3)](https://github.com/callstack/react-native-paper/blob/main/src/styles/themes/v3/LightTheme.tsx) if no `theme` or `version` prop is passed to the `PaperProvider`. + +## Accessing theme properties + +Use the built-in `useTheme()` hook to get access to the theme's variables: + +```js +import * as React from 'react'; +import { useTheme } from 'react-native-paper'; + +export default function PaymentScreen() { + const theme = useTheme(); + + return ; +} +``` + +You can also use the `withTheme()` HOC exported from the library. If you wrap your component with the HOC, you'll receive the theme as a prop: + +```js +import * as React from 'react'; +import { withTheme } from 'react-native-paper'; + +function PaymentScreen({ theme }) { + return ; +} + +export default withTheme(PaymentScreen); +``` + +## Theme properties + +You can change the theme prop dynamically and all the components will automatically update to reflect the new theme. + +A theme usually contains the following properties: + +- `dark` (`boolean`): whether this is a dark theme or light theme. +- `version`: specify which design system components should follow in the app + - 3 - new Material You (MD3) + - 2 - previous Material Design (MD2) +- `mode` (`'adaptive' | 'exact'`): color mode for dark theme (See [Dark Theme](#dark-theme)). +- `roundness` (`number`): roundness of common elements, such as buttons. +- `colors` (`object`): various colors used throughout different elements. + + > The primary key color is used to derive roles for key components across the UI, such as the FAB, prominent buttons, active states, as well as the tint of elevated surfaces. + + - `primary` + - `onPrimary` + - `primaryContainer` + - `onPrimaryContainer` + + > The secondary key color is used for less prominent components in the UI such as filter chips, while expanding the opportunity for color expression. + + - `secondary` + - `onSecondary` + - `secondaryContainer` + - `onSecondaryContainer` + + > The tertiary key color is used to derive the roles of contrasting accents that can be used to balance primary and secondary colors or bring heightened attention to an element. + + > The tertiary color role is left for teams to use at their discretion and is intended to support broader color expression in products. + + - `tertiary` + - `onTertiary` + - `tertiaryContainer` + - `onTertiaryContainer` + + > The neutral key color is used to derive the roles of surface and background, as well as high emphasis text and icons. + + - `background` + - `onBackground` + - `surface` + - `onSurface` + + > The neutral variant key color is used to derive medium emphasis text and icons, surface variants, and component outlines. + + - `surfaceVariant` + - `onSurfaceVariant` + - `outline` + + > In addition to the accent and neutral key color, the color system includes a semantic color role for error + + - `error` + - `onError` + - `errorContainer` + - `onErrorContainer` + + > Surfaces at elevation levels 0-5 are tinted via color overlays based on the primary color, such as app bars or menus. The addition of a grade from 0-5 introduces tonal variation to the surface baseline. + + - `elevation` (`object`) + - `level0` - transparent + - `level1` - 5% opacity + - `level2` - 8% opacity + - `level3` - 11% opacity + - `level4` - 12% opacity + - `level5` - 14% opacity + + > Colors for disabled state + + - `surfaceDisabled` + - `onSurfaceDisabled` + + > These additional role mappings exist in a scheme and are mapped to components where needed. + + - `shadow` + - `inverseOnSurface` + - `inverseSurface` + - `inversePrimary` + - `backdrop` + +- `fonts` (`object`): various fonts styling properties under the text variant key used in component. + - [`variant` e.g. `labelMedium`] (`object`): + - `fontFamily` + - `letterSpacing` + - `fontWeight` + - `lineHeight` + - `fontSize` +- `animation` (`object`) + - `scale` - scale for all animations + +When creating a custom theme, you will need to provide all of these properties. + +If you don't use a custom theme, Paper will automatically turn animations on/off, depending on device settings. + +Otherwise, your custom theme will need to handle it manually, using React Native's [AccessibilityInfo API](https://reactnative.dev/docs/accessibilityinfo). + +## Extending the theme + +Keeping your own properties in the theme is fully supported by our library: + +```js +import * as React from 'react'; +import { + MD3LightTheme as DefaultTheme, + PaperProvider, +} from 'react-native-paper'; +import App from './src/App'; + +const theme = { + ...DefaultTheme, + // Specify custom property + myOwnProperty: true, + // Specify custom property in nested object + colors: { + ...DefaultTheme.colors, + myOwnColor: '#BADA55', + }, +}; + +export default function Main() { + return ( + + + + ); +} +``` + +## Creating dynamic theme colors + +Dynamic Color Themes allows for generating two color schemes lightScheme and darkScheme, based on the provided source color. +Created schemes are following the Material Design 3 color system and covering colors structure from the Paper theme. User may generate these schemes using the following tool: + + + +
+ +Passed source color into the util is translated into tones to automatically provide the range of tones that map to color roles. + +![customColors](../../../static/screenshots/custom-colors.png) + +_Source: [Material You Color System](https://m3.material.io/styles/color/the-color-system/custom-colors)_ + +### Using schemes + +Once we have copied the color schemes from the generated JSON above, we can use by passing it to the colors in `theme` object as below: + +```jsx +import * as React from 'react'; +import { + MD3LightTheme as DefaultTheme, + PaperProvider, +} from 'react-native-paper'; +import App from './src/App'; + +const theme = { + ...DefaultTheme, + colors: yourGeneratedLightOrDarkScheme.colors, // Copy it from the color codes scheme and then use it here +}; + +export default function Main() { + return ( + + + + ); +} +``` + +### Sync dynamic colors with system colors + +Using [`pchmn/expo-material3-theme`](https://github.com/pchmn/expo-material3-theme) library you can easily access the Material 3 system colors from Android 12+ devices and seamlessly integrate them into your dynamic theme. Any changes made by the user to the system colors will be automatically reflected in the theme. + +:::info +In case of incompatible devices, the library will revert to a default theme. +::: + +To get started, follow the [installation instructions](https://github.com/pchmn/expo-material3-theme#installation) and check the following code: + +```tsx +import { useMaterial3Theme } from '@pchmn/expo-material3-theme'; +import { useColorScheme } from 'react-native'; +import { MD3DarkTheme, MD3LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +export default function Main() { + const colorScheme = useColorScheme(); + const { theme } = useMaterial3Theme(); + + const paperTheme = + colorScheme === 'dark' + ? { ...MD3DarkTheme, colors: theme.dark } + : { ...MD3LightTheme, colors: theme.light }; + + return ( + + + + ); +} +``` + +## Adapting React Navigation theme + +The `adaptNavigationTheme` function takes an existing React Navigation theme and returns a React Navigation theme using the colors from Material Design 3. This theme can be passed to `NavigationContainer` so that React Navigation's UI elements have the same color scheme as Paper. + +```ts +adaptNavigationTheme(themes); +``` + +:::info +For users of `react-navigation` version `7.0.0` and above, `adaptNavigationTheme` overrides the **fonts** from the navigation theme as follows: + +```ts +fonts: { + regular: { + fontFamily: materialTheme.fonts.bodyMedium.fontFamily, + fontWeight: materialTheme.fonts.bodyMedium.fontWeight, + letterSpacing: materialTheme.fonts.bodyMedium.letterSpacing, + }, + medium: { + fontFamily: materialTheme.fonts.titleMedium.fontFamily, + fontWeight: materialTheme.fonts.titleMedium.fontWeight, + letterSpacing: materialTheme.fonts.titleMedium.letterSpacing, + }, + bold: { + fontFamily: materialTheme.fonts.headlineSmall.fontFamily, + fontWeight: materialTheme.fonts.headlineSmall.fontWeight, + letterSpacing: materialTheme.fonts.headlineSmall.letterSpacing, + }, + heavy: { + fontFamily: materialTheme.fonts.headlineLarge.fontFamily, + fontWeight: materialTheme.fonts.headlineLarge.fontWeight, + letterSpacing: materialTheme.fonts.headlineLarge.letterSpacing, + }, +} +``` + +::: + +Parameters: + +| NAME | TYPE | +| ------ | ------ | +| themes | object | + +Valid `themes` keys are: + +- `reactNavigationLight` () - React Navigation compliant light theme. +- `reactNavigationDark` () - React Navigation compliant dark theme. +- `materialLight` () - React Native Paper compliant light theme. +- `materialDark` () - React Native Paper compliant dark theme. + +```ts +// App.tsx +import { NavigationContainer, DefaultTheme } from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; +import { + PaperProvider, + MD3LightTheme, + adaptNavigationTheme, +} from 'react-native-paper'; +const Stack = createStackNavigator(); +const { LightTheme } = adaptNavigationTheme({ + reactNavigationLight: DefaultTheme, +}); +export default function App() { + return ( + + + + + + + + + ); +} +``` + +## TypeScript + +By default, TypeScript works well whenever you change the value of the built-in theme's properties. It gets more complicated when you want to extend the theme's properties or change their types. In order to fully support TypeScript, you will need to follow the guide that fits your use-case most accurately: + +There are two supported ways of overriding the theme: + +1. Simple built-in theme overrides - when you only customize the values and + the whole theme schema remains the same +2. Advanced theme overrides - when you add new properties or + change the built-in schema shape + + +:::caution +TypeScript support for `withTheme` is currently limited to Material You (MD3) theme only. + + + We are planning to provide a better support of handling custom theme overrides + in future releases. + +::: + +### Simple built-in theme overrides + +You can provide a `theme` prop with a theme object with the same properties as the default theme: + +```js +import * as React from 'react'; +import { MD3LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +const theme = { + ...MD3LightTheme, // or MD3DarkTheme + roundness: 2, + colors: { + ...MD3LightTheme.colors, + primary: '#3498db', + secondary: '#f1c40f', + tertiary: '#a1b2c3', + }, +}; + +export default function Main() { + return ( + + + + ); +} +``` + +### Advanced theme overrides + +If you need to modify the built-in theme schema by adding a new property or changing its type, you need to follow these steps: + +1. Pass your theme overrides to the PaperProvider component + +```ts +import * as React from 'react'; +import { MD3LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +const theme = { + ...MD3LightTheme, + + // Specify a custom property + custom: 'property', + + // Specify a custom property in nested object + colors: { + ...MD3LightTheme.colors, + brandPrimary: '#fefefe', + brandSecondary: 'red', + }, +}; + +export default function Main() { + return ( + + + + ); +} +``` + +2. Create a typed `useAppTheme()` hook in your project + +```ts +import * as React from 'react'; +import { MD3LightTheme, PaperProvider, useTheme } from 'react-native-paper'; +import App from './src/App'; + +const theme = { + ...MD3LightTheme, + + // Specify a custom property + custom: 'property', + + // Specify a custom property in nested object + colors: { + ...MD3LightTheme.colors, + brandPrimary: '#fefefe', + brandSecondary: 'red', + }, +}; + +export type AppTheme = typeof theme; + +export const useAppTheme = () => useTheme(); + +export default function Main() { + return ( + + + + ); +} +``` + +3. Start using the `useAppTheme()` hook across your components in the whole app + +```ts +import * as React from 'react'; +import { useAppTheme } from './App'; + +export default function HomeScreen() { + const { + colors: { brandPrimary }, + } = useAppTheme(); + + return ...; +} +``` + +## Material Design 2 + +Using Material Design 2 is fully supported in React Native Paper v5.x. + +### Simple setup + +In order to use the Material Design 2 theme you can just pass `{ version: 2 }` to the PaperProvider theme prop: + +```js +import * as React from 'react'; +import { PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +export default function Main() { + return ( + + + + ); +} +``` + +Specifying `{ version: 2 }` tells React Native Paper to use the built in Material Design 2 theme, so you don't have to fully extend it on your own. + +### Advanced setup + +As with any theme, you can also specify your custom properties within the Material Design 2 theme: + +```js +import * as React from 'react'; +import { MD2LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +export default function Main() { + const theme = { + ...MD2LightTheme, + + // Specify a custom property + custom: 'property', + + // Specify a custom nested property + colors: { + ...MD2LightTheme.colors, + primary: '#fefefe', + }, + }; + + return ( + + + + ); +} +``` + +### Typescript + +Due to the amount of changes in the theme's schema shape it falls into the [Advanced theme overrides](#advanced-theme-overrides) category. The steps are identical as with any advanced theme, just make sure to extend the built-in `MD2LightTheme` or `MD2DarkTheme` instead of `MD3LightTheme` or `MD3DarkTheme`. + +The final example for Material Design 2 would look like this: + +```ts +import * as React from 'react'; +import { MD2LightTheme, PaperProvider, useTheme } from 'react-native-paper'; +import App from './src/App'; + +const theme = { + // Extend Material Design 2 theme + + ...MD2LightTheme, // or MD2DarkTheme + + // Specify a custom property + myOwnProperty: true, + + // Specify a custom nested property + colors: { + ...MD2LightTheme.colors, + myOwnColor: '#BADA55', + }, +}; + +export type AppTheme = typeof theme; + +export const useAppTheme = () => useTheme(); + +export default function Main() { + return ( + + + + ); +} + +// App.tsx + +export default function App() { + const { theme } = useAppTheme(); + + return ; +} +``` + +### Migrating to Material You + +If you are migrating from Material Design 2 (4.x and lower) to Material You (5.x), please refer to our [Migration Guide](https://callstack.github.io/react-native-paper/docs/guides/migration-guide-to-5.0) + +## Applying a theme to a paper component + +If you want to change the theme for a certain component from the library, you can directly pass the `theme` prop to the component. The theme passed as the prop is merged with the theme from the `PaperProvider`: + +```js +import * as React from 'react'; +import { Button } from 'react-native-paper'; + +export default function ButtonExample() { + return ( + + ); +} +``` + +## Customizing all instances of a component + +Sometimes you want to style a component in a different way everywhere, but don't want to change the properties in the theme, so that other components are not affected. For example, say you want to change the font for all your buttons, but don't want to change `theme.fonts.labelLarge` because it affects other components. + +We don't have an API to do this, because you can already do it with components: + +```js +import * as React from 'react'; +import { Button } from 'react-native-paper'; + +export default function FancyButton(props) { + return ( + +``` + +:::note +Choose the correct tab below based on your `react-native-vector-icons` version: + +- Use **latest** tab if you're using the latest version of `@react-native-vector-icons/material-design-icons` +- Use **10.x** tab if you're still using the old single package `react-native-vector-icons`, version `10.x` + +If you are using `@expo/vector-icons`, please check their [searchable list of icons](https://icons.expo.fyi/). +::: + + + +
+ See the list of supported icons + +
+
+ +
+ See the list of supported icons + +
+
+
+ +### 2. An image source + +You can pass an image source, such as an object of shape `{ uri: 'https://path.to' }` or a local image: `require('../path/to/image.png')` to use as an icon. The image might be rendered with a different color than the one provided depending on the component. If don't want this behavior, see the next example to pass an `Image` element. + +Remote image: + +```js + +``` + +Local image: + +```js + +``` + +### 3. A render function + +You can pass a function which returns a react element to be used an icon. The function receives an object with `size` and `color` properties as its arguments. The element is used as is without any modification. However, it might get clipped if the provided element's size is bigger than what the component renders. It's up to you to make sure that the size of the element is correct. + +Example: + +```js + +``` + +### 4. Use custom icons + +If you want to use icons other than `MaterialDesignIcons` you need to import the icons and pass it to the `settings` prop within `PaperProvider`. + +Example: + +```js +import AwesomeIcon from '@react-native-vector-icons/fontawesome'; +// ... + + , + }} + theme={this.state.theme} +> + // ... +; +``` + +## RTL support + +If you want your icon to behave properly in a RTL environment, you can pass an object to the `icon` prop with shape: `{ source: { uri: 'https://path.to' }, direction : 'rtl' }`. `source` can be any of the values that the `icon` prop accepts in [option 1](#1.-an-icon-name) and [option 2](#2.-an-image-source). For `direction` you have a few options: + +1. `auto` - uses the device language to determine if icon should be displayed from rtl. Uses the `I18nManager` module to get this info. +2. `rtl` - flips the icon so that it is rtl, this is regardless of the device language. +3. `ltr` - displays from ltr, even if in an rtl environment. + +Example for using an image source: + +```js + +``` + +Example for using an icon name: + +```js + +``` + +You can also use a render function. Along with `size` and `color`, you have access to `direction` which will either be `'rtl'` or `'ltr'`. You can then decide how to render your icon component accordingly. + +Example of using a render function: + +```js + +``` diff --git a/docs/versioned_docs/version-5.x/guides/04-fonts.md b/docs/versioned_docs/version-5.x/guides/04-fonts.md new file mode 100644 index 0000000000..60d3c6e457 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/04-fonts.md @@ -0,0 +1,527 @@ +--- +title: Fonts +--- + +# Fonts + +## Installing custom fonts + +The easiest way to install custom fonts to your RN project is do as follows: + + 1. Define path to assets directory with fonts in project: + + Example: + + ```js + module.exports = { + ... + assets: [ + './assets/fonts' + ], + ``` + +:::note +`fonts` is a folder with `.ttf` files +::: + + 2. Place your font files in your assets directory. + + 3. Link font files, using the following command in the terminal: + + * React Native `>= 0.69`: + + ```sh + npx react-native-asset + ``` + + * React Native `< 0.69`: + + ```sh + npx react-native link + ``` + + + 4. Restart your project to refresh changes. + +Now, you are able to use `fontFamily` from font files. + +## Configuring fonts in ThemeProvider + +### Material Design 2 + +#### Using `configureFonts` helper + +To create a custom font, prepare a `fontConfig` object where fonts are divided by platforms. After that, you have to: + +* pass the `fontConfig` into `configureFonts` params object property called `config` +* set the params object property `isV3` to `false`. + +The `fontConfig` object accepts `ios`, `android`, `macos`, `windows`, `web`, and `native`. Use these to override fonts on particular platforms. + +:::info +At a minimum, you need to explicitly pass fonts for `android`, `ios`, and `web`. +::: + +```js +import * as React from 'react'; +import { configureFonts, MD2LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +const fontConfig = { + web: { + regular: { + fontFamily: 'sans-serif', + fontWeight: 'normal', + }, + medium: { + fontFamily: 'sans-serif-medium', + fontWeight: 'normal', + }, + light: { + fontFamily: 'sans-serif-light', + fontWeight: 'normal', + }, + thin: { + fontFamily: 'sans-serif-thin', + fontWeight: 'normal', + }, + }, + ios: { + regular: { + fontFamily: 'sans-serif', + fontWeight: 'normal', + }, + medium: { + fontFamily: 'sans-serif-medium', + fontWeight: 'normal', + }, + light: { + fontFamily: 'sans-serif-light', + fontWeight: 'normal', + }, + thin: { + fontFamily: 'sans-serif-thin', + fontWeight: 'normal', + }, + }, + android: { + regular: { + fontFamily: 'sans-serif', + fontWeight: 'normal', + }, + medium: { + fontFamily: 'sans-serif-medium', + fontWeight: 'normal', + }, + light: { + fontFamily: 'sans-serif-light', + fontWeight: 'normal', + }, + thin: { + fontFamily: 'sans-serif-thin', + fontWeight: 'normal', + }, + } +}; + +const theme = { + ...MD2LightTheme, + fonts: configureFonts({config: fontConfig, isV3: false}), +}; + +export default function Main() { + return ( + + + + ); +} +``` + +:::tip +If you're using TypeScript use `as const` when defining `fontConfig`. +::: + +### Material Design 3 + +#### Variants + +In the latest version fonts in theme are structured based on the `variant` keys e.g. `displayLarge` or `bodyMedium` which are then used in `Text`'s component throughout the whole library. + +:::info +The default `fontFamily` is different per particular platfrom: + +```js +Platform.select({ + web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', + ios: 'System', + default: 'sans-serif', // and 'sans-serif-medium' for `fontWeight:"500"` +}), +``` +::: + +* #### Display + +
+
+ + ```json + "displaySmall": { + "fontFamily": "Font", + "fontSize": 36, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 44, + } + ``` + +
+ +
+ + ```json + "displayMedium": { + "fontFamily": "Font", + "fontSize": 45, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 52, + } + ``` + +
+ +
+ + ```json + "displayLarge": { + "fontFamily": "Font", + "fontSize": 57, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 64, + } + ``` + +
+
+ +* #### Headline + +
+
+ + ```json + "headlineSmall": { + "fontFamily": "Font", + "fontSize": 24, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 32, + } + ``` + +
+ +
+ + ```json + "headlineMedium": { + "fontFamily": "Font", + "fontSize": 28, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 36, + } + ``` + +
+ +
+ + ```json + "headlineLarge": { + "fontFamily": "Font", + "fontSize": 32, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 40, + } + ``` + +
+
+ +* #### Title + +
+
+ + ```json + "titleSmall": { + "fontFamily": "Font", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + } + ``` + +
+ +
+ + ```json + "titleMedium": { + "fontFamily": "Font", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + } + ``` + +
+ +
+ + ```json + "titleLarge": { + "fontFamily": "Font", + "fontSize": 22, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 28, + } + ``` + +
+
+ +* #### Label + +
+
+ + ```json + "labelSmall": { + "fontFamily": "Font", + "fontSize": 11, + "fontWeight": "500", + "letterSpacing": 0.5, + "lineHeight": 16, + } + ``` + +
+ +
+ + ```json + "labelMedium": { + "fontFamily": "Font", + "fontSize": 12, + "fontWeight": "500", + "letterSpacing": 0.5, + "lineHeight": 16, + } + ``` + +
+ +
+ + ```json + "labelLarge": { + "fontFamily": "Font", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + } + ``` + +
+
+ +* #### Body + +
+
+ + ```json + "bodySmall": { + "fontFamily": "Font", + "fontSize": 12, + "fontWeight": "400", + "letterSpacing": 0.4, + "lineHeight": 16, + } + ``` + +
+ +
+ + ```json + "bodyMedium": { + "fontFamily": "Font", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, + } + ``` + +
+ +
+ + ```json + "bodyLarge": { + "fontFamily": "Font", + "fontSize": 16, + "fontWeight": "400", + "letterSpacing": 0.15, + "lineHeight": 24, + } + ``` + +
+
+ +:::info +If any component uses Paper's `Text` component, without specified variant, then `default` variant is applied: + +```json +"default": { + "fontFamily": "FontFamily", + "fontWeight": "400", + "letterSpacing": 0, +}, +``` +::: + +#### Using `configureFonts` helper + +* If there is a need to create a custom font variant, prepare its config object including required all fonts properties. After that, defined `fontConfig` has to be passed under the `variant` name as `config` into the params object: + +```js +import * as React from 'react'; +import { configureFonts, MD3LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +const fontConfig = { + customVariant: { + fontFamily: Platform.select({ + web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', + ios: 'System', + default: 'sans-serif', + }), + fontWeight: '400', + letterSpacing: 0.5, + lineHeight: 22, + fontSize: 20, + } +}; + +const theme = { + ...MD3LightTheme, + fonts: configureFonts({config: fontConfig}), +}; + +export default function Main() { + return ( + + + + ); +} +``` + +If you're using TypeScript you will need to create a custom `Text` component which accepts your custom variants: + +```typescript +import { customText } from 'react-native-paper' + +// Use this instead of importing `Text` from `react-native-paper` +export const Text = customText<'customVariant'>() +``` + + +* In order to override one of the available `variant`'s font properties, pass the modified `fontConfig` under specific `variant` name as `config` into the params object: + +```js +import * as React from 'react'; +import { configureFonts, MD3LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +const fontConfig = { + bodyLarge: { + letterSpacing: 0.5, + lineHeight: 22, + fontSize: 20, + } +}; + +const theme = { + ...MD3LightTheme, + fonts: configureFonts({config: fontConfig}), +}; + +export default function Main() { + return ( + + + + ); +} +``` + +* However, if you just want to override any font property e.g. `fontFamily` or `letterSpacing` for all variants, you can pass the `fontConfig` as a `config` into the params object without specifying variant name: + +```js +import * as React from 'react'; +import { configureFonts, MD3LightTheme, PaperProvider } from 'react-native-paper'; +import App from './src/App'; + +const fontConfig = { + fontFamily: 'NotoSans' +}; + +const theme = { + ...MD3LightTheme, + fonts: configureFonts({config: fontConfig}), +}; + +export default function Main() { + return ( + + + + ); +} +``` + + +## Variable fonts + +Although React Native Paper supports `fontWeight` and `fontStyle` properties, there are multiple limitations to custom +fonts in React Native. Using custom [variable fonts](https://fonts.google.com/knowledge/introducing_type/introducing_variable_fonts) +is especially problematic, with some platforms failing to render variants entirely. To ensure correct typography in your +app, we suggest installing each font variant as a separate file. Below you'll find example on how to set up React Native Paper +theme with custom fonts. + +Should you decide to use a variable font anyway, second example will show you how to test if the font is rendered correctly in React Native on all platforms. + +
+ Variable fonts examples + +
diff --git a/docs/versioned_docs/version-5.x/guides/05-react-native-web.md b/docs/versioned_docs/version-5.x/guides/05-react-native-web.md new file mode 100644 index 0000000000..af0331a7d9 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/05-react-native-web.md @@ -0,0 +1,282 @@ +--- +title: Using on the Web +--- + +# Using on the Web + +## Pre-requisites + +Make sure that you have followed the getting started guide and have `react-native-paper` installed and configured before following this guide. + +We're going to use [react-native-web](https://github.com/necolas/react-native-web) and [webpack](https://webpack.js.org/) to use React Native Paper on the web, so let's install them as well. + +To install `react-native-web`, run: + +```bash npm2yarn +npm install react-native-web react-dom react-art +``` + +### Using CRA ([Create React App](https://github.com/facebook/create-react-app)) + +Install [`react-app-rewired`](https://github.com/timarney/react-app-rewired) to override `webpack` configuration: + +```bash npm2yarn +npm install --save-dev react-app-rewired +``` + +[Configure `babel-loader`](#2-configure-babel-loader) using a new file called `config-overrides.js`: + +```js +module.exports = function override(config, env) { + config.module.rules.push({ + test: /\.js$/, + exclude: /node_modules[/\\](?!react-native-vector-icons)/, + use: { + loader: 'babel-loader', + options: { + // Disable reading babel configuration + babelrc: false, + configFile: false, + + // The configuration for compilation + presets: [ + ['@babel/preset-env', { useBuiltIns: 'usage' }], + '@babel/preset-react', + '@babel/preset-flow', + '@babel/preset-typescript', + ], + plugins: [ + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-object-rest-spread', + ], + }, + }, + }); + + return config; +}; +``` + +Change your script in `package.json`: + +```diff +/* package.json */ + + "scripts": { +- "start": "react-scripts start", ++ "start": "react-app-rewired start", +- "build": "react-scripts build", ++ "build": "react-app-rewired build", +- "test": "react-scripts test --env=jsdom", ++ "test": "react-app-rewired test --env=jsdom" +} +``` + +### Custom webpack setup + +To install `webpack`, run: + +```bash npm2yarn +npm install --save-dev webpack webpack-cli webpack-dev-server +``` + +If you don't have a webpack config in your project, copy the following to `webpack.config.js` get started: + +```js +const path = require('path'); + +module.exports = { + mode: 'development', + + // Path to the entry file, change it according to the path you have + entry: path.join(__dirname, 'App.js'), + + // Path for the output files + output: { + path: path.join(__dirname, 'dist'), + filename: 'app.bundle.js', + }, + + // Enable source map support + devtool: 'source-map', + + // Loaders and resolver config + module: { + rules: [], + }, + resolve: {}, + + // Development server config + devServer: { + contentBase: [path.join(__dirname, 'public')], + historyApiFallback: true, + }, +}; +``` + +Also create a folder named `public` and add the following file named `index.html`: + +```html + + + + + + + + App + + + + +
+ + +``` + +Now we're ready to start configuring the project. + +## Configure webpack + +### 1. Alias `react-native` to `react-native-web` + +First, we have to tell webpack to use `react-native-web` instead of `react-native`. Add the following alias in your webpack config under `resolve`: + +```js +alias: { + 'react-native$': require.resolve('react-native-web'), +} +``` + +### 2. Configure `babel-loader` + +Next, we want to tell `babel-loader` to compile `react-native-paper` and `react-native-vector-icons`. We would also want to disable reading the babel configuration files to prevent any conflicts. + +First install the required dependencies: + +```bash npm2yarn +npm install --save-dev babel-loader @babel/preset-env @babel/preset-react @babel/preset-flow @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread +``` + +Now, add the following in the `module.rules` array in your webpack config: + +```js +{ + test: /\.js$/, + exclude: /node_modules[/\\](?!react-native-vector-icons)/, + use: { + loader: 'babel-loader', + options: { + // Disable reading babel configuration + babelrc: false, + configFile: false, + + // The configuration for compilation + presets: [ + ['@babel/preset-env', { useBuiltIns: 'usage' }], + '@babel/preset-react', + '@babel/preset-flow', + "@babel/preset-typescript" + ], + plugins: [ + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-object-rest-spread' + ], + }, + }, +}, +``` + +### 3. Configure `file-loader` + +#### webpack < 5.0 + +To be able to import images and other assets using `require`, we need to configure `file-loader`. Let's install it: + +```bash npm2yarn +npm install --save-dev file-loader +``` + +To configure it, add the following in the `module.rules` array in your webpack config: + +```js +{ + test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/, + loader: 'file-loader', +} +``` + +##### webpack >= 5.0 + +Use `asset/resource`, since `file-loader` was deprecated in webpack v5. + +```js +{ + test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/, + type: 'asset/resource' +} +``` + +## Load the Material Design Icons + +If you followed the getting started guide, you should have the following code in your root component: + +```js + + + +``` + +Now we need tweak this section to load the Material Design Icons from the [`react-native-vector-icons`](https://github.com/oblador/react-native-vector-icons) library: + +```js + + + {Platform.OS === 'web' ? ( + + ) : null} + + + +``` + +Remember to import `Platform` from `react-native` at the top: + +```js +import { Platform } from 'react-native'; +``` + +You can also load these fonts using [`css-loader`](https://github.com/webpack-contrib/css-loader) if you prefer. + +## Load the Roboto fonts (optional) + +The default theme in React Native Paper uses the Roboto font. You can add them to your project following [the instructions on its Google Fonts page](https://fonts.google.com/specimen/Roboto?selection.family=Roboto:100,300,400,500). + +## We're done! + +You can run `webpack-dev-server` to run the webpack server and open your project in the browser. You can add the following script in your `package.json` under the `"scripts"` section to make it easier: + +```json +"web": "webpack-dev-server --open" +``` + +Now you can run `yarn web` to run the project on web. diff --git a/docs/versioned_docs/version-5.x/guides/06-recommended-libraries.md b/docs/versioned_docs/version-5.x/guides/06-recommended-libraries.md new file mode 100644 index 0000000000..e70157ca1d --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/06-recommended-libraries.md @@ -0,0 +1,40 @@ +--- +title: Recommended Libraries +--- + +# Recommended Libraries + +Our mission is to provide a full suite of well-integrated components built with Material Design in mind. However, many components already have better well maintained implementations. + +Here are some of the libraries we recommend: + +## Tabs + +[react-native-community/react-native-tab-view](https://github.com/react-native-community/react-native-tab-view) +Material Design themed [swipeable tabs](https://material.io/design/components/tabs.html), maintained by [@satya164](https://twitter.com/satya164) and [@mosdnk](https://twitter.com/mosdnk). + +[react-native-paper-tabs](https://github.com/web-ridge/react-native-paper-tabs) +Material Design themed [swipeable tabs](https://material.io/design/components/tabs.html) for React Native Paper, maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) + +## Bottom sheet + +[osdnk/reanimated-bottom-sheet](https://github.com/osdnk/react-native-reanimated-bottom-sheet) +An implementation of the [bottom sheet behaviour](https://material.io/design/components/sheets-bottom.html), maintained by [@mosdnk](https://twitter.com/mosdnk). + +[gorhom/react-native-bottom-sheet](https://github.com/gorhom/react-native-bottom-sheet) +An implementation of the [bottom sheet behaviour](https://material.io/design/components/sheets-bottom.html), maintained by [@Gorhom](https://twitter.com/Gorhom). + + +## Date Picker +[web-ridge/react-native-paper-dates](https://github.com/web-ridge/react-native-paper-dates) +Material Design themed [date picker](https://material.io/components/date-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) + +[react-native-community/react-native-datetimepicker](https://github.com/react-native-community/react-native-datetimepicker) + +## Time Picker +[web-ridge/react-native-paper-dates](https://github.com/web-ridge/react-native-paper-dates) +Material Design themed [time picker](https://material.io/components/time-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) + +## System Colors +[pchmn/expo-material3-theme](https://github.com/pchmn/expo-material3-theme) +Retrieve Material 3 system colors from Android 12+ devices \ No newline at end of file diff --git a/docs/versioned_docs/version-5.x/guides/07-contributing.mdx b/docs/versioned_docs/version-5.x/guides/07-contributing.mdx new file mode 100644 index 0000000000..2861043dc3 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/07-contributing.mdx @@ -0,0 +1,7 @@ +--- +title: Contributing +--- + +import Contributing from '../../../../CONTRIBUTING.md'; + + diff --git a/docs/versioned_docs/version-5.x/guides/08-theming-with-react-navigation.md b/docs/versioned_docs/version-5.x/guides/08-theming-with-react-navigation.md new file mode 100644 index 0000000000..197d2bfa31 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/08-theming-with-react-navigation.md @@ -0,0 +1,356 @@ +--- +title: Theming with React Navigation +--- + +# Theming with React Navigation + +In this guide, we will look into how to apply theming for an application using React Native Paper and React Navigation at the same time. + +Offering different theme options, especially dark/light ones, has become increasingly a standard requirement of the modern mobile application. Fortunately, both React Navigation and React Native Paper support configurable theming out-of-the-box. +But how to make them work together? + +## Themes adaptation + +### Material Design 2 + +Fortunately, in Material Design 2, both React Navigation and React Native Paper offer very similar API when it comes to theming and theme color structure. It's possible to import them in light and dark variants from both. + +```js +import { + DarkTheme as NavigationDarkTheme, + DefaultTheme as NavigationDefaultTheme, +} from '@react-navigation/native'; + +import { + MD2LightTheme, + MD2DarkTheme, +} from 'react-native-paper'; +``` + +### Material Design 3 + +From v5, React Native Paper theme colors structure follows the Material Design 3 (known as Material You) colors system, which differs significantly from both the previous Paper's theme and React Navigation theme. + +However, to simplify adapting React Navigation theme colors, to use the ones from React Native Paper, it's worth using a utility called `adaptNavigationTheme` – it accepts navigation-compliant themes in both modes and returns their equivalents adjusted to Material Design 3. + +```ts +import { + DarkTheme as NavigationDarkTheme, + DefaultTheme as NavigationDefaultTheme, +} from '@react-navigation/native'; +import { adaptNavigationTheme } from 'react-native-paper'; + +const { LightTheme, DarkTheme } = adaptNavigationTheme({ + reactNavigationLight: NavigationDefaultTheme, + reactNavigationDark: NavigationDarkTheme, +}); +``` + +Library exports also Material Design 3 themes in both modes: + +```js +import { + MD3LightTheme, + MD3DarkTheme, +} from 'react-native-paper'; +``` + +## Combining theme objects + +Both libraries require a wrapper to be used at the entry point of the application. +React Navigation exposes `NavigationContainer`, which ensures that navigation works correctly and accepts `theme` as an optional property. Read more about setting up navigation [here](https://reactnavigation.org/docs/getting-started/). +For React Native Paper theme to work, we need to use `PaperProvider` also at the application's entry point. + +```js +import { NavigationContainer } from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; +import { TouchableOpacity } from 'react-native'; +import { + Card, + Title, + Paragraph, + List, + PaperProvider, +} from 'react-native-paper'; + +const Stack = createStackNavigator(); + +const HomeScreen = ({ navigation }) => ( + + navigation?.push('Details', { + title, + content, + }) + } + > + + + {title} + {content} + + + +); + +const DetailsScreen = (props) => { + const { title, content } = props?.route?.params; + return ( + + {title} + + + ); +}; + +export default function App() { + return ( + + + + + + + + + ); +} +``` + + + +Our goal here is to combine those two themes, so that we could control the theme for the entire application from a single place. + +To make things easier we can use [deepmerge](https://www.npmjs.com/package/deepmerge) package. We can install it with: + +```bash npm2yarn +npm install deepmerge +``` + +### Material Design 2 + +```js +import { + NavigationContainer, + DarkTheme as NavigationDarkTheme, + DefaultTheme as NavigationDefaultTheme, +} from '@react-navigation/native'; +import { + MD2DarkTheme, + MD2LightTheme, +} from 'react-native-paper'; +import merge from 'deepmerge'; + +const CombinedDefaultTheme = merge(MD2LightTheme, NavigationDefaultTheme); +const CombinedDarkTheme = merge(MD2DarkTheme, NavigationDarkTheme); +``` + +### Material Design 3 + +```js +import { + NavigationContainer, + DarkTheme as NavigationDarkTheme, + DefaultTheme as NavigationDefaultTheme, +} from '@react-navigation/native'; +import { + MD3DarkTheme, + MD3LightTheme, + adaptNavigationTheme, +} from 'react-native-paper'; +import merge from 'deepmerge'; + +const { LightTheme, DarkTheme } = adaptNavigationTheme({ + reactNavigationLight: NavigationDefaultTheme, + reactNavigationDark: NavigationDarkTheme, +}); + +const CombinedDefaultTheme = merge(MD3LightTheme, LightTheme); +const CombinedDarkTheme = merge(MD3DarkTheme, DarkTheme); +``` + +Alternatively, we could merge those themes using vanilla JavaScript: + +### Material Design 2 + +```js +const CombinedDefaultTheme = { + ...MD2LightTheme, + ...NavigationDefaultTheme, + colors: { + ...MD2LightTheme.colors, + ...NavigationDefaultTheme.colors, + }, +}; +const CombinedDarkTheme = { + ...MD2DarkTheme, + ...NavigationDarkTheme, + colors: { + ...MD2DarkTheme.colors, + ...NavigationDarkTheme.colors, + }, +}; +``` + +### Material Design 3 + +```js +const { LightTheme, DarkTheme } = adaptNavigationTheme({ + reactNavigationLight: NavigationDefaultTheme, + reactNavigationDark: NavigationDarkTheme, +}); + +const CombinedDefaultTheme = { + ...MD3LightTheme, + ...LightTheme, + colors: { + ...MD3LightTheme.colors, + ...LightTheme.colors, + }, +}; +const CombinedDarkTheme = { + ...MD3DarkTheme, + ...DarkTheme, + colors: { + ...MD3DarkTheme.colors, + ...DarkTheme.colors, + }, +}; +``` + +## Passing theme with Providers + +After combining the themes, we will be able to control theming in both libraries from a single source, which will come in handy later. + +Next, we need to pass merged themes into the Providers. For this part, we use the dark one - `CombinedDarkTheme`. + +```js +const Stack = createStackNavigator(); + +export default function App() { + return ( + + + + + + + + + ); +} +``` + +## Customizing theme + +We don't need to limit ourselves to the themes offered by the libraries' default. Both packages allow for custom themes to be applied. +You can learn all about it in their documentation: + +- [Theming in React Navigation](https://reactnavigation.org/docs/themes/) +- [Theming in React Native Paper](https://callstack.github.io/react-native-paper/docs/guides/theming) + +## React Context for theme customization + +Now, we wouldn't want to stay on dark theme forever, which is why we need to gain the ability to control theme dynamically. A bit of state management is needed for this purpose. + +React Context proves itself very useful in handling cross-cutting concerns like global theme handling, so we will use just that. + +## Creating Context + +First, we define our Context. + +```js +import React from 'react'; + +export const PreferencesContext = React.createContext({ + toggleTheme: () => {}, + isThemeDark: false, +}); +``` + +## Using Context + +Context Provider should be imported also at the entry point, as we want it to wrap the whole app, for the theme values to be accessible at every component that we have. + +```js +import React from 'react'; +import { PreferencesContext } from './PreferencesContext'; + +const Stack = createStackNavigator(); + +export default function App() { + const [isThemeDark, setIsThemeDark] = React.useState(false); + + let theme = isThemeDark ? CombinedDarkTheme : CombinedDefaultTheme; + + const toggleTheme = React.useCallback(() => { + return setIsThemeDark(!isThemeDark); + }, [isThemeDark]); + + const preferences = React.useMemo( + () => ({ + toggleTheme, + isThemeDark, + }), + [toggleTheme, isThemeDark] + ); + + return ( + // Context is wired into the local state of our main component, so that its values could be propagated throughout the entire application + + + + + + + + + + + ); +} +``` + +Now that the Context is available at every component, all we need to do is import it. Next, provide the user with some UI element to control changing the theme. We will use `Paper`'s [Switch](https://callstack.github.io/react-native-paper/docs/components/Switch) for this purpose. + +```js +import React from 'react'; +import { useTheme, Appbar, TouchableRipple, Switch } from 'react-native-paper'; +import { PreferencesContext } from './PreferencesContext'; + +const Header = ({ scene }) => { + const theme = useTheme(); + const { toggleTheme, isThemeDark } = React.useContext(PreferencesContext); + + return ( + + + + + ); +}; +``` + +And now you can switch between light and dark theme! + +![paperGuide1](../../../static/screenshots/themingWithReactNavigationDarkLightSwitch.gif) + +Thanks to the linking of themes that we did earlier, switching themes can be controlled with only one piece of state. + +React Native Paper components will automatically use the provided theme thanks to the `PaperProvider` that is wrapped around the entry point of our application, but we can also access theme values manually with `useTheme` hook, +exposed by the library. You can see how it's done in the `Header` component code above. + +If light/dark themes are not enough for your use case, you can learn more about creating Material Design themes [here](https://material.io/design/material-theming/implementing-your-theme.html#color). +On `main` branch of the example app, you will find implemented [Menu](https://callstack.github.io/react-native-paper/docs/components/Menu) component, which allows you to choose a few custom themes. Inspecting code in `utils` and `Header` may give you some idea of how to use your own themes with `Paper`, in addition to dedicated [docs](https://callstack.github.io/react-native-paper/docs/components/Menu). + +Read more about integrating `Paper` with `React Navigation` in a brilliant [article](https://reactnavigation.org/blog/2020/01/29/using-react-navigation-5-with-react-native-paper/) by [@trensik](https://twitter.com/trensik) diff --git a/docs/versioned_docs/version-5.x/guides/09-bottom-navigation.md b/docs/versioned_docs/version-5.x/guides/09-bottom-navigation.md new file mode 100644 index 0000000000..21776718c9 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/09-bottom-navigation.md @@ -0,0 +1,248 @@ +--- +title: Using BottomNavigation with React Navigation +--- + +:::caution +The `createMaterialBottomTabNavigator` has been deprecated as of `react-native-paper@5.14.0`. Instead, use `@react-navigation/bottom-tabs` version `7.x` or later, combined with `BottomNavigation.Bar` to achieve a Material Design look. + +For implementation details, see the [dedicated example](https://callstack.github.io/react-native-paper/docs/components/BottomNavigation/BottomNavigationBar#with-react-navigation). +::: + +A material-design themed tab bar on the bottom of the screen that lets you switch between different routes with animation. Routes are lazily initialized - their screen components are not mounted until they are first focused. + +This wraps the [`BottomNavigation`](https://callstack.github.io/react-native-paper/docs/components/BottomNavigation/) component from `react-native-paper`, however if you [configure the Babel plugin](https://callstack.github.io/react-native-paper/docs/guides/getting-started/), it won't include the whole library in your bundle. + + + +:::info +To use this navigator, ensure that you have [`@react-navigation/native` and its dependencies (follow this guide)](https://reactnavigation.org/docs/getting-started): +::: + +> 👉 For a complete example please visit `createMaterialBottomTabNavigator` [snack](https://snack.expo.dev/@react-native-paper/creatematerialbottomtabnavigator) + +## API Definition + +To use this tab navigator, import it from `react-native-paper/react-navigation`: + +```js +import { createMaterialBottomTabNavigator } from 'react-native-paper/react-navigation'; + +const Tab = createMaterialBottomTabNavigator(); + +function MyTabs() { + return ( + + + + + ); +} +``` + +> 👉 For a complete usage guide please visit [Tab Navigation](https://reactnavigation.org/docs/tab-based-navigation/) + +### Props + +The `Tab.Navigator` component accepts following props: + +#### `id` + +Optional unique ID for the navigator. This can be used with [`navigation.getParent`](https://reactnavigation.org/docs/navigation-prop#getparent) to refer to this navigator in a child navigator. + +#### `initialRouteName` + +The name of the route to render on first load of the navigator. + +#### `screenOptions` + +Default options to use for the screens in the navigator. + +#### `backBehavior` + +This controls what happens when `goBack` is called in the navigator. This includes pressing the device's back button or back gesture on Android. + +It supports the following values: + +- `firstRoute` - return to the first screen defined in the navigator (default) +- `initialRoute` - return to initial screen passed in `initialRouteName` prop, if not passed, defaults to the first screen +- `order` - return to screen defined before the focused screen +- `history` - return to last visited screen in the navigator; if the same screen is visited multiple times, the older entries are dropped from the history +- `none` - do not handle back button + +#### `shifting` + +Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label. + +By default, this is `true` when you have more than 3 tabs. Pass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation. + +#### `labeled` + +Whether to show labels in tabs. When `false`, only icons will be displayed. + +#### `activeColor` + +Custom color for icon and label in the active tab. + +#### `inactiveColor` + +Custom color for icon and label in the inactive tab. + +#### `barStyle` + +Style for the bottom navigation bar. You can pass custom background color here: + +```js + + {/* ... */} + +``` + +If you have a translucent navigation bar on Android, you can also set a bottom padding here: + +```js + + {/* ... */} + +``` + +#### `theme` + +Enables the customization of default theme attributes (e.g. colors) or facilitates the utilization of a personalized custom theme. + +### Options + +The following [options](https://reactnavigation.org/docs/screen-options) can be used to configure the screens in the navigator: + +#### `title` + +Generic title that can be used as a fallback for `headerTitle` and `tabBarLabel`. + +#### `tabBarIcon` + +Function that given `{ focused: boolean, color: string }` returns a React.Node, to display in the tab bar. + +#### `tabBarColor`
In v5.x works only with theme version 2.
+ +Color for the tab bar when the tab corresponding to the screen is active. Used for the ripple effect. This is only supported when `shifting` is `true`. + +#### `tabBarLabel` + +Title string of a tab displayed in the tab bar. When undefined, scene `title` is used. To hide, see `labeled` option in the previous section. + +#### `tabBarBadge` + +Badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text. + +#### `tabBarAccessibilityLabel` + +Accessibility label for the tab button. This is read by the screen reader when the user taps the tab. It's recommended to set this if you don't have a label for the tab. + +#### `tabBarTestID` + +ID to locate this tab button in tests. + +### Events + +The navigator can [emit events](https://reactnavigation.org/docs/navigation-events) on certain actions. Supported events are: + +#### `tabPress` + +This event is fired when the user presses the tab button for the current screen in the tab bar. By default a tab press does several things: + +- If the tab is not focused, tab press will focus that tab +- If the tab is already focused: + - If the screen for the tab renders a scroll view, you can use [`useScrollToTop`](https://reactnavigation.org/docs/use-scroll-to-top) to scroll it to top + - If the screen for the tab renders a stack navigator, a `popToTop` action is performed on the stack + +To prevent the default behavior, you can call `event.preventDefault`: + +```js +React.useEffect(() => { + const unsubscribe = navigation.addListener('tabPress', (e) => { + // Prevent default behavior + + e.preventDefault(); + // Do something manually + // ... + }); + + return unsubscribe; +}, [navigation]); +``` + +### Helpers + +The tab navigator adds the following methods to the navigation prop: + +#### `jumpTo` + +Navigates to an existing screen in the tab navigator. The method accepts following arguments: + +- `name` - _string_ - Name of the route to jump to. +- `params` - _object_ - Screen params to pass to the destination route. + + + +```js +navigation.jumpTo('Profile', { name: 'Michaś' }); +``` + +## Example + +```js +import { createMaterialBottomTabNavigator } from 'react-native-paper/react-navigation'; +import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons'; + +const Tab = createMaterialBottomTabNavigator(); + +function MyTabs() { + return ( + + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + + ); +} +``` diff --git a/docs/versioned_docs/version-5.x/guides/09-react-navigation.md b/docs/versioned_docs/version-5.x/guides/09-react-navigation.md new file mode 100644 index 0000000000..d9227cfb2b --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/09-react-navigation.md @@ -0,0 +1,281 @@ +--- +title: Integrate AppBar with React Navigation +--- + +# Integrate AppBar with react-navigation + +## Prerequisites + + - `react-native-paper` + - `react-navigation` + +We assume that you have already installed the mentioned libraries above, otherwise please check out the guides below. + + [React Native Paper - Getting Started](https://callstack.github.io/react-native-paper/docs/guides/getting-started) + + [React Navigation - Getting Started](https://reactnavigation.org/docs/getting-started/) + +## Stack Navigator + +We will start with `react-navigation` by creating a basic navigation stack. Stack navigator gives us a possibility of transition between screens in our app and manage navigation's history. In a simple scenario where there is only one stack navigator present in the app, it resembles a navigation state in a browser. +Screens are pushed and popped from the stack while the user navigates to a new screen or go back to the previous one. + +Let's create two screens. A main screen named `Home` and details screen named `Details`. + +```js +import 'react-native-gesture-handler'; +import React from 'react'; +import { NavigationContainer } from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; + +const Stack = createStackNavigator(); + +export default function App() { + return ( + + + + + + + ); +} +``` + +At the moment our navigation stack contains two screens and will render `HomeScreen` or `DetailsScreen` components according to the current navigation state. We have not implemented those components yet, so let's do this now: + +```js +import React from 'react'; +import {View, Text, Button, StyleSheet} from 'react-native'; + +function HomeScreen() { + return ( + + Home Screen + + ); +} + +function DetailsScreen() { + return ( + + Details Screen + + ); +} + +const style = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, +}); +``` + +Once we have finished implementing the components, we can run the app and check how Stack looks like. + + + +To navigate from `HomeScreen` to `DetailsScreen` we can use the navigation object provided by `Stack.Screen` component. Every component rendered by `Stack.Screen` has an access to the navigation object via props. Let's modify our `HomeScreen` component: + +```js +import { Button } from 'react-native-paper'; + +function HomeScreen({ navigation }) { + return ( + + Home Screen + + + ); +} + +const style = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, +}); +``` + +Our result: + + + +As you can see, we can already navigate between two screens. In the next steps, we will show you how to use Paper's `AppBar` instead of the default header. + +### Adding `AppBar` + +We can customize Stack's header by passing custom component: + +```js +export default function App() { + return ( + + , + }}> + + + + + ); +} +``` + +Now we will implement `CustomNavigationBar` using `AppBar` component: + +```js +import { Appbar } from 'react-native-paper'; + +export default function CustomNavigationBar() { + return ( + + + + ); +} +``` + +Current implementation of the `CustomNavigationBar` is simple - we just render a title inside of it, however it's hardcoded "My awesome app" text, so let's adjust it. +To automatically get the correct `title` for the screen, we're going to take the advantage of `getHeaderTitle` helper available in `@react-navigation/elements` in the following way: + +```js +import { Appbar } from 'react-native-paper'; +import { getHeaderTitle } from '@react-navigation/elements'; + +export default function CustomNavigationBar({ route, options }) { + const title = getHeaderTitle(options, route.name); + + return ( + + + + ); +} +``` + +You may notice there is no way to go back to the previous screen, because the back button is not visible in the header. Let's add it now and let's make sure it's visible on all Stack's screens except `Home` screen. + +In order to achieve it, we firstly check, if the navigation bar receives a `back` prop. If it has, it means there is another screen on the stack beneath the current screen and we should render the back arrow button in such a case. + +```js +import { Appbar } from 'react-native-paper'; +import { getHeaderTitle } from '@react-navigation/elements'; + +export default function CustomNavigationBar({ navigation, route, options, back }) { + const title = getHeaderTitle(options, route.name); + + return ( + + {back ? : null} + + + ); +} +``` + + + +### Adding more items to `Appbar` + +Another interesting pattern that can be implemented with `react-native-paper` and `react-navigation` is a "menu" button. Thanks to the `Menu` component we can add a nice looking pop-up to our `Appbar`. To implement this feature we need to make a couple of changes in `CustomNavigationBar`: +- Render a `Menu` component +- Pass `Appbar.Action` to the anchor prop +- Add a state to control `Menu` visibility + +:::note +To have properly working `Menu` component, remember to wrap your root component with the `PaperProvider`: + +```js +import { PaperProvider } from 'react-native-paper'; + +// ... + + + + , + }}> + + + + + +``` +::: + +We also want the menu to appear only on `HomeScreen`, which means we will render it conditionally based on the `back` prop. + +```js +import React from 'react'; +import { Appbar, Menu } from 'react-native-paper'; +import { getHeaderTitle } from '@react-navigation/elements'; + +export default function CustomNavigationBar({ + navigation, + route, + options, + back, +}) { + const [visible, setVisible] = React.useState(false); + const openMenu = () => setVisible(true); + const closeMenu = () => setVisible(false); + + const title = getHeaderTitle(options, route.name); + + return ( + + {back ? : null} + + {!back ? ( + + }> + { + console.log('Option 1 was pressed'); + }} + title="Option 1" + /> + { + console.log('Option 2 was pressed'); + }} + title="Option 2" + /> + { + console.log('Option 3 was pressed'); + }} + title="Option 3" + disabled + /> + + ) : null} + + ); +} +``` + +Final result: + + + +That's all we need! We have app bar that contains everything we need to navigate through screens and access an additional menu on the main screen. As you can see, with Material design `Appbar` provided by `react-native-paper` used together with `react-navigation` we can easily create an app that looks and works great. + +Grab the Expo [snack](https://snack.expo.dev/@react-native-paper/integrate-appbar-with-react-navigation) if you want to check the whole code! diff --git a/docs/versioned_docs/version-5.x/guides/10-migration-guide-to-5.0.md b/docs/versioned_docs/version-5.x/guides/10-migration-guide-to-5.0.md new file mode 100644 index 0000000000..670b9f7ab5 --- /dev/null +++ b/docs/versioned_docs/version-5.x/guides/10-migration-guide-to-5.0.md @@ -0,0 +1,656 @@ +--- +title: Introducing v5 with Material You +--- + +React Native Paper v5 is all about adopting the new Material Design 3 aka Material You. It was released in October 2021 after intense work and effort to make Material You follow a more expressive approach to design. + +Paper now supports both Material Design 2 and 3 through the configuration described in [Versioning](#versioning) and is compatible with a handful of API changes. + +# Migration guide to Material You 5.0 + +Version 5.0 brings support for the next Material Design iteration branded as Material You (in fact being Material Design v3 or in short MD3) into the `react-native-paper` library. All the components were refined according to the official [design kit on figma](https://www.figma.com/community/file/1035203688168086460) and adjusted in terms of visuals by changes in colors, typography and animations. + +Below you can find the most important information about the components whose API may have changed API has been changed due to supporting new props, renaming existing ones or some deprecations. Hopefully, based on the presented required changes, migration to the latest version should be smooth. Enjoy! + +### Installation + +* The `v5` has been oficially released as a stable version, which means it will be installed by default from the `npm`. In order to do that, open a Terminal in your project's folder and run: + +```bash npm2yarn +npm install react-native-paper +``` + +* From `v5` there is a need to install [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) for handling safe area. + +```bash npm2yarn +npm install react-native-safe-area-context +``` + +Additionaly for `iOS` platform there is a requirement to link the native parts of the library: + +```bash +npx pod-install +``` + +## Theming + +### Versioning + +Introducing Material You (MD3) into `react-native-paper` doesn't mean dropping previous Material Design (MD2)! On the contrary, both of them will be supported, however, not simultaneously. To specify which design system components should follow in the app, there is a newly created property in [the theme](https://callstack.github.io/react-native-paper/docs/guides/theming#theme-properties) named `version` that accepts one of two values: + +* 3 – (default) new Material You (MD3), +* 2 - previous Material Design (MD2). + +```js +theme: { + /* ... */ + version: 3 | 2 +} +``` + +Read more about using Material Design 2 in our [Material Design 2 theming guide](https://callstack.github.io/react-native-paper/docs/guides/theming#material-design-2) + +### Colors + +New theme introduces a new color palette along with new namings reflecting design color tokens, but written in camel case. Palette contains a set of five key colors, where primary, secondary and tertiary are classified into accent colors. The second group of colors is neutral and neutral variant colors used for defining surface or background roles as well as specifying high and medium emphasis text and icons. Additionally, the color system includes a semantic color role for error. + +Each accent and error colors has a group of related tones. The tones are mapped to roles that create contrast and visual interest when applied to elements in the UI. + +:::note +Dynamic colors are not supported yet +::: + +![color-palette](../../../static/migration/color-palette.png) + +Colors theme structure should follow the default palette and contain the following properties: + +```js +theme: { + /* ... */ + colors: { + primary, + primaryContainer, + secondary, + secondaryContainer, + tertiary, + tertiaryContainer, + surface, + surfaceVariant, + surfaceDisabled, + background, + error, + errorContainer, + onPrimary, + onPrimaryContainer, + onSecondary, + onSecondaryContainer, + onTertiary, + onTertiaryContainer, + onSurface, + onSurfaceVariant, + onSurfaceDisabled, + onError, + onErrorContainer, + onBackground, + outline, + shadow, + inverseOnSurface, + inverseSurface, + inversePrimary, + backdrop, + elevation: { + level0, + level1, + level2, + level3, + level4, + level5 + } + } +} +``` + +👉 You can find more about color on the [Material You website](https://m3.material.io/styles/color/the-color-system/key-colors-tones) + +## Typography + +A new way of approaching typography introduces one component `` which accepts prop `variant`. Variant defines appropriate text styles for type role and its size. The updated type scale organizes styles into five roles that are named to describe their purposes: Display, Headline, Title, Label and Body along with three display styles large, medium, and small. In total, there are fifteen variants that are MD3 compliant and are reflecting design typography tokens written in camel case. + +:::info +If any component uses Paper's `Text` component, without specified variant, then `default` variant is applied. +::: + +```js +Display Large +Display Medium +Display small + +Headline Large +Headline Medium +Headline Small + +Title Large +Title Medium +Title Small + +Body Large +Body Medium +Body Small + +Label Large +Label Medium +Label Small + ``` + +Take a look at the suggested replacement diff: + + ```diff +- Headline ++ Headline + +- Title ++ Title + +- Subheading ++ Subheading + +- Paragraph ++ Paragraph + +- Caption ++ Caption + ``` + + +👉 You can find more about typography on the [Material You website](https://m3.material.io/styles/typography/overview) + +### Configure fonts + +The existing utility called `configureFonts` was adjusted to help users configure their theme fonts in both version, that's why that function, as of today, is going to accept the object with the follwing properties as an argument: + +```ts +configureFonts(params) +``` + +Parameters: + +| NAME | TYPE | REQUIRED | +| ----------- | ----------- | ----------- | +| params | object | No | + +Valid `params` keys are: + + * `config` ([MD2FontsConfig](https://github.com/callstack/react-native-paper/blob/main/src/styles/fonts.tsx#L63) | [MD3FontsConfig](https://github.com/callstack/react-native-paper/blob/main/src/styles/fonts.tsx#L67)) - fonts config object appropriate to the MD version + * `isV3` (boolean) - whether adjusting theme fonts for v3. Default it true. + +To use your current font config from v2 and migrate to v3 there are two requirements: +* the font config previously passed directly into function has to be passed into the params object property called `config` +* the params object property `isV3` has to be set to `false` + +```diff +- configureFonts(fontConfig) ++ configureFonts({config: fontConfig, isV3: false}) +``` + +:::tip +If you want to check how to use `configureFonts` on MD3, check the [Fonts](https://callstack.github.io/react-native-paper/docs/guides/fonts.html) guide. +::: + +## Components + +### Appbar (Top app bar) + +`Appbar` and `Appbar.Header` in the latest version can be used in four various modes due to new prop `mode`: + +* `small` - Appbar with default height (64) (default), +* `medium` - Appbar with medium height (112), +* `large` - Appbar with large height (152), +* `center-aligned` - Appbar with default height (64) and center-aligned title. + +```js + + /* ... */ + +``` + +To make it easier for users to build the `BottomBar`, formed on the `Appbar` components, we have added a property `safeAreaInsets`: + +```js + + /* ... */ + +``` + +It's worth noting that by default the theme version 3 `Appbar` and `Appbar.Header` don't have a shadow. However, it can be added by passing prop `elevated` into the component: + +```js + + /* ... */ + +``` + +#### Appbar.Action + +`Appbar.Action` received new prop `isLeading`, which defines whether it's the leading button and should be placed at the beginning of the `Appbar`. + +```js + {}} /> +``` + +#### Appbar.Content + +New design guidelines indicate there is no subtitle in `Appbar.Content`, that's why there are two deprecations and the following props won't be supported anymore: `subtitle` and `subtitleStyle`. + +```diff +- ++ +``` + +### Banner, Searchbar and Snackbar + +According to the updates in `Surface` on the top of which `Banner`, `Searchbar` and `Snackbar` are implemented, all three component received `elevation` prop to adjust its value. + +```diff +- Hello ++ Hello +``` + +### BottomNavigation (Navigation bar) + +For the sake of new animation of pill shape, indicating active destination, and assisting icon change from outlined to filled, there are three changes within `navigationState.routes` property items: + +* `color` is deprecated since color is constant and the same for all routes, +* `icon` is renamed to `focusedIcon`, as the name implies, with theme version 3 it's the outline icon used as focused tab icon and with theme version 2 it's a default icon, +* `unfocusedIcon` (optional) is the filled icon used as the unfocused tab icon, compatible with theme version 3. + +:::info +`unfocusedIcon` is optional, if you can't find outline icon equivalent, omit that prop, so `focusedIcon` will be displayed in both active and inactive state. +::: + +```diff +routes: [ +- { key: "album", title: "Album", icon: "image-album", color: "#3F51B5" }, ++ { key: "album", title: "Album", focusedIcon: "image-album" }, +- { key: "library", title: "Library", icon: "inbox", color: "#009688" }, ++ { key: "library", title: "Library", focusedIcon: "inbox", unfocusedIcon: "inbox-outline" }, +- { key: "favorites", title: "Favorites", icon: "heart", color: "#795548" }, ++ { key: "favorites", title: "Favorites", focusedIcon: "heart", unfocusedIcon: "heart-outline; }, +- { key: "purchased", title: "Purchased", icon: "shopping-cart", color: "#607D8B" }, ++ { key: "purchased", title: "Purchased", focusedIcon: "shopping-cart" }, +] +``` + +The `compact` prop was also introduced, working with both themes. It indicates whether tabs should be spread across the entire width, especially in a horizontal mode. + +```js + +``` + +It's worth to mention that default value of prop `shifting` depends on the theme version: +* 3 - it's `false`, +* 2 - it's `true` when there are more than 3 tabs. + +Two additional props that control the scene animation were introduced that control the animation of the tabs when `sceneAnimationEnabled` is `true`: +* `sceneAnimationType: "opacity" | "shifting" | undefined` - defines the animation type for the scene. `shifting` enables a new animation where navigating to a scene will shift it horizontally into view. Both `opacity` and `undefined` have the same effect, fading the scene into view. +* `sceneAnimationEasing` allows specifying a custom easing function for the scene animation. + +![shiftingAnimation](../../../static/screenshots/bottom-navigation-shifting.gif) + +On a final note, please be aware that `BottomNavigation` with theme version 3 doesn't have a shadow. + +### Button + +`Button`'s property `mode` has been expanded with two additional options: +* `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background, +* `container-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. + +```js +<> + + + +``` + +The property `color` is deprecated, but in its place two new props called `buttonColor` and `textColor` are introduced: +* `buttonColor` - custom button's background color, +* `textColor` - custom button's text color. + +```diff +- ++ +``` + +```diff +- ++ +``` + +Please be aware that along with theme version 3, by default text in the `Button` component isn't uppercased and `contained` button doesn't have any shadow (use then `elevated`). + +### Card + +The `Card` component's property `mode` has been expanded with one additional option called `contained`, which applies to the card's specified background color without any elevation and border. + +```js + +``` + +#### Card.Title + +Since there is no one right way to make a card, there is also no one right way for specifying a title and subtitle variant. Therefore two new props come in handy: + +* `titleVariant` - title text variant defines appropriate text styles for type role and its size. +* `subtitleVariant` - subtitle text variant defines appropriate text styles for type role and its size. + +```js + +``` + +### Checkbox +#### Checkbox.Item + +`Checkbox.Item` similarly to `RadioButton.Item` has been expanded with the prop called `labelVariant`, which defines appropriate text styles for type role and its size. + +```js + +``` + +### Chip + +To properly compose `Chip` component and adjust into required type, there are three new props that will come in handy: + +* `compact` - sets smaller horizontal paddings around the label, useful for `Chip` containing only the label, +* `elevated` - indicating whether `Chip` should be elevated, +* `showSelectedOverlay` - defining whether to display an overlay on a selected button. + +```js +<> + Compact Chip + Elevated Chip + Chip with selected overlay + +``` +### Dialog +#### Dialog.Icon + +`Dialog.Icon` is another freshly added component presenting an icon within a `Dialog`, placed at the top of the content. + +:::caution +It's working only with theme version 3. +::: + +```js + + + + + +``` +### Divider + +`Divider` component received two new props: + +* `bold` - divider is bolded, +* `horizontalInset` - divider has horizontal insets on both sides. + +Additionally prop `inset` was renamed to `leftInset`. + +```diff +- ++ +``` + +### Drawer +#### Drawer.CollapsedItem (Navigation rail) + +`Drawer.CollapsedItem` is a newly created side navigation component that can be used within `Drawer`, representing a destination in the form of an action item with an icon and optionally label. + +:::caution +It's working only with theme version 3. +::: + +```js + + + + +``` + +#### Drawer.Section + +With the latest version, there is a possibility to specify whether `Drawer.Section` should have a separator, in form of `Divider` component, displayed at the end of the section. To adjust it, a new property called `showDivider` was introduced, which by default is `true`: + +```js + + + + +``` + +### FAB + +`FAB`, `AnimatedFAB` and `FAB.Group` in the latest version can be used with four variants and two modes, thanks to two new props: + +* `variant` defines color mappings variant for combinations of container and icon colors. Can be one of: primary (default), secondary, tertiary or surface. + +```js + +``` + +* `mode` specifies whether a button should be flat or elevated: + - `flat` - button without a shadow, + - `elevated` - button with a shadow. + +```js + +``` + +#### FAB + +Additionaly `FAB` may be applied in one of three available sizes, thanks to new prop `size`: + +* `small` - FAB with small height (40), +* `medium` - Appbar with default medium height (56), +* `large` - Appbar with large height (96). + +```js + +``` + +However, if you would like to have your own size of `FAB`, there is a new prop called `customSize`: + +```js + +``` + +Accordingly to introducing `size="small"`, prop `small` was deprecated. + +```diff +- ++ +``` + +#### FAB.Group + +There is also deprecation in one of the `actions` properties, namely `small` prop is deprecated and replaced in favour of the default `size="small"`. + +```diff +- ++ +``` + +Additionally, the action item property previously known as `labelStyle` was renamed to `containerStyle` since it's tied mostly with the container styles. At the same time, `labelStyle` is still available with the new role related to styling item label. + +```diff +- ++ +``` + +### IconButton + +`IconButton` received two new props: + +* `selected` sets alternative combination of icon and container color, + +```js + +``` + +* `containerColor` custom background color of the icon container. + +```js + +``` + +At the same time, the `color` prop was renamed to `iconColor`. + +```diff +- ++ +``` + +### Menu +#### Menu.Item + +`Menu.Item` received two new props: + +* `dense` sets smaller item height for more condensed layout, +* `trailingIcon` which handles displaying an icon at the end of the item row. + +```js + +``` + +At the same time, by analogy to the second new prop, the `icon` prop was renamed to `leadingIcon`. + +```diff +- {}} title="Redo" /> ++ {}} title="Redo" /> +``` + +### RadioButton +##### RadioButton.Item + +`RadioButton.Item` has been expanded with the prop called `labelVariant`, which defines appropriate text styles for type role and its size. + +```js + +``` + +### SegmentedButtons + +`SegmentedButtons` is a completely new component introduced in the latest version. It allows people to select options, switch views, or sort elements. It supports single and multiselect select variant and provide a lot +of customization options. + +![segmentedButtons](../../../static/screenshots/segmentedbuttons.gif) + +```js +const MyComponent = () => { + const [value, setValue] = React.useState(''); + + return ( + + ); +}; +``` + +### Snackbar + +`Snackbar` due to the optional close affordance, in form of `IconButton` (located on the right side of action button), received three new props: + +* `icon` - icon to display when `onIconPress` is defined. Default will be `close` icon. +* `onIconPress` - function to execute on icon button press. The icon button appears only when this prop is specified. +* `iconAccessibilityLabel` - accessibility label for the icon button. + +### Surface + +`Surface` component received one new prop: +* `elevation` - accepts values from `0` to `5` and applies background color and shadows to the `Surface` component. Supports both iOS and Android. + +Previously `elevation` was passed inside the `style` prop. Since it supported not only Android, but also iOS, we decided to extract it from `style` and create a separate `elevation` prop for that. + +```diff +- ++ +``` + +### TextInput +#### TextInput.Icon + +The property `name` was renamed to `icon`, since the scope and type of that prop is much wider than just the icon name – it accepts also the function which receives an object with color and size properties and + +```diff +- ++ +``` + +## Tooltip + +Component displayed upon tapping and holding a screen element or component used to present an informative text label identifying an element, such as a description of its function. + +![tooltip](../../../static/screenshots/tooltips.gif) + + +```js + + {}} /> + +``` + +## Credits + +With this, that’s a wrap. + +The update wouldn't happen without a group of great React Native experts I'm happy to work with. +From this place I would like to thank: +- [Daniel Szczepanik](https://github.com/Drakeoon) for his commitment, effort and collaborative work on adjusting components, +- [Olimpia Zurek](https://github.com/OlimpiaZurek) for her contribution and help, +- [Aleksandra Desmurs-Linczewska](https://github.com/p-syche), [Jan Jaworski](https://github.com/jaworek) and [Kewin Wereszczyński](https://github.com/kwereszczynski) for checking and testing changes as well as providing valuable feedback, +- [Bruno Castro](https://github.com/brunohkbx) for creating a long-awaited `Tooltip` component, +- [Muhammad Hur Ali](https://github.com/hurali97) for various bug fixes and `List` subcomponents adjustments, + +and, last but not least, [Satya Sahoo](https://github.com/satya164) for his mentoring during the process. diff --git a/docs/docs/guides/11-ripple-effect.md b/docs/versioned_docs/version-5.x/guides/11-ripple-effect.md similarity index 100% rename from docs/docs/guides/11-ripple-effect.md rename to docs/versioned_docs/version-5.x/guides/11-ripple-effect.md diff --git a/docs/docs/guides/_category_.json b/docs/versioned_docs/version-5.x/guides/_category_.json similarity index 100% rename from docs/docs/guides/_category_.json rename to docs/versioned_docs/version-5.x/guides/_category_.json diff --git a/docs/docs/showcase.mdx b/docs/versioned_docs/version-5.x/showcase.mdx similarity index 78% rename from docs/docs/showcase.mdx rename to docs/versioned_docs/version-5.x/showcase.mdx index db58df8f24..7024ab018d 100644 --- a/docs/docs/showcase.mdx +++ b/docs/versioned_docs/version-5.x/showcase.mdx @@ -2,7 +2,7 @@ title: Showcase --- -import Showcase from '../src/components/Showcase.tsx'; +import Showcase from '@site/src/components/Showcase.tsx'; # Who's using Paper? diff --git a/docs/versioned_docs/version-6.x/components/ActivityIndicator.mdx b/docs/versioned_docs/version-6.x/components/ActivityIndicator.mdx new file mode 100644 index 0000000000..51fcd3899d --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/ActivityIndicator.mdx @@ -0,0 +1,96 @@ +--- +title: ActivityIndicator +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Activity indicator is used to present progress of some activity in the app. +It can be used as a drop-in replacement for the ActivityIndicator shipped with React Native. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ActivityIndicator, Palette } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### animating + +
+ + + +
+ +### color + +
+ + + +
+ +### size + +
+ + + +
+ +### hidesWhenStopped + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/Appbar.mdx b/docs/versioned_docs/version-6.x/components/Appbar/Appbar.mdx new file mode 100644 index 0000000000..b9f52f72ae --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Appbar/Appbar.mdx @@ -0,0 +1,174 @@ +--- +title: Appbar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to display action items in a bar. It can be placed at the top or bottom. +The top bar usually contains the screen title, controls such as navigation buttons, menu button etc. +The bottom bar usually provides access to a drawer and up to four actions. + +By default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. +See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations + + + + + + + +## Usage +### Top bar +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} /> + + {}} /> + {}} /> + +); + +export default MyComponent; +``` + +### Bottom bar +```js +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { Appbar, FAB, useTheme } from 'react-native-paper'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; + +const BOTTOM_APPBAR_HEIGHT = 80; +const MEDIUM_FAB_HEIGHT = 56; + +const MyComponent = () => { + const { bottom } = useSafeAreaInsets(); + const theme = useTheme(); + + return ( + + {}} /> + {}} /> + {}} /> + {}} /> + {}} + style={[ + styles.fab, + { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 }, + ]} + /> + + ); +}; + +const styles = StyleSheet.create({ + bottom: { + backgroundColor: 'aquamarine', + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + }, + fab: { + position: 'absolute', + right: 16, + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dark + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### elevated Available in v5.x with theme version 3 + +
+ + + +
+ +### safeAreaInsets + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarAction.mdx b/docs/versioned_docs/version-6.x/components/Appbar/AppbarAction.mdx new file mode 100644 index 0000000000..d4b3fe6ca1 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Appbar/AppbarAction.mdx @@ -0,0 +1,134 @@ +--- +title: Appbar.Action +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display an action item in the appbar. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; +import { Platform } from 'react-native'; + +const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; + +const MyComponent = () => ( + + + {}} /> + {}} /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### color + +
+ + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### disabled + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### isLeading Available in v5.x with theme version 3 + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarBackAction.mdx b/docs/versioned_docs/version-6.x/components/Appbar/AppbarBackAction.mdx new file mode 100644 index 0000000000..1bddbc1a47 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Appbar/AppbarBackAction.mdx @@ -0,0 +1,101 @@ +--- +title: Appbar.BackAction +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display a back button in the appbar. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### color + +
+ + + +
+ +### size + +
+ + + +
+ +### disabled + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarContent.mdx b/docs/versioned_docs/version-6.x/components/Appbar/AppbarContent.mdx new file mode 100644 index 0000000000..f0cd732f9e --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Appbar/AppbarContent.mdx @@ -0,0 +1,129 @@ +--- +title: Appbar.Content +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display a title in an appbar. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => ( + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### titleRef + +
+ + + +
+ +### onPress + +
+ + + +
+ +### disabled + +
+ + + +
+ +### color + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarHeader.mdx b/docs/versioned_docs/version-6.x/components/Appbar/AppbarHeader.mdx new file mode 100644 index 0000000000..131dbdec53 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Appbar/AppbarHeader.mdx @@ -0,0 +1,125 @@ +--- +title: Appbar.Header +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to use as a header at the top of the screen. +It can contain the screen title, controls such as navigation buttons, menu button etc. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Appbar } from 'react-native-paper'; + +const MyComponent = () => { + const _goBack = () => console.log('Went back'); + + const _handleSearch = () => console.log('Searching'); + + const _handleMore = () => console.log('Shown more'); + + return ( + + + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dark + +
+ + + +
+ +### statusBarHeight + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### elevated Available in v5.x with theme version 3 + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Avatar/AvatarIcon.mdx b/docs/versioned_docs/version-6.x/components/Avatar/AvatarIcon.mdx new file mode 100644 index 0000000000..8d2cb9f401 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Avatar/AvatarIcon.mdx @@ -0,0 +1,81 @@ +--- +title: Avatar.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Avatars can be used to represent people in a graphical way. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar } from 'react-native-paper'; + +const MyComponent = () => ( + +); +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### color + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Avatar/AvatarImage.mdx b/docs/versioned_docs/version-6.x/components/Avatar/AvatarImage.mdx new file mode 100644 index 0000000000..79c7c9bb0a --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Avatar/AvatarImage.mdx @@ -0,0 +1,122 @@ +--- +title: Avatar.Image +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Avatars can be used to represent people in a graphical way. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar } from 'react-native-paper'; + +const MyComponent = () => ( + +); +export default MyComponent +``` + + + ## Props + + + + +
+ +### source (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### style + +
+ + + +
+ +### onError + +
+ + + +
+ +### onLayout + +
+ + + +
+ +### onLoad + +
+ + + +
+ +### onLoadEnd + +
+ + + +
+ +### onLoadStart + +
+ + + +
+ +### onProgress + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Avatar/AvatarText.mdx b/docs/versioned_docs/version-6.x/components/Avatar/AvatarText.mdx new file mode 100644 index 0000000000..ec1379adf4 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Avatar/AvatarText.mdx @@ -0,0 +1,97 @@ +--- +title: Avatar.Text +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Avatars can be used to represent people in a graphical way. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar } from 'react-native-paper'; + +const MyComponent = () => ( + +); +``` + + + ## Props + + + + +
+ +### label (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### color + +
+ + + +
+ +### style + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Badge.mdx b/docs/versioned_docs/version-6.x/components/Badge.mdx new file mode 100644 index 0000000000..77326a9bf0 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Badge.mdx @@ -0,0 +1,96 @@ +--- +title: Badge +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Badges are small status descriptors for UI elements. +A badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Badge } from 'react-native-paper'; + +const MyComponent = () => ( + 3 +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### visible + +
+ + + +
+ +### children + +
+ + + +
+ +### size + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Banner.mdx b/docs/versioned_docs/version-6.x/components/Banner.mdx new file mode 100644 index 0000000000..14c1107d0b --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Banner.mdx @@ -0,0 +1,172 @@ +--- +title: Banner +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Banner displays a prominent message and related actions. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Image } from 'react-native'; +import { Banner } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(true); + + return ( + setVisible(false), + }, + { + label: 'Learn more', + onPress: () => setVisible(false), + }, + ]} + icon={({size}) => ( + + )}> + There was a problem processing a transaction on your credit card. + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### visible (required) + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### icon + +
+ + + +
+ +### actions + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + +
+ +### onShowAnimationFinished + +
+ + + +
+ +### onHideAnimationFinished + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigation.mdx b/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigation.mdx new file mode 100644 index 0000000000..fb90edf463 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigation.mdx @@ -0,0 +1,308 @@ +--- +title: BottomNavigation +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar. +It is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar). + +By default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. +See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { BottomNavigation, Text } from 'react-native-paper'; + +const MusicRoute = () => Music; + +const AlbumsRoute = () => Albums; + +const RecentsRoute = () => Recents; + +const NotificationsRoute = () => Notifications; + +const MyComponent = () => { + const [index, setIndex] = React.useState(0); + const [routes] = React.useState([ + { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'}, + { key: 'albums', title: 'Albums', focusedIcon: 'album' }, + { key: 'recents', title: 'Recents', focusedIcon: 'history' }, + { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' }, + ]); + + const renderScene = BottomNavigation.SceneMap({ + music: MusicRoute, + albums: AlbumsRoute, + recents: RecentsRoute, + notifications: NotificationsRoute, + }); + + return ( + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### shifting + +
+ + + +
+ +### labeled + +
+ + + +
+ +### compact + +
+ + + +
+ +### navigationState (required) + +
+ + + +
+ +### onIndexChange (required) + +
+ + + +
+ +### renderScene (required) + +
+ + + +
+ +### renderIcon + +
+ + + +
+ +### renderLabel + +
+ + + +
+ +### renderTouchable + +
+ + + +
+ +### getAccessibilityLabel + +
+ + + +
+ +### getBadge + +
+ + + +
+ +### getLabelText + +
+ + + +
+ +### getLazy + +
+ + + +
+ +### getTestID + +
+ + + +
+ +### onTabPress + +
+ + + +
+ +### onTabLongPress + +
+ + + +
+ +### activeColor + +
+ + + +
+ +### inactiveColor + +
+ + + +
+ +### sceneAnimationEnabled + +
+ + + +
+ +### sceneAnimationType + +
+ + + +
+ +### sceneAnimationEasing + +
+ + + +
+ +### keyboardHidesNavigationBar + +
+ + + +
+ +### safeAreaInsets + +
+ + + +
+ +### barStyle + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### activeIndicatorStyle + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigationBar.mdx b/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigationBar.mdx new file mode 100644 index 0000000000..f5d064f3b1 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigationBar.mdx @@ -0,0 +1,285 @@ +--- +title: BottomNavigation.Bar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/). + + + + + + + + + ## Usage +### without React Navigation +```js +import React from 'react'; +import { useState } from 'react'; +import { View } from 'react-native'; +import { BottomNavigation, Text, Provider } from 'react-native-paper'; +import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; + +function HomeScreen() { + return ( + + Home! + + ); +} + +function SettingsScreen() { + return ( + + Settings! + + ); +} + +export default function MyComponent() { + const [index, setIndex] = useState(0); + + const routes = [ + { key: 'home', title: 'Home', icon: 'home' }, + { key: 'settings', title: 'Settings', icon: 'cog' }, + ]; + + const renderScene = ({ route }) => { + switch (route.key) { + case 'home': + return ; + case 'settings': + return ; + default: + return null; + } + }; + + return ( + + {renderScene({ route: routes[index] })} + { + const newIndex = routes.findIndex((r) => r.key === route.key); + if (newIndex !== -1) { + setIndex(newIndex); + } + }} + renderIcon={({ route, color }) => ( + + )} + getLabelText={({ route }) => route.title} + /> + + ); +} +``` + + ### with React Navigation + \n Home!\n
\n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst MyTabs = createBottomTabNavigator({\n screenOptions: {\n animation: 'shift',\n },\n tabBar: ({ navigation, state, descriptors, insets }) => (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n ),\n screens: {\n Home: {\n screen: HomeScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n Settings: {\n screen: SettingsScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n },\n});\n\nconst Navigation = createStaticNavigation(MyTabs);\n\nexport default function App() {\n return (\n \n \n \n \n \n );\n}","dynamic":"import { Text, View } from 'react-native';\nimport { NavigationContainer, CommonActions } from '@react-navigation/native';\nimport { createBottomTabNavigator } from '@react-navigation/bottom-tabs';\nimport { Provider, BottomNavigation } from 'react-native-paper';\nimport MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst Tab = createBottomTabNavigator();\n\nexport default function App() {\n return (\n \n \n (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n )}>\n (\n \n ),\n }}\n />\n (\n \n ),\n }}\n />\n \n \n \n );\n}\n"}}} /> + + + + ## Props + + + + +
+ +### shifting + +
+ + + +
+ +### labeled + +
+ + + +
+ +### compact + +
+ + + +
+ +### navigationState (required) + +
+ + + +
+ +### renderIcon + +
+ + + +
+ +### renderLabel + +
+ + + +
+ +### renderTouchable + +
+ + + +
+ +### getAccessibilityLabel + +
+ + + +
+ +### getBadge + +
+ + + +
+ +### getLabelText + +
+ + + +
+ +### getTestID + +
+ + + +
+ +### onTabPress (required) + +
+ + + +
+ +### onTabLongPress + +
+ + + +
+ +### activeColor + +
+ + + +
+ +### inactiveColor + +
+ + + +
+ +### animationEasing + +
+ + + +
+ +### keyboardHidesNavigationBar + +
+ + + +
+ +### safeAreaInsets + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### activeIndicatorStyle + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Button/Button.mdx b/docs/versioned_docs/version-6.x/components/Button/Button.mdx new file mode 100644 index 0000000000..f85bbe1d76 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Button/Button.mdx @@ -0,0 +1,273 @@ +--- +title: Button +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A button is component that the user can press to trigger an action. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Button } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### mode + +
+ + + +
+ +### dark + +
+ + + +
+ +### compact + +
+ + + +
+ +### buttonColor + +
+ + + +
+ +### textColor + +
+ + + +
+ +### loading + +
+ + + +
+ +### icon + +
+ + + +
+ +### disabled + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### uppercase + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityHint + +
+ + + +
+ +### accessibilityRole + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### style + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### theme + +
+ + + +
+ +### touchableRef + +
+ + + +
+ +### ref + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Card/Card.mdx b/docs/versioned_docs/version-6.x/components/Card/Card.mdx new file mode 100644 index 0000000000..98fbe67108 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Card/Card.mdx @@ -0,0 +1,172 @@ +--- +title: Card +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A card is a sheet of material that serves as an entry point to more detailed information. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar, Button, Card, Text } from 'react-native-paper'; + +const LeftContent = props => + +const MyComponent = () => ( + + + + Card title + Card content + + + + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### mode + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### disabled + +
+ + + +
+ +### elevation + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### accessible + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardActions.mdx b/docs/versioned_docs/version-6.x/components/Card/CardActions.mdx new file mode 100644 index 0000000000..856f640420 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Card/CardActions.mdx @@ -0,0 +1,72 @@ +--- +title: Card.Actions +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a list of actions inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Card, Button } from 'react-native-paper'; + +const MyComponent = () => ( + + + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardContent.mdx b/docs/versioned_docs/version-6.x/components/Card/CardContent.mdx new file mode 100644 index 0000000000..bb7496c1dd --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Card/CardContent.mdx @@ -0,0 +1,64 @@ +--- +title: Card.Content +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show content inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Card, Text } from 'react-native-paper'; + +const MyComponent = () => ( + + + Card title + Card content + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardCover.mdx b/docs/versioned_docs/version-6.x/components/Card/CardCover.mdx new file mode 100644 index 0000000000..0bfac77665 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Card/CardCover.mdx @@ -0,0 +1,63 @@ +--- +title: Card.Cover +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a cover image inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Card } from 'react-native-paper'; + +const MyComponent = () => ( + + + +); + +export default MyComponent; +``` + + + + + ## Props + ### Image props + + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardTitle.mdx b/docs/versioned_docs/version-6.x/components/Card/CardTitle.mdx new file mode 100644 index 0000000000..912d3f51a8 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Card/CardTitle.mdx @@ -0,0 +1,176 @@ +--- +title: Card.Title +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a title, subtitle and an avatar inside a Card. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Avatar, Card, IconButton } from 'react-native-paper'; + +const MyComponent = () => ( + } + right={(props) => {}} />} + /> +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### titleNumberOfLines + +
+ + + +
+ +### titleVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### subtitle + +
+ + + +
+ +### subtitleStyle + +
+ + + +
+ +### subtitleNumberOfLines + +
+ + + +
+ +### subtitleVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### left + +
+ + + +
+ +### leftStyle + +
+ + + +
+ +### right + +
+ + + +
+ +### rightStyle + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### subtitleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Checkbox/Checkbox.mdx b/docs/versioned_docs/version-6.x/components/Checkbox/Checkbox.mdx new file mode 100644 index 0000000000..d8c4419527 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Checkbox/Checkbox.mdx @@ -0,0 +1,124 @@ +--- +title: Checkbox +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Checkboxes allow the selection of multiple options from a set. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Checkbox } from 'react-native-paper'; + +const MyComponent = () => { + const [checked, setChecked] = React.useState(false); + + return ( + { + setChecked(!checked); + }} + /> + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### status (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### error + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Checkbox/CheckboxItem.mdx b/docs/versioned_docs/version-6.x/components/Checkbox/CheckboxItem.mdx new file mode 100644 index 0000000000..faa96df173 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Checkbox/CheckboxItem.mdx @@ -0,0 +1,180 @@ +--- +title: Checkbox.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox. + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Checkbox } from 'react-native-paper'; + +const MyComponent = () => ( + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### status (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### label (required) + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### style + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### labelVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### position + +
+ + + +
+ +### hitSlop + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Chip/Chip.mdx b/docs/versioned_docs/version-6.x/components/Chip/Chip.mdx new file mode 100644 index 0000000000..87b98dc6fc --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Chip/Chip.mdx @@ -0,0 +1,283 @@ +--- +title: Chip +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Chips are compact elements that can represent inputs, attributes, or actions. +They can have an icon or avatar on the left, and a close button icon on the right. +They are typically used to: +
    +
  • Present multiple options
  • +
  • Represent attributes active or chosen
  • +
  • Present filter options
  • +
  • Trigger actions related to primary content
  • +
+ + + + + + + +## Usage +```js +import * as React from 'react'; +import { Chip } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')}>Example Chip +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### mode + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### icon + +
+ + + +
+ +### avatar + +
+ + + +
+ +### closeIcon + +
+ + + +
+ +### selected + +
+ + + +
+ +### selectedColor + +
+ + + +
+ +### showSelectedOverlay Available in v5.x with theme version 3 + +
+ + + +
+ +### showSelectedCheck + +
+ + + +
+ +### disabled + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### closeIconAccessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### onClose + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### compact Available in v5.x with theme version 3 + +
+ + + +
+ +### elevated Available in v5.x with theme version 3 + +
+ + + +
+ +### textStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### ellipsizeMode + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### accessibilityRole + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTable.mdx b/docs/versioned_docs/version-6.x/components/DataTable/DataTable.mdx new file mode 100644 index 0000000000..510a195f7e --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/DataTable/DataTable.mdx @@ -0,0 +1,127 @@ +--- +title: DataTable +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Data tables allow displaying sets of data. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => { + const [page, setPage] = React.useState(0); + const [numberOfItemsPerPageList] = React.useState([2, 3, 4]); + const [itemsPerPage, onItemsPerPageChange] = React.useState( + numberOfItemsPerPageList[0] + ); + + const [items] = React.useState([ + { + key: 1, + name: 'Cupcake', + calories: 356, + fat: 16, + }, + { + key: 2, + name: 'Eclair', + calories: 262, + fat: 16, + }, + { + key: 3, + name: 'Frozen yogurt', + calories: 159, + fat: 6, + }, + { + key: 4, + name: 'Gingerbread', + calories: 305, + fat: 3.7, + }, + ]); + + const from = page * itemsPerPage; + const to = Math.min((page + 1) * itemsPerPage, items.length); + + React.useEffect(() => { + setPage(0); + }, [itemsPerPage]); + + return ( + + + Dessert + Calories + Fat + + + {items.slice(from, to).map((item) => ( + + {item.name} + {item.calories} + {item.fat} + + ))} + + setPage(page)} + label={`${from + 1}-${to} of ${items.length}`} + numberOfItemsPerPageList={numberOfItemsPerPageList} + numberOfItemsPerPage={itemsPerPage} + onItemsPerPageChange={onItemsPerPageChange} + showFastPaginationControls + selectPageDropdownLabel={'Rows per page'} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableCell.mdx b/docs/versioned_docs/version-6.x/components/DataTable/DataTableCell.mdx new file mode 100644 index 0000000000..b21f71127e --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/DataTable/DataTableCell.mdx @@ -0,0 +1,109 @@ +--- +title: DataTable.Cell +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a single cell inside of a table. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + 1 + 2 + 3 + 4 + +); + +export default MyComponent; +``` + +If you want to support multiline text, please use View instead, as multiline text doesn't comply with +MD Guidelines (https://github.com/callstack/react-native-paper/issues/2381). + + + + + ## Props + ### TouchableRipple props + + + +
+ +### children (required) + +
+ + + +
+ +### numeric + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### textStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableHeader.mdx b/docs/versioned_docs/version-6.x/components/DataTable/DataTableHeader.mdx new file mode 100644 index 0000000000..bf318e56db --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/DataTable/DataTableHeader.mdx @@ -0,0 +1,77 @@ +--- +title: DataTable.Header +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to display title in table header. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + + + Dessert + + Calories + Fat (g) + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTablePagination.mdx b/docs/versioned_docs/version-6.x/components/DataTable/DataTablePagination.mdx new file mode 100644 index 0000000000..eb6afe233e --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/DataTable/DataTablePagination.mdx @@ -0,0 +1,187 @@ +--- +title: DataTable.Pagination +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show pagination for data table. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const numberOfItemsPerPageList = [2, 3, 4]; + +const items = [ + { + key: 1, + name: 'Page 1', + }, + { + key: 2, + name: 'Page 2', + }, + { + key: 3, + name: 'Page 3', + }, +]; + +const MyComponent = () => { + const [page, setPage] = React.useState(0); + const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]); + const from = page * numberOfItemsPerPage; + const to = Math.min((page + 1) * numberOfItemsPerPage, items.length); + + React.useEffect(() => { + setPage(0); + }, [numberOfItemsPerPage]); + + return ( + + setPage(page)} + label={`${from + 1}-${to} of ${items.length}`} + showFastPaginationControls + numberOfItemsPerPageList={numberOfItemsPerPageList} + numberOfItemsPerPage={numberOfItemsPerPage} + onItemsPerPageChange={onItemsPerPageChange} + selectPageDropdownLabel={'Rows per page'} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### page (required) + +
+ + + +
+ +### numberOfPages (required) + +
+ + + +
+ +### onPageChange (required) + +
+ + + +
+ +### showFastPaginationControls + +
+ + + +
+ +### theme + +
+ + + +
+ +### numberOfItemsPerPage + +
+ + + +
+ +### numberOfItemsPerPageList + +
+ + + +
+ +### onItemsPerPageChange + +
+ + + +
+ +### selectPageDropdownLabel + +
+ + + +
+ +### selectPageDropdownAccessibilityLabel + +
+ + + +
+ +### label + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableRow.mdx b/docs/versioned_docs/version-6.x/components/DataTable/DataTableRow.mdx new file mode 100644 index 0000000000..e2765dcca8 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/DataTable/DataTableRow.mdx @@ -0,0 +1,90 @@ +--- +title: DataTable.Row +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a single row inside of a table. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + 1 + 2 + 3 + 4 + +); + +export default MyComponent; +``` + + + + + ## Props + ### TouchableRipple props + + + +
+ +### children (required) + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### pointerEvents + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableTitle.mdx b/docs/versioned_docs/version-6.x/components/DataTable/DataTableTitle.mdx new file mode 100644 index 0000000000..8967b338c8 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/DataTable/DataTableTitle.mdx @@ -0,0 +1,125 @@ +--- +title: DataTable.Title +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to display title in table header. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { DataTable } from 'react-native-paper'; + +const MyComponent = () => ( + + + + Dessert + + Calories + Fat (g) + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### numeric + +
+ + + +
+ +### sortDirection + +
+ + + +
+ +### numberOfLines + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### textStyle + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/Dialog.mdx b/docs/versioned_docs/version-6.x/components/Dialog/Dialog.mdx new file mode 100644 index 0000000000..29450a344b --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Dialog/Dialog.mdx @@ -0,0 +1,136 @@ +--- +title: Dialog +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. +To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const showDialog = () => setVisible(true); + + const hideDialog = () => setVisible(false); + + return ( + + + + + + Alert + + This is simple dialog + + + + + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dismissable + +
+ + + +
+ +### dismissableBackButton + +
+ + + +
+ +### onDismiss + +
+ + + +
+ +### visible + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogActions.mdx b/docs/versioned_docs/version-6.x/components/Dialog/DialogActions.mdx new file mode 100644 index 0000000000..b8936894a6 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Dialog/DialogActions.mdx @@ -0,0 +1,80 @@ +--- +title: Dialog.Actions +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a list of actions in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Button, Dialog, Portal } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogContent.mdx b/docs/versioned_docs/version-6.x/components/Dialog/DialogContent.mdx new file mode 100644 index 0000000000..18dc952022 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Dialog/DialogContent.mdx @@ -0,0 +1,71 @@ +--- +title: Dialog.Content +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show content in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + This is simple dialog + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogIcon.mdx b/docs/versioned_docs/version-6.x/components/Dialog/DialogIcon.mdx new file mode 100644 index 0000000000..30e0d48f25 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Dialog/DialogIcon.mdx @@ -0,0 +1,101 @@ +--- +title: Dialog.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +@supported Available in v5.x with theme version 3 +A component to show an icon in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + This is a title + + This is simple dialog + + + + ); +}; + +const styles = StyleSheet.create({ + title: { + textAlign: 'center', + }, +}) + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### color + +
+ + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogScrollArea.mdx b/docs/versioned_docs/version-6.x/components/Dialog/DialogScrollArea.mdx new file mode 100644 index 0000000000..cc49955a83 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Dialog/DialogScrollArea.mdx @@ -0,0 +1,87 @@ +--- +title: Dialog.ScrollArea +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a scrollable content in a Dialog. The component only provides appropriate styling. +For the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ScrollView } from 'react-native'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + + + This is a scrollable area + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogTitle.mdx b/docs/versioned_docs/version-6.x/components/Dialog/DialogTitle.mdx new file mode 100644 index 0000000000..4755404144 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Dialog/DialogTitle.mdx @@ -0,0 +1,80 @@ +--- +title: Dialog.Title +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a title in a Dialog. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Dialog, Portal, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const hideDialog = () => setVisible(false); + + return ( + + + This is a title + + This is simple dialog + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Divider.mdx b/docs/versioned_docs/version-6.x/components/Divider.mdx new file mode 100644 index 0000000000..d3b868eefe --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Divider.mdx @@ -0,0 +1,93 @@ +--- +title: Divider +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A divider is a thin, lightweight separator that groups content in lists and page layouts. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Divider, Text } from 'react-native-paper'; + +const MyComponent = () => ( + + Lemon + + Mango + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### leftInset Renamed from 'inset' to 'leftInset` in v5.x + +
+ + + +
+ +### horizontalInset Available in v5.x with theme version 3 + +
+ + + +
+ +### bold Available in v5.x with theme version 3 + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Drawer/DrawerCollapsedItem.mdx b/docs/versioned_docs/version-6.x/components/Drawer/DrawerCollapsedItem.mdx new file mode 100644 index 0000000000..7193539e8f --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Drawer/DrawerCollapsedItem.mdx @@ -0,0 +1,149 @@ +--- +title: Drawer.CollapsedItem +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Note: Available in v5.x with theme version 3 + +Collapsed component used to show an action item with an icon and optionally label in a navigation drawer. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Drawer } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### label + +
+ + + +
+ +### badge + +
+ + + +
+ +### disabled + +
+ + + +
+ +### focusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x + +
+ + + +
+ +### unfocusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x + +
+ + + +
+ +### active + +
+ + + +
+ +### onPress + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Drawer/DrawerItem.mdx b/docs/versioned_docs/version-6.x/components/Drawer/DrawerItem.mdx new file mode 100644 index 0000000000..8320143a75 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Drawer/DrawerItem.mdx @@ -0,0 +1,147 @@ +--- +title: Drawer.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to show an action item with an icon and a label in a navigation drawer. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Drawer } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### label (required) + +
+ + + +
+ +### icon + +
+ + + +
+ +### active + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### background + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### right + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Drawer/DrawerSection.mdx b/docs/versioned_docs/version-6.x/components/Drawer/DrawerSection.mdx new file mode 100644 index 0000000000..63c7b98497 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Drawer/DrawerSection.mdx @@ -0,0 +1,110 @@ +--- +title: Drawer.Section +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to group content inside a navigation drawer. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Drawer } from 'react-native-paper'; + +const MyComponent = () => { + const [active, setActive] = React.useState(''); + + return ( + + setActive('first')} + /> + setActive('second')} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### showDivider + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/FAB/FAB.mdx b/docs/versioned_docs/version-6.x/components/FAB/FAB.mdx new file mode 100644 index 0000000000..4c3990ce52 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/FAB/FAB.mdx @@ -0,0 +1,173 @@ +--- +title: FAB +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A floating action button represents the primary action on a screen. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { FAB } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')} + /> +); + +const styles = StyleSheet.create({ + fab: { + position: 'absolute', + margin: 16, + right: 0, + bottom: 0, + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### variant + +
+ + + +
+ +### containerColor + +
+ + + +
+ +### contentColor + +
+ + + +
+ +### size + +
+ + + +
+ +### visible + +
+ + + +
+ +### onPress + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityState + +
+ + + +
+ +### background + +
+ + + +
+ +### style + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + +
+ +### ref + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/FAB/FABExtended.mdx b/docs/versioned_docs/version-6.x/components/FAB/FABExtended.mdx new file mode 100644 index 0000000000..2ab71359a8 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/FAB/FABExtended.mdx @@ -0,0 +1,199 @@ +--- +title: Extended +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +An extended floating action button represents the primary action on a screen +and shows a label next to the icon. Animates between expanded (icon + label) +and collapsed (icon only) states. + + + + + +## Usage +```js +import * as React from 'react'; +import { StyleSheet } from 'react-native'; +import { FAB } from 'react-native-paper'; + +const MyComponent = () => { + const [expanded, setExpanded] = React.useState(true); + + return ( + setExpanded((v) => !v)} + style={styles.fab} + /> + ); +}; + +const styles = StyleSheet.create({ + fab: { + position: 'absolute', + margin: 16, + left: 0, + bottom: 0, + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### label (required) + +
+ + + +
+ +### variant + +
+ + + +
+ +### containerColor + +
+ + + +
+ +### contentColor + +
+ + + +
+ +### size + +
+ + + +
+ +### expanded (required) + +
+ + + +
+ +### visible + +
+ + + +
+ +### onPress + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityState + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### background + +
+ + + +
+ +### style + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + +
+ +### ref + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/FAB/FABMenu.mdx b/docs/versioned_docs/version-6.x/components/FAB/FABMenu.mdx new file mode 100644 index 0000000000..e0c82b0f86 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/FAB/FABMenu.mdx @@ -0,0 +1,122 @@ +--- +title: Menu +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Floating action button menu. Wraps a trigger FAB; when `expanded` is true, +items appear stacked above and the trigger morphs into the spec'd close +button (`shape: 'full'`, 56 dp, saturated role color). + +No visual backdrop and no outside-tap dismiss — that matches the MD3 spec +and lets the user keep interacting with the content underneath. Dismiss +via the close button or by tapping an item. + + + + + + + +## Usage +```tsx +const [open, setOpen] = React.useState(false); + + + setOpen(false)} + trigger={{ icon: 'plus', variant: 'primary', onPress: () => setOpen(true) }} + items={[ + { icon: 'email', label: 'Send', onPress: () => {} }, + { icon: 'bell', label: 'Remind', onPress: () => {} }, + ]} + /> + +``` + + + ## Props + + + + +
+ +### expanded (required) + +
+ + + +
+ +### onDismiss (required) + +
+ + + +
+ +### trigger (required) + +
+ + + +
+ +### alignment + +
+ + + +
+ +### closeIcon + +
+ + + +
+ +### items (required) + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Icon.mdx b/docs/versioned_docs/version-6.x/components/Icon.mdx new file mode 100644 index 0000000000..5dad95ddd6 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Icon.mdx @@ -0,0 +1,95 @@ +--- +title: Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +An icon component which renders icon from vector library. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Icon, Palette } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### size (required) + +
+ + + +
+ +### allowFontScaling + +
+ + + +
+ +### source (required) + +
+ + + +
+ +### color + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/IconButton/IconButton.mdx b/docs/versioned_docs/version-6.x/components/IconButton/IconButton.mdx new file mode 100644 index 0000000000..00d0a69e4e --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/IconButton/IconButton.mdx @@ -0,0 +1,182 @@ +--- +title: IconButton +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +An icon button is a button which displays only an icon without a label. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { IconButton, Palette } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')} + /> +); + +export default MyComponent; +``` + + + + + ## Props + ### TouchableRipple props + + + +
+ +### icon (required) + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### iconColor Renamed from 'color' to 'iconColor' in v5.x + +
+ + + +
+ +### containerColor + +
+ + + +
+ +### selected + +
+ + + +
+ +### size + +
+ + + +
+ +### disabled + +
+ + + +
+ +### animated + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### onPress + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + +
+ +### loading + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListAccordion.mdx b/docs/versioned_docs/version-6.x/components/List/ListAccordion.mdx new file mode 100644 index 0000000000..eed29d3232 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/List/ListAccordion.mdx @@ -0,0 +1,265 @@ +--- +title: List.Accordion +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display an expandable list item. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List } from 'react-native-paper'; + +const MyComponent = () => { + const [expanded, setExpanded] = React.useState(true); + + const handlePress = () => setExpanded(!expanded); + + return ( + + }> + + + + + } + expanded={expanded} + onPress={handlePress}> + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### description + +
+ + + +
+ +### left + +
+ + + +
+ +### right + +
+ + + +
+ +### expanded + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### delayLongPress + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### theme + +
+ + + +
+ +### background + +
+ + + +
+ +### style + +
+ + + +
+ +### containerStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### descriptionStyle + +
+ + + +
+ +### titleNumberOfLines + +
+ + + +
+ +### descriptionNumberOfLines + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### descriptionMaxFontSizeMultiplier + +
+ + + +
+ +### id + +
+ + + +
+ +### testID + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### pointerEvents + +
+ + + +
+ +### hitSlop + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListAccordionGroup.mdx b/docs/versioned_docs/version-6.x/components/List/ListAccordionGroup.mdx new file mode 100644 index 0000000000..7224e87927 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/List/ListAccordionGroup.mdx @@ -0,0 +1,85 @@ +--- +title: List.AccordionGroup +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work. +List.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version. +At most one Accordion can be expanded at a given time. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { List } from 'react-native-paper'; + +const MyComponent = () => ( + + + + + + + + + + List.Accordion can be wrapped because implementation uses React.Context. + + + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### onAccordionPress + +
+ + + +
+ +### expandedId + +
+ + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListIcon.mdx b/docs/versioned_docs/version-6.x/components/List/ListIcon.mdx new file mode 100644 index 0000000000..c8b5ee12df --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/List/ListIcon.mdx @@ -0,0 +1,79 @@ +--- +title: List.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show an icon in a list item. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List, Palette } from 'react-native-paper'; + +const MyComponent = () => ( + <> + + + + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### color + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListItem.mdx b/docs/versioned_docs/version-6.x/components/List/ListItem.mdx new file mode 100644 index 0000000000..7ba8aa8dfa --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/List/ListItem.mdx @@ -0,0 +1,201 @@ +--- +title: List.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show tiles inside a List. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List } from 'react-native-paper'; + +const MyComponent = () => ( + } + /> +); + +export default MyComponent; +``` + + + + + ## Props + ### TouchableRipple props + + + +
+ +### title (required) + +
+ + + +
+ +### description + +
+ + + +
+ +### left + +
+ + + +
+ +### right + +
+ + + +
+ +### onPress + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### containerStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### descriptionStyle + +
+ + + +
+ +### titleNumberOfLines + +
+ + + +
+ +### descriptionNumberOfLines + +
+ + + +
+ +### titleEllipsizeMode + +
+ + + +
+ +### descriptionEllipsizeMode + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### descriptionMaxFontSizeMultiplier + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListSection.mdx b/docs/versioned_docs/version-6.x/components/List/ListSection.mdx new file mode 100644 index 0000000000..c90da6872d --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/List/ListSection.mdx @@ -0,0 +1,90 @@ +--- +title: List.Section +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to group list items. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { List, Palette } from 'react-native-paper'; + +const MyComponent = () => ( + + Some title + } /> + } + /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### theme + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListSubheader.mdx b/docs/versioned_docs/version-6.x/components/List/ListSubheader.mdx new file mode 100644 index 0000000000..a5f507ec34 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/List/ListSubheader.mdx @@ -0,0 +1,63 @@ +--- +title: List.Subheader +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component used to display a header in lists. + + + + + +## Usage +```js +import * as React from 'react'; +import { List } from 'react-native-paper'; + +const MyComponent = () => My List Title; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Menu/Menu.mdx b/docs/versioned_docs/version-6.x/components/Menu/Menu.mdx new file mode 100644 index 0000000000..0d4044f003 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Menu/Menu.mdx @@ -0,0 +1,190 @@ +--- +title: Menu +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Button, Menu, Divider, PaperProvider } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const openMenu = () => setVisible(true); + + const closeMenu = () => setVisible(false); + + return ( + + + Show menu}> + {}} title="Item 1" /> + {}} title="Item 2" /> + + {}} title="Item 3" /> + + + + ); +}; + +export default MyComponent; +``` + +### Note +When using `Menu` within a React Native's `Modal` component, you need to wrap all +`Modal` contents within a `PaperProvider` in order for the menu to show. This +wrapping is not necessary if you use Paper's `Modal` instead. + + + ## Props + + + + +
+ +### visible (required) + +
+ + + +
+ +### anchor (required) + +
+ + + +
+ +### anchorPosition + +
+ + + +
+ +### statusBarHeight + +
+ + + +
+ +### onDismiss + +
+ + + +
+ +### overlayAccessibilityLabel + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### elevation + +
+ + + +
+ +### mode + +
+ + + +
+ +### theme + +
+ + + +
+ +### keyboardShouldPersistTaps + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Menu/MenuItem.mdx b/docs/versioned_docs/version-6.x/components/Menu/MenuItem.mdx new file mode 100644 index 0000000000..3db471c8b8 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Menu/MenuItem.mdx @@ -0,0 +1,190 @@ +--- +title: Menu.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to show a single list item inside a Menu. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Menu } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} title="Redo" /> + {}} title="Undo" /> + {}} title="Cut" disabled /> + {}} title="Copy" disabled /> + {}} title="Paste" /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### title (required) + +
+ + + +
+ +### leadingIcon Renamed from 'icon' to 'leadingIcon' in v5.x + +
+ + + +
+ +### trailingIcon Available in v5.x with theme version 3 + +
+ + + +
+ +### disabled + +
+ + + +
+ +### dense Available in v5.x with theme version 3 + +
+ + + +
+ +### background + +
+ + + +
+ +### onPress + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### style + +
+ + + +
+ +### containerStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### titleStyle + +
+ + + +
+ +### theme + +
+ + + +
+ +### hitSlop + +
+ + + +
+ +### testID + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### accessibilityState + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Modal.mdx b/docs/versioned_docs/version-6.x/components/Modal.mdx new file mode 100644 index 0000000000..c96efbf7b5 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Modal.mdx @@ -0,0 +1,146 @@ +--- +title: Modal +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +The Modal component is a simple way to present content above an enclosing view. +To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component. +Note that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const showModal = () => setVisible(true); + const hideModal = () => setVisible(false); + const containerStyle = { backgroundColor: 'white', padding: 20 }; + + return ( + + + + Example Modal. Click outside this area to dismiss. + + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### dismissable + +
+ + + +
+ +### dismissableBackButton + +
+ + + +
+ +### onDismiss + +
+ + + +
+ +### overlayAccessibilityLabel + +
+ + + +
+ +### visible + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### contentContainerStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Portal/Portal.mdx b/docs/versioned_docs/version-6.x/components/Portal/Portal.mdx new file mode 100644 index 0000000000..9d3d325820 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Portal/Portal.mdx @@ -0,0 +1,74 @@ +--- +title: Portal +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Portal allows rendering a component at a different place in the parent tree. +You can use it to render content which should appear above other elements, similar to `Modal`. +It requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree. +Note that if you're using the `Provider` component, this already includes a `Portal.Host`. + + + + + +## Usage +```js +import * as React from 'react'; +import { Portal, Text } from 'react-native-paper'; + +const MyComponent = () => ( + + This is rendered at a different place + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### theme (required) + +
+ + + + + + + ## More Examples +
+ Toggle to grab more examples + +
+ + + + + diff --git a/docs/versioned_docs/version-6.x/components/Portal/PortalHost.mdx b/docs/versioned_docs/version-6.x/components/Portal/PortalHost.mdx new file mode 100644 index 0000000000..f54c0b8609 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Portal/PortalHost.mdx @@ -0,0 +1,56 @@ +--- +title: Portal.Host +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Portal host renders all of its children `Portal` elements. +For example, you can wrap a screen in `Portal.Host` to render items above the screen. +If you're using the `Provider` component, it already includes `Portal.Host`. + + + + + +## Usage +```js +import * as React from 'react'; +import { Text } from 'react-native'; +import { Portal } from 'react-native-paper'; + +const MyComponent = () => ( + + Content of the app + +); + +export default MyComponent; +``` + +Here any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`. + + + ## Props + + + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/ProgressBar.mdx b/docs/versioned_docs/version-6.x/components/ProgressBar.mdx new file mode 100644 index 0000000000..9492b0f7e9 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/ProgressBar.mdx @@ -0,0 +1,119 @@ +--- +title: ProgressBar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Progress bar is an indicator used to present progress of some activity in the app. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ProgressBar, Palette } from 'react-native-paper'; + +const MyComponent = () => ( + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### animatedValue + +
+ + + +
+ +### progress + +
+ + + +
+ +### color + +
+ + + +
+ +### indeterminate + +
+ + + +
+ +### visible + +
+ + + +
+ +### fillStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButton.mdx b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButton.mdx new file mode 100644 index 0000000000..260bd42fe7 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButton.mdx @@ -0,0 +1,123 @@ +--- +title: RadioButton +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Radio buttons allow the selection a single option from a set. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { RadioButton } from 'react-native-paper'; + +const MyComponent = () => { + const [checked, setChecked] = React.useState('first'); + + return ( + + setChecked('first')} + /> + setChecked('second')} + /> + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### value (required) + +
+ + + +
+ +### status + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonAndroid.mdx b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonAndroid.mdx new file mode 100644 index 0000000000..2faf286f16 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonAndroid.mdx @@ -0,0 +1,99 @@ +--- +title: RadioButton.Android +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + + + + + + + +Radio buttons allow the selection a single option from a set. +This component follows platform guidelines for Android, but can be used +on any platform. + + + + + ## Props + ### TouchableRipple props + + + +
+ +### value (required) + +
+ + + +
+ +### status + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonGroup.mdx b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonGroup.mdx new file mode 100644 index 0000000000..bcaa9333d0 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonGroup.mdx @@ -0,0 +1,81 @@ +--- +title: RadioButton.Group +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Radio button group allows to control a group of radio buttons. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { RadioButton, Text } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('first'); + + return ( + setValue(newValue)} value={value}> + + First + + + + Second + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### onValueChange (required) + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonIOS.mdx b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonIOS.mdx new file mode 100644 index 0000000000..83db4152cc --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonIOS.mdx @@ -0,0 +1,91 @@ +--- +title: RadioButton.IOS +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + + + + + + + +Radio buttons allow the selection a single option from a set. +This component follows platform guidelines for iOS, but can be used +on any platform. + + + + + ## Props + ### TouchableRipple props + + + +
+ +### value (required) + +
+ + + +
+ +### status + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### color + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonItem.mdx b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonItem.mdx new file mode 100644 index 0000000000..c5f863d44b --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonItem.mdx @@ -0,0 +1,202 @@ +--- +title: RadioButton.Item +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { RadioButton } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('first'); + + return ( + setValue(value)} value={value}> + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### value (required) + +
+ + + +
+ +### label (required) + +
+ + + +
+ +### disabled + +
+ + + +
+ +### background + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### uncheckedColor + +
+ + + +
+ +### color + +
+ + + +
+ +### status + +
+ + + +
+ +### style + +
+ + + +
+ +### labelStyle + +
+ + + +
+ +### labelVariant Available in v5.x with theme version 3 + +
+ + + +
+ +### labelMaxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### mode + +
+ + + +
+ +### position + +
+ + + +
+ +### hitSlop + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Searchbar.mdx b/docs/versioned_docs/version-6.x/components/Searchbar.mdx new file mode 100644 index 0000000000..657f96101f --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Searchbar.mdx @@ -0,0 +1,248 @@ +--- +title: Searchbar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Searchbar is a simple input box where users can type search queries. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Searchbar } from 'react-native-paper'; + +const MyComponent = () => { + const [searchQuery, setSearchQuery] = React.useState(''); + + return ( + + ); +}; + +export default MyComponent; + +``` + + + ## Props + + + + +
+ +### placeholder + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### onChangeText + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### icon + +
+ + + +
+ +### iconColor + +
+ + + +
+ +### onIconPress + +
+ + + +
+ +### onClearIconPress + +
+ + + +
+ +### searchAccessibilityLabel + +
+ + + +
+ +### clearIcon + +
+ + + +
+ +### clearAccessibilityLabel + +
+ + + +
+ +### traileringIcon Available in v5.x with theme version 3 + +
+ + + +
+ +### traileringIconColor Available in v5.x with theme version 3 + +
+ + + +
+ +### onTraileringIconPress + +
+ + + +
+ +### traileringIconAccessibilityLabel + +
+ + + +
+ +### right Available in v5.x with theme version 3 + +
+ + + +
+ +### showDivider Available in v5.x with theme version 3 + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### inputStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### loading + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + +
+ +### ref + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/SegmentedButtons/SegmentedButtons.mdx b/docs/versioned_docs/version-6.x/components/SegmentedButtons/SegmentedButtons.mdx new file mode 100644 index 0000000000..36496a7c1b --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/SegmentedButtons/SegmentedButtons.mdx @@ -0,0 +1,107 @@ +--- +title: SegmentedButtons +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Segmented buttons can be used to select options, switch views or sort elements. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { SafeAreaView, StyleSheet } from 'react-native'; +import { SegmentedButtons } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState(''); + + return ( + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### buttons (required) + +
+ + + +
+ +### density + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Snackbar.mdx b/docs/versioned_docs/version-6.x/components/Snackbar.mdx new file mode 100644 index 0000000000..66581f61fc --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Snackbar.mdx @@ -0,0 +1,218 @@ +--- +title: Snackbar +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped. + +Note: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the "More Examples` section. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { Button, Snackbar } from 'react-native-paper'; + +const MyComponent = () => { + const [visible, setVisible] = React.useState(false); + + const onToggleSnackBar = () => setVisible(!visible); + + const onDismissSnackBar = () => setVisible(false); + + return ( + + + { + // Do something + }, + }}> + Hey there! I'm a Snackbar. + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'space-between', + }, +}); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### visible (required) + +
+ + + +
+ +### action + +
+ + + +
+ +### icon Available in v5.x with theme version 3 + +
+ + + +
+ +### onIconPress + +
+ + + +
+ +### iconAccessibilityLabel Available in v5.x with theme version 3 + +
+ + + +
+ +### duration + +
+ + + +
+ +### onDismiss (required) + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### maxFontSizeMultiplier + +
+ + + +
+ +### wrapperStyle + +
+ + + +
+ +### contentStyle + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + + + + + ## More Examples +
+ Toggle to grab more examples + +
+ + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Surface.mdx b/docs/versioned_docs/version-6.x/components/Surface.mdx new file mode 100644 index 0000000000..e70b85c42e --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Surface.mdx @@ -0,0 +1,119 @@ +--- +title: Surface +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Surface is a basic container that can give depth to an element with elevation shadow. +On dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface. +See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. +Overlay and shadow can be applied by specifying the `elevation` property both on Android and iOS. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Surface, Text } from 'react-native-paper'; +import { StyleSheet } from 'react-native'; + +const MyComponent = () => ( + + Surface + +); + +export default MyComponent; + +const styles = StyleSheet.create({ + surface: { + padding: 8, + height: 80, + width: 80, + alignItems: 'center', + justifyContent: 'center', + }, +}); +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### elevation Available in v5.x with theme version 3 + +
+ + + +
+ +### mode Available in v5.x with theme version 3 + +
+ + + +
+ +### theme + +
+ + + +
+ +### testID + +
+ + + +
+ +### ref + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Switch/Switch.mdx b/docs/versioned_docs/version-6.x/components/Switch/Switch.mdx new file mode 100644 index 0000000000..12d997fa26 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Switch/Switch.mdx @@ -0,0 +1,125 @@ +--- +title: Switch +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Material 3 toggle between two mutually exclusive states (on / off). + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Switch } from 'react-native-paper'; + +const Example = () => { + const [on, setOn] = React.useState(false); + return ; +}; +``` + +## Theming +Customize by overriding these `theme.colors` roles: +- `primary` / `onPrimary`: selected track + icon / selected handle +- `primaryContainer`: selected handle on hover, press +- `surfaceContainerHighest`: unselected track + icon +- `outline`: unselected resting handle, unselected track outline +- `onSurfaceVariant`: unselected handle on hover, press +- `onSurface`: disabled track, handle, and icon fills +- `surface`: disabled selected handle +- `secondary`: focus indicator + + + ## Props + + + + +
+ +### value + +
+ + + +
+ +### onValueChange + +
+ + + +
+ +### disabled + +
+ + + +
+ +### checkedIcon + +
+ + + +
+ +### uncheckedIcon + +
+ + + +
+ +### style + +
+ + + +
+ +### testID + +
+ + + +
+ +### theme + +
+ + + +
+ +### accessibilityLabel + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Text/Text.mdx b/docs/versioned_docs/version-6.x/components/Text/Text.mdx new file mode 100644 index 0000000000..076a690898 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Text/Text.mdx @@ -0,0 +1,109 @@ +--- +title: Text +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Typography component showing styles complied with passed `variant` prop and supported by the type system. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { Text } from 'react-native-paper'; + +const MyComponent = () => ( + <> + Display Large + Display Medium + Display small + + Headline Large + Headline Medium + Headline Small + + Title Large + Title Medium + Title Small + + Body Large + Body Medium + Body Small + + Label Large + Label Medium + Label Small + +); + +export default MyComponent; +``` + + + + + ## Props + ### Text props + + + +
+ +### variant Available in v5.x with theme version 3 + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### theme + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/TextInput/TextInput.mdx b/docs/versioned_docs/version-6.x/components/TextInput/TextInput.mdx new file mode 100644 index 0000000000..1928ab0072 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/TextInput/TextInput.mdx @@ -0,0 +1,177 @@ +--- +title: TextInput +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A text input lets users enter and edit text. It shows an optional floating label, +supports `filled` and `outlined` variants, optional supporting text (including +error state), and start/end accessories. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { TextInput } from 'react-native-paper'; + +const MyComponent = () => { + const [text, setText] = React.useState(''); + + const searchAccessory = (accessoryProps) => ( + + ); + + const clearAccessory = ({ style, disabled }) => ( + setText('')} + role="button" + aria-label="Clear text" + > + + + ); + + return ( + + ); +}; + +export default MyComponent; +``` + + + + + ## Props + ### TextInput props + + + +
+ +### ref + +
+ + + +
+ +### variant + +
+ + + +
+ +### error + +
+ + + +
+ +### label + +
+ + + +
+ +### supportingText + +
+ + + +
+ +### counter + +
+ + + +
+ +### disabled + +
+ + + +
+ +### prefix + +
+ + + +
+ +### suffix + +
+ + + +
+ +### theme + +
+ + + +
+ +### startAccessory + +
+ + + +
+ +### endAccessory + +
+ + + +
+ +### render + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/TextInput/TextInputIcon.mdx b/docs/versioned_docs/version-6.x/components/TextInput/TextInputIcon.mdx new file mode 100644 index 0000000000..96271b8578 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/TextInput/TextInputIcon.mdx @@ -0,0 +1,95 @@ +--- +title: TextInput.Icon +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A component to render a leading / trailing icon in the TextInput +(return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as +`TextInputAccessoryProps`, which TextInput passes into those render props. + + + + + +## Usage +```js +import * as React from 'react'; +import { TextInput } from 'react-native-paper'; + +const MyComponent = () => { + const [text, setText] = React.useState(''); + + const searchAccessory = (props) => ( + + ); + + const clearAccessory = (props) => ( + setText('')} /> + ); + + return ( + + ); +}; + +export default MyComponent; +``` + + + + + ## Props + ### IconButton props + + + +
+ +### style (required) + +
+ + + +
+ +### multiline (required) + +
+ + + +
+ +### disabled (required) + +
+ + + +
+ +### error (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButton.mdx b/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButton.mdx new file mode 100644 index 0000000000..990a014737 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButton.mdx @@ -0,0 +1,154 @@ +--- +title: ToggleButton +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons, +a group should share a common container. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ToggleButton } from 'react-native-paper'; + +const ToggleButtonExample = () => { + const [status, setStatus] = React.useState('checked'); + + const onButtonToggle = value => { + setStatus(status === 'checked' ? 'unchecked' : 'checked'); + }; + + return ( + + ); +}; + +export default ToggleButtonExample; + +``` + + + ## Props + + + + +
+ +### icon (required) + +
+ + + +
+ +### size + +
+ + + +
+ +### iconColor + +
+ + + +
+ +### disabled + +
+ + + +
+ +### accessibilityLabel + +
+ + + +
+ +### onPress + +
+ + + +
+ +### value + +
+ + + +
+ +### status + +
+ + + +
+ +### style + +
+ + + +
+ +### theme + +
+ + + +
+ +### ref + +
+ + + +
+ +### testID + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonGroup.mdx b/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonGroup.mdx new file mode 100644 index 0000000000..e29c66ad41 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonGroup.mdx @@ -0,0 +1,78 @@ +--- +title: ToggleButton.Group +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Toggle group allows to control a group of toggle buttons. +It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow). + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ToggleButton } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('left'); + + return ( + setValue(value)} + value={value} + > + + + + ); +}; + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### onValueChange (required) + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### children (required) + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonRow.mdx b/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonRow.mdx new file mode 100644 index 0000000000..8ef840854d --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonRow.mdx @@ -0,0 +1,83 @@ +--- +title: ToggleButton.Row +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Toggle button row renders a group of toggle buttons in a row. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { ToggleButton } from 'react-native-paper'; + +const MyComponent = () => { + const [value, setValue] = React.useState('left'); + + return ( + setValue(value)} value={value}> + + + + ); +}; + +export default MyComponent; + +``` + + + ## Props + + + + +
+ +### onValueChange (required) + +
+ + + +
+ +### value (required) + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/Tooltip/Tooltip.mdx b/docs/versioned_docs/version-6.x/components/Tooltip/Tooltip.mdx new file mode 100644 index 0000000000..840879f1b8 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/Tooltip/Tooltip.mdx @@ -0,0 +1,99 @@ +--- +title: Tooltip +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +Tooltips display informative text when users hover over, focus on, or tap an element. + +Plain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { IconButton, Tooltip } from 'react-native-paper'; + +const MyComponent = () => ( + + {}} /> + +); + +export default MyComponent; +``` + + + ## Props + + + + +
+ +### children (required) + +
+ + + +
+ +### enterTouchDelay + +
+ + + +
+ +### leaveTouchDelay + +
+ + + +
+ +### title (required) + +
+ + + +
+ +### titleMaxFontSizeMultiplier + +
+ + + +
+ +### theme + +
+ + + + + + + + + ## Theme colors + + + + + diff --git a/docs/versioned_docs/version-6.x/components/TouchableRipple/TouchableRipple.mdx b/docs/versioned_docs/version-6.x/components/TouchableRipple/TouchableRipple.mdx new file mode 100644 index 0000000000..c94b4f9b64 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/TouchableRipple/TouchableRipple.mdx @@ -0,0 +1,165 @@ +--- +title: TouchableRipple +--- + +import PropTable from '@site/src/components/PropTable.tsx'; +import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; + +A wrapper for views that should respond to touches. +Provides a material "ink ripple" interaction effect for supported platforms (>= Android Lollipop). +On unsupported platforms, it falls back to a highlight effect. + + + + + + + +## Usage +```js +import * as React from 'react'; +import { View } from 'react-native'; +import { Text, TouchableRipple } from 'react-native-paper'; + +const MyComponent = () => ( + console.log('Pressed')} + rippleColor="rgba(0, 0, 0, .32)" + > + Press anywhere + +); + +export default MyComponent; +``` + + + + + ## Props + ### Pressable props + + + +
+ +### borderless + +
+ + + +
+ +### background + +
+ + + +
+ +### centered + +
+ + + +
+ +### disabled + +
+ + + +
+ +### onPress + +
+ + + +
+ +### onLongPress + +
+ + + +
+ +### onPressIn + +
+ + + +
+ +### onPressOut + +
+ + + +
+ +### rippleColor + +
+ + + +
+ +### underlayColor + +
+ + + +
+ +### children (required) + +
+ + + +
+ +### style + +
+ + + +
+ +### ref + +
+ + + +
+ +### theme + +
+ + + + + + + + + + diff --git a/docs/versioned_docs/version-6.x/components/_category_.json b/docs/versioned_docs/version-6.x/components/_category_.json new file mode 100644 index 0000000000..c1ea2a2a75 --- /dev/null +++ b/docs/versioned_docs/version-6.x/components/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Components" +} \ No newline at end of file diff --git a/docs/docs/guides/01-getting-started.mdx b/docs/versioned_docs/version-6.x/guides/01-getting-started.mdx similarity index 100% rename from docs/docs/guides/01-getting-started.mdx rename to docs/versioned_docs/version-6.x/guides/01-getting-started.mdx diff --git a/docs/docs/guides/02-theming.mdx b/docs/versioned_docs/version-6.x/guides/02-theming.mdx similarity index 99% rename from docs/docs/guides/02-theming.mdx rename to docs/versioned_docs/version-6.x/guides/02-theming.mdx index db65637b1b..e35a3f1068 100644 --- a/docs/docs/guides/02-theming.mdx +++ b/docs/versioned_docs/version-6.x/guides/02-theming.mdx @@ -195,7 +195,7 @@ Created schemes are following the Material Design 3 color system and covering co Passed source color into the util is translated into tones to automatically provide the range of tones that map to color roles. -![customColors](../../static/screenshots/custom-colors.png) +![customColors](../../../static/screenshots/custom-colors.png) _Source: [Material You Color System](https://m3.material.io/styles/color/the-color-system/custom-colors)_ diff --git a/docs/docs/guides/03-icons.mdx b/docs/versioned_docs/version-6.x/guides/03-icons.mdx similarity index 100% rename from docs/docs/guides/03-icons.mdx rename to docs/versioned_docs/version-6.x/guides/03-icons.mdx diff --git a/docs/docs/guides/04-fonts.md b/docs/versioned_docs/version-6.x/guides/04-fonts.md similarity index 100% rename from docs/docs/guides/04-fonts.md rename to docs/versioned_docs/version-6.x/guides/04-fonts.md diff --git a/docs/docs/guides/05-react-native-web.md b/docs/versioned_docs/version-6.x/guides/05-react-native-web.md similarity index 100% rename from docs/docs/guides/05-react-native-web.md rename to docs/versioned_docs/version-6.x/guides/05-react-native-web.md diff --git a/docs/docs/guides/06-recommended-libraries.md b/docs/versioned_docs/version-6.x/guides/06-recommended-libraries.md similarity index 100% rename from docs/docs/guides/06-recommended-libraries.md rename to docs/versioned_docs/version-6.x/guides/06-recommended-libraries.md diff --git a/docs/versioned_docs/version-6.x/guides/07-contributing.mdx b/docs/versioned_docs/version-6.x/guides/07-contributing.mdx new file mode 100644 index 0000000000..2861043dc3 --- /dev/null +++ b/docs/versioned_docs/version-6.x/guides/07-contributing.mdx @@ -0,0 +1,7 @@ +--- +title: Contributing +--- + +import Contributing from '../../../../CONTRIBUTING.md'; + + diff --git a/docs/docs/guides/08-theming-with-react-navigation.md b/docs/versioned_docs/version-6.x/guides/08-theming-with-react-navigation.md similarity index 99% rename from docs/docs/guides/08-theming-with-react-navigation.md rename to docs/versioned_docs/version-6.x/guides/08-theming-with-react-navigation.md index 67b360dbba..aa4f5386ee 100644 --- a/docs/docs/guides/08-theming-with-react-navigation.md +++ b/docs/versioned_docs/version-6.x/guides/08-theming-with-react-navigation.md @@ -287,7 +287,7 @@ const Header = ({ scene }) => { And now you can switch between light and dark theme! -![paperGuide1](../../static/screenshots/themingWithReactNavigationDarkLightSwitch.gif) +![paperGuide1](../../../static/screenshots/themingWithReactNavigationDarkLightSwitch.gif) Thanks to the linking of themes that we did earlier, switching themes can be controlled with only one piece of state. diff --git a/docs/docs/guides/09-bottom-navigation.md b/docs/versioned_docs/version-6.x/guides/09-bottom-navigation.md similarity index 100% rename from docs/docs/guides/09-bottom-navigation.md rename to docs/versioned_docs/version-6.x/guides/09-bottom-navigation.md diff --git a/docs/docs/guides/09-react-navigation.md b/docs/versioned_docs/version-6.x/guides/09-react-navigation.md similarity index 100% rename from docs/docs/guides/09-react-navigation.md rename to docs/versioned_docs/version-6.x/guides/09-react-navigation.md diff --git a/docs/docs/guides/10-rtl.md b/docs/versioned_docs/version-6.x/guides/10-rtl.md similarity index 100% rename from docs/docs/guides/10-rtl.md rename to docs/versioned_docs/version-6.x/guides/10-rtl.md diff --git a/docs/versioned_docs/version-6.x/guides/11-ripple-effect.md b/docs/versioned_docs/version-6.x/guides/11-ripple-effect.md new file mode 100644 index 0000000000..31845060d7 --- /dev/null +++ b/docs/versioned_docs/version-6.x/guides/11-ripple-effect.md @@ -0,0 +1,45 @@ +--- +title: Ripple effect +--- + +The ripple effect is a visual feedback that occurs when a user interacts with a pressable UI element, such as a button. This response takes the form of a circular ripple expanding from the point of contact, much like a drop falling into water and creating ripples. + +The ripple effect is an essential aspect of Material Design, and Paper's pressable components have it enabled by default. Nonetheless, it can be tailored to suit specific needs. + +:::note +The ripple effect on the iOS platform is replaced by a highlight effect. +::: + +## Customize ripple effect color in component + +The `rippleColor` prop is available for every pressable component which allows you to set the color of the ripple effect. For the instance, to make the `Button` component's ripple effect transparent, simply pass the desired color value to the prop: + +```typescript + +``` + +## Disable ripple effect in all components + +To disable the ripple effect in **all** of Paper's components set `rippleEffectEnabled: false` on the `settings` prop of `PaperProvider`. + +```typescript +import { Provider as PaperProvider } from 'react-native-paper'; +// ... + + + // ... + +``` + + + diff --git a/docs/docs/guides/12-migration.md b/docs/versioned_docs/version-6.x/guides/12-migration.md similarity index 100% rename from docs/docs/guides/12-migration.md rename to docs/versioned_docs/version-6.x/guides/12-migration.md diff --git a/docs/versioned_docs/version-6.x/guides/_category_.json b/docs/versioned_docs/version-6.x/guides/_category_.json new file mode 100644 index 0000000000..82c9b69949 --- /dev/null +++ b/docs/versioned_docs/version-6.x/guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 0 +} diff --git a/docs/versioned_docs/version-6.x/showcase.mdx b/docs/versioned_docs/version-6.x/showcase.mdx new file mode 100644 index 0000000000..7024ab018d --- /dev/null +++ b/docs/versioned_docs/version-6.x/showcase.mdx @@ -0,0 +1,11 @@ +--- +title: Showcase +--- + +import Showcase from '@site/src/components/Showcase.tsx'; + +# Who's using Paper? + +Check out these apps built using Paper. Send us a [pull request](https://github.com/callstack/react-native-paper/pulls) to add your app to this list. + + diff --git a/docs/versioned_sidebars/version-5.x-sidebars.json b/docs/versioned_sidebars/version-5.x-sidebars.json new file mode 100644 index 0000000000..9553dd0602 --- /dev/null +++ b/docs/versioned_sidebars/version-5.x-sidebars.json @@ -0,0 +1,14 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "guides" + } + ], + "components": [ + { + "type": "autogenerated", + "dirName": "components" + } + ] +} diff --git a/docs/versioned_sidebars/version-6.x-sidebars.json b/docs/versioned_sidebars/version-6.x-sidebars.json new file mode 100644 index 0000000000..9553dd0602 --- /dev/null +++ b/docs/versioned_sidebars/version-6.x-sidebars.json @@ -0,0 +1,14 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "guides" + } + ], + "components": [ + { + "type": "autogenerated", + "dirName": "components" + } + ] +} diff --git a/docs/versions.json b/docs/versions.json new file mode 100644 index 0000000000..3f6328c126 --- /dev/null +++ b/docs/versions.json @@ -0,0 +1,4 @@ +[ + "6.x", + "5.x" +] diff --git a/scripts/generate-component-docs.ts b/scripts/generate-component-docs.ts new file mode 100644 index 0000000000..cc0ff5d93d --- /dev/null +++ b/scripts/generate-component-docs.ts @@ -0,0 +1,168 @@ +const childProcess = require('child_process'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +type PluginOptions = { + docsRootDir: string; + libsRootDir: string; + pages: unknown; +}; + +type PluginConfig = [string, PluginOptions]; + +type PluginFactory = ( + context: unknown, + options: PluginOptions +) => Promise<{ + loadContent: () => Promise; +}>; + +const isRecord = (value: unknown): value is { [key: string]: unknown } => + typeof value === 'object' && value !== null; + +const normalizeDocs = (value: unknown, sourceDir: string): unknown => { + if (Array.isArray(value)) { + return value.map((item) => normalizeDocs(item, sourceDir)); + } + + if (!isRecord(value)) { + return value; + } + + return Object.fromEntries( + Object.entries(value).map(([key, item]) => { + if (key !== 'dependencies' || !Array.isArray(item)) { + return [key, normalizeDocs(item, sourceDir)]; + } + + return [ + key, + item.map((dependency) => { + if (typeof dependency !== 'string' || !path.isAbsolute(dependency)) { + return dependency; + } + + const realDependency = fs.existsSync(dependency) + ? fs.realpathSync(dependency) + : dependency; + const relativeDependency = path.relative(sourceDir, realDependency); + + if ( + relativeDependency.startsWith('..') || + path.isAbsolute(relativeDependency) + ) { + return dependency; + } + + return relativeDependency.split(path.sep).join('/'); + }), + ]; + }) + ); +}; + +const main = async () => { + const [branchName, outputPath = 'docs/src/data/componentDocs5x.json'] = + process.argv.slice(2); + + if (!branchName) { + console.error( + 'Usage: node scripts/generate-component-docs.ts [output]' + ); + process.exitCode = 1; + return; + } + + const rootDir = path.resolve(__dirname, '..'); + const tempDir = fs.mkdtempSync( + path.join(os.tmpdir(), 'react-native-paper-docs-') + ); + + try { + const archivePath = path.join(tempDir, 'source.tar'); + const sourceDir = path.join(tempDir, 'source'); + + childProcess.execFileSync( + 'git', + ['rev-parse', '--verify', `${branchName}^{commit}`], + { + cwd: rootDir, + stdio: 'ignore', + } + ); + childProcess.execFileSync( + 'git', + ['archive', '--format=tar', '--output', archivePath, branchName], + { + cwd: rootDir, + stdio: 'inherit', + } + ); + + fs.mkdirSync(sourceDir); + childProcess.execFileSync('tar', ['-xf', archivePath, '-C', sourceDir], { + cwd: rootDir, + stdio: 'inherit', + }); + + const nodeModulesPath = path.join(rootDir, 'docs', 'node_modules'); + const archivedNodeModulesPath = path.join( + sourceDir, + 'docs', + 'node_modules' + ); + + if (fs.existsSync(nodeModulesPath)) { + fs.symlinkSync(nodeModulesPath, archivedNodeModulesPath, 'dir'); + } + + const configPath = path.join(sourceDir, 'docs', 'docusaurus.config.js'); + const config = require(configPath); + + if (!isRecord(config) || !Array.isArray(config.plugins)) { + throw new Error(`Unable to read plugins from ${configPath}`); + } + + const pluginConfig = config.plugins.find( + (plugin: unknown): plugin is PluginConfig => + Array.isArray(plugin) && + plugin[0] === './component-docs-plugin' && + isRecord(plugin[1]) && + typeof plugin[1].docsRootDir === 'string' && + typeof plugin[1].libsRootDir === 'string' + ); + + if (!pluginConfig) { + throw new Error( + `Unable to find component docs plugin config in ${configPath}` + ); + } + + const pluginFactory: PluginFactory = require(path.join( + sourceDir, + 'docs', + 'component-docs-plugin' + )); + const plugin = await pluginFactory({}, pluginConfig[1]); + const docs = await plugin.loadContent(); + const destination = path.resolve(rootDir, outputPath); + + fs.mkdirSync(path.dirname(destination), { recursive: true }); + fs.writeFileSync( + destination, + `${JSON.stringify( + { docs: normalizeDocs(docs, fs.realpathSync(sourceDir)) }, + null, + 2 + )}\n` + ); + } catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; + } finally { + fs.rmSync(tempDir, { recursive: true, force: true }); + } +}; + +void main(); From 5e851f0ef317996e835c4bb782e4860b03f8cfb1 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 15 Jun 2026 17:41:40 +0200 Subject: [PATCH 42/94] chore: bump node to latest LTS --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index d2c5c8a013..e8416a151f 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v22.15.0 \ No newline at end of file +v24.13.0 From 871724e40f8996d8b288031f9bb82eadef819206 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 15 Jun 2026 23:39:00 +0200 Subject: [PATCH 43/94] chore: avoid unnecessary prompts for release-it --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5cf4ec146..96550b6fbc 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "lint-no-fix": "eslint --ext '.js,.ts,.tsx' .", "test": "jest", "prepack": "bob build && node ./scripts/generate-mappings.js", - "release": "release-it", + "release": "release-it --only-version", "docs": "yarn --cwd docs", "example": "yarn --cwd example" }, From ece0e1adbddadadaa8139b947ad11e1a3c44bc30 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 15 Jun 2026 23:40:18 +0200 Subject: [PATCH 44/94] chore: release 6.0.0-alpha.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 96550b6fbc..570fbb066e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-paper", - "version": "5.15.1", + "version": "6.0.0-alpha.0", "description": "Material design for React Native", "main": "lib/module/index.js", "react-native-paper-source": "src/index.tsx", From 2d7e2d9f5b9ca0cf170cbecf02620c6074a5f4a8 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 17 Jun 2026 22:35:00 +0200 Subject: [PATCH 45/94] chore: add instructions for copilot --- .agents/README.md | 8 +++++ .github/copilot-instructions.md | 17 +++++++++ .github/instructions/code.instructions.md | 40 ++++++++++++++++++++++ .github/instructions/docs.instructions.md | 12 +++++++ .github/instructions/tests.instructions.md | 10 ++++++ 5 files changed, 87 insertions(+) create mode 100644 .agents/README.md create mode 100644 .github/copilot-instructions.md create mode 100644 .github/instructions/code.instructions.md create mode 100644 .github/instructions/docs.instructions.md create mode 100644 .github/instructions/tests.instructions.md diff --git a/.agents/README.md b/.agents/README.md new file mode 100644 index 0000000000..8a71d618a7 --- /dev/null +++ b/.agents/README.md @@ -0,0 +1,8 @@ +# Agent Instructions + +For code review behavior, use: + +- `.github/copilot-instructions.md` +- `.github/instructions/*.instructions.md` + +These files are the source of truth because they are also consumed by GitHub Copilot code review. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..d0482badea --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,17 @@ +# General Code Review Standards + +## Review Priorities + +- Prioritize correctness, regressions, missing tests, and API design issues. +- Check that the behavior scope matches the stated intent. +- Verify claims about external standards, platform conventions, accessibility, and design systems against the implementation. +- Avoid asking for unrelated refactors, stylistic churn, or backwards compatibility unless required by the change. + +## Public APIs + +- Review new or modified public APIs for visibility, naming, defaults, customizability, and extensibility. +- Ensure public APIs and exported types do not leak implementation details. + +## Tests + +- Call out missing tests when the changed behavior is risky. diff --git a/.github/instructions/code.instructions.md b/.github/instructions/code.instructions.md new file mode 100644 index 0000000000..a627454507 --- /dev/null +++ b/.github/instructions/code.instructions.md @@ -0,0 +1,40 @@ +--- +applyTo: "**/*.{ts,tsx,js,jsx}" +--- + +# TypeScript And React Review Instructions + +## Code Quality + +- Flag unnecessary abstractions, one-off helpers, excessive indirection, and defensive code without a real use case. +- Flag comments that restate obvious code instead of explaining non-obvious intent. +- Check that changes follow existing project style, structure, naming, and local patterns. + +## TypeScript + +- Flag uses of `any`, unsafe `as` assertions, and non-null assertions (`!`) unless clearly justified. +- Avoid broad types such as `object`, `Function`, `{}`, and `Record` unless the broadness is intentional. +- Do not accept `@ts-ignore`. +- Only accept `@ts-expect-error` when it has a comment explaining why the error is expected and cannot be resolved. +- Prefer simple types over complex conditional types, overloads, or type logic when simpler approaches work. +- Prefer `type` aliases over `interface` unless there is a specific reason to use `interface`. + +## React + +- Check that hooks follow the Rules of Hooks. +- Check dependency arrays for unstable values. +- Ensure components do not perform side effects during rendering. +- Avoid storing values in state when they can be derived from props or existing state. +- Avoid multiple sources of truth for the same state. +- Avoid `React.Children`, `cloneElement`, and reading React elements directly unless there is a strong justification. +- Treat memoization as something that needs a reason: expensive calculation, stable identity requirement, or measured performance need. +- Check async effects for cancellation or race conditions when relevant. +- Check accessibility: labels, roles, focus order, keyboard interactions, disabled states, and loading states. + +## Animations And Layout + +- Check for unnecessary layout shifts. +- Prefer animating only `transform` and `opacity` unless another property is required. +- For layout transitions, verify the approach handles rapid state changes, gestures, interruption, and cancellation. +- Check that duration and easing feel appropriate for the platform and interaction. +- Verify text and UI elements do not overlap or overflow at supported screen sizes. diff --git a/.github/instructions/docs.instructions.md b/.github/instructions/docs.instructions.md new file mode 100644 index 0000000000..d9b0627f52 --- /dev/null +++ b/.github/instructions/docs.instructions.md @@ -0,0 +1,12 @@ +--- +applyTo: "**/*.{md,mdx}" +--- + +# Documentation Review Instructions + +- Verify examples match the actual API. +- Check changed heading hierarchy for consistency. +- Check changed internal links and anchors for consistency. +- Check that documentation explains public behavior, not implementation details. +- Check that examples are self-contained and demonstrate only the relevant behavior instead of mixing multiple behaviors. +- Check that migration guide examples show diff or before/after code snippets when relevant. diff --git a/.github/instructions/tests.instructions.md b/.github/instructions/tests.instructions.md new file mode 100644 index 0000000000..6424174362 --- /dev/null +++ b/.github/instructions/tests.instructions.md @@ -0,0 +1,10 @@ +--- +applyTo: "**/*.{test,spec}.{ts,tsx,js,jsx}" +--- + +# Test Review Instructions + +- Check that tests verify public behavior, not implementation details. +- Check that test titles describe user-facing behavior. +- Check coverage of happy paths, edge cases, and error states. +- Flag unnecessary mocking. From 9bbb87f2fca7efbeddda9e4d2186d720be63ab18 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 22 Jun 2026 12:07:27 +0200 Subject: [PATCH 46/94] chore: add a workflow to label internal contributors --- .github/workflows/contributor-label.yml | 51 +++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/contributor-label.yml diff --git a/.github/workflows/contributor-label.yml b/.github/workflows/contributor-label.yml new file mode 100644 index 0000000000..e43cacdaf1 --- /dev/null +++ b/.github/workflows/contributor-label.yml @@ -0,0 +1,51 @@ +name: Contributor Label + +on: + pull_request_target: + types: [opened, reopened] + +permissions: + issues: write + pull-requests: read + +jobs: + label: + name: Add contributor label + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + + let permission = 'none'; + + try { + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, + username: context.payload.pull_request.user.login, + }); + + permission = data.permission; + } catch (error) { + if (error.status !== 404) { + throw error; + } + } + + if ( + !['MEMBER', 'OWNER'].includes(context.payload.pull_request.author_association) && + !['admin', 'maintain', 'write'].includes(permission) + ) { + return; + } + + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: context.issue.number, + labels: ['Contributor: Internal'], + }); From 8d72e214f05a3ede95088e1a30efea6ed75ddd51 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 22 Jun 2026 14:32:50 +0200 Subject: [PATCH 47/94] chore: fix workflow for labeling contributors --- .github/workflows/contributor-label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/contributor-label.yml b/.github/workflows/contributor-label.yml index e43cacdaf1..39e69bf3c9 100644 --- a/.github/workflows/contributor-label.yml +++ b/.github/workflows/contributor-label.yml @@ -6,7 +6,7 @@ on: permissions: issues: write - pull-requests: read + pull-requests: write jobs: label: From 6b16adcf3161fde3d135e149e7cc08267cf3732d Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 22 Jun 2026 18:58:44 +0200 Subject: [PATCH 48/94] chore: cleanup unused packages and update setup (#5012) --- .eslintignore | 12 - .eslintrc | 79 - .github/pull_request_template.md | 2 +- .github/workflows/ci.yml | 23 +- .gitignore | 9 +- CONTRIBUTING.md | 10 +- docs/.eslintrc | 15 - docs/component-docs-plugin/generatePageMDX.js | 1 - docs/component-docs-plugin/index.js | 6 +- docs/package.json | 5 +- docs/src/components/DynamicColorTheme.tsx | 3 +- docs/src/components/PropTable.tsx | 4 +- docs/src/utils/__tests__/themeColors.test.tsx | 114 - docs/src/utils/themes.tsx | 1 - eslint-local-rules/index.js | 2 - .../no-import-react-forwardref.js | 27 - .../no-react-forwardref-usage.js | 28 - eslint.config.mjs | 230 ++ example/.eslintrc | 11 - example/.gitignore | 10 +- example/package.json | 3 + example/src/Examples/CheckboxExample.tsx | 4 +- example/src/RootNavigator.tsx | 2 +- example/src/index.tsx | 12 +- jest/jest-native.d.ts | 9 - lefthook.yml | 17 + package.json | 65 +- scripts/generate-component-docs.ts | 33 +- scripts/generate-mappings.js | 64 - scripts/generate-mappings.ts | 111 + scripts/generate-ts-tests.js | 51 - scripts/generate-ts-tests.ts | 67 + scripts/package.json | 3 + ...tput-lint.js => typescript-output-lint.ts} | 15 +- src/babel/.eslintrc | 7 - src/babel/__tests__/index.js | 3 +- src/babel/index.js | 5 +- src/components/ActivityIndicator.tsx | 4 +- src/components/Appbar/AppbarAction.tsx | 4 +- src/components/Appbar/AppbarContent.tsx | 4 +- src/components/Banner.tsx | 2 +- .../BottomNavigation/BottomNavigation.tsx | 4 +- .../BottomNavigation/BottomNavigationBar.tsx | 4 +- src/components/Checkbox/Checkbox.tsx | 4 +- src/components/Chip/Chip.tsx | 4 +- src/components/DataTable/DataTable.tsx | 4 - src/components/FAB/Menu.tsx | 20 +- src/components/Menu/Menu.tsx | 22 +- src/components/Portal/PortalHost.tsx | 1 - src/components/RadioButton/utils.ts | 12 +- src/components/Switch/Switch.tsx | 4 +- src/components/TextInput/hooks.ts | 10 +- .../ToggleButton/ToggleButtonRow.tsx | 4 +- .../TouchableRipple.native.tsx | 4 +- .../__tests__/ActivityIndicator.test.tsx | 18 +- .../__tests__/Appbar/Appbar.test.tsx | 181 +- src/components/__tests__/Avatar.test.tsx | 74 +- src/components/__tests__/Badge.test.tsx | 30 +- src/components/__tests__/Banner.test.tsx | 220 +- .../__tests__/BottomNavigation.test.tsx | 401 +-- src/components/__tests__/Button.test.tsx | 214 +- src/components/__tests__/Card/Card.test.tsx | 81 +- .../__tests__/Checkbox/Checkbox.test.tsx | 42 +- .../__tests__/Checkbox/CheckboxItem.test.tsx | 75 +- src/components/__tests__/Chip.test.tsx | 88 +- src/components/__tests__/DataTable.test.tsx | 126 +- src/components/__tests__/Dialog.test.tsx | 64 +- .../Drawer/DrawerCollapsedItem.test.tsx | 41 +- .../__tests__/Drawer/DrawerSection.test.tsx | 12 +- src/components/__tests__/DrawerItem.test.tsx | 18 +- src/components/__tests__/FAB.test.tsx | 90 +- src/components/__tests__/FABExtended.test.tsx | 82 +- src/components/__tests__/FABMenu.test.tsx | 190 +- src/components/__tests__/Icon.test.tsx | 28 +- src/components/__tests__/IconButton.test.tsx | 50 +- .../__tests__/ListAccordion.test.tsx | 104 +- src/components/__tests__/ListImage.test.tsx | 40 +- src/components/__tests__/ListItem.test.tsx | 168 +- src/components/__tests__/ListSection.test.tsx | 92 +- src/components/__tests__/Menu.test.tsx | 118 +- src/components/__tests__/MenuItem.test.tsx | 62 +- src/components/__tests__/Modal.test.tsx | 244 +- src/components/__tests__/Portal.test.tsx | 7 +- src/components/__tests__/ProgressBar.test.tsx | 56 +- .../RadioButton/RadioButton.test.tsx | 30 +- .../RadioButton/RadioButtonGroup.test.tsx | 12 +- .../RadioButton/RadioButtonItem.test.tsx | 82 +- src/components/__tests__/Searchbar.test.tsx | 137 +- .../__tests__/SegmentedButton.test.tsx | 140 +- src/components/__tests__/Snackbar.test.tsx | 92 +- src/components/__tests__/Surface.test.tsx | 97 +- src/components/__tests__/Switch.test.tsx | 52 +- src/components/__tests__/TextInput.test.tsx | 759 +++--- .../__tests__/ToggleButton.test.tsx | 46 +- src/components/__tests__/Tooltip.test.tsx | 244 +- .../__tests__/TouchableRipple.test.tsx | 36 +- .../__tests__/Typography/Text.test.tsx | 51 +- .../__snapshots__/Text.test.tsx.snap | 34 +- .../BottomNavigation.test.tsx.snap | 166 +- .../__snapshots__/FABExtended.test.tsx.snap | 48 +- .../__snapshots__/Menu.test.tsx.snap | 16 +- .../__snapshots__/MenuItem.test.tsx.snap | 14 +- .../__snapshots__/Portal.test.tsx.snap | 8 +- src/core/PaperProvider.tsx | 4 +- src/core/__tests__/PaperProvider.test.tsx | 86 +- src/core/useSystemColorScheme.ts | 2 +- src/theme/__tests__/fonts.test.js | 2 + src/theme/fonts.tsx | 1 - src/theme/provider.tsx | 3 +- src/theme/schemes/DynamicTheme.android.tsx | 5 +- src/theme/tokens/sys/elevation.ts | 1 - src/utils/__tests__/addEventListener.js | 62 - src/utils/__tests__/addEventListener.test.tsx | 77 + src/utils/addEventListener.tsx | 8 +- src/utils/splitStyles.ts | 2 +- tsconfig.json | 13 +- yarn.lock | 2415 ++++++++--------- 117 files changed, 4581 insertions(+), 4354 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc delete mode 100644 docs/.eslintrc delete mode 100644 docs/src/utils/__tests__/themeColors.test.tsx delete mode 100644 eslint-local-rules/index.js delete mode 100644 eslint-local-rules/no-import-react-forwardref.js delete mode 100644 eslint-local-rules/no-react-forwardref-usage.js create mode 100644 eslint.config.mjs delete mode 100644 example/.eslintrc delete mode 100644 jest/jest-native.d.ts create mode 100644 lefthook.yml delete mode 100644 scripts/generate-mappings.js create mode 100644 scripts/generate-mappings.ts delete mode 100644 scripts/generate-ts-tests.js create mode 100644 scripts/generate-ts-tests.ts create mode 100644 scripts/package.json rename scripts/{typescript-output-lint.js => typescript-output-lint.ts} (86%) delete mode 100644 src/babel/.eslintrc delete mode 100644 src/utils/__tests__/addEventListener.js create mode 100644 src/utils/__tests__/addEventListener.test.tsx diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 5090b2f9a5..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,12 +0,0 @@ -node_modules/ -coverage/ -docs/dist/ -docs/build/ -docs/public/ -docs/static/ -web-build/ -jest/testSetup.js -__fixtures__/ - -# generated by bob -lib/ diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 223dd94031..0000000000 --- a/.eslintrc +++ /dev/null @@ -1,79 +0,0 @@ -{ - "env": { - "browser": true - }, - - "extends": "@callstack", - - "plugins": ["eslint-plugin-local-rules"], - - "rules": { - "local-rules/no-import-react-forwardref": "error", - "local-rules/no-react-forwardref-usage": "error", - - "one-var": "off", - "no-multi-assign": "off", - "no-nested-ternary": "off", - "no-undef": "off", - "global-require": "off", - "react/react-in-jsx-scope": "off", - "react/jsx-uses-react": "off", - - "import/no-extraneous-dependencies": "off", - "import/first": "off", - "import/order": [ - "error", - { - "groups": [ - ["external", "builtin"], - "internal", - ["sibling", "parent"], - "index" - ], - "pathGroups": [ - { - "pattern": "@(react|react-native)", - "group": "external", - "position": "before" - }, - { - "pattern": "@src/**", - "group": "internal" - } - ], - "pathGroupsExcludedImportTypes": ["internal", "react"], - "newlines-between": "always", - "alphabetize": { - "order": "asc", - "caseInsensitive": true - } - } - ], - - "react-native/no-unused-styles": "error", - "react-native/split-platform-components": "off", - "react-native/no-raw-text": "off", - "react-native-a11y/has-valid-accessibility-descriptors": "off" - }, - - "overrides": [ - { - "files": ["*.test.js", "*.test.tsx"], - "rules": { - "local-rules/no-react-forwardref-usage": "off" - } - } - ], - - "settings": { - "import/extensions": [".js", ".ts", ".tsx"], - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"] - }, - "import/resolver": { - "node": { - "extensions": [".js", ".ts", ".tsx"] - } - } - } -} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f14817e3d1..2168fac68d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -13,4 +13,4 @@ - + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8096174f9a..852bb6bd97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,10 +18,10 @@ jobs: uses: ./.github/actions/setup - name: Lint - run: yarn lint-no-fix + run: yarn lint - typescript: - name: TypeScript + typecheck: + name: Typecheck runs-on: ubuntu-latest steps: - name: Checkout @@ -31,7 +31,7 @@ jobs: uses: ./.github/actions/setup - name: Check types - run: yarn typescript + run: yarn typecheck unit-tests: name: Unit tests @@ -73,7 +73,20 @@ jobs: - name: Build package run: | yarn prepack - node ./scripts/typescript-output-lint + node ./scripts/typescript-output-lint.ts + + build-example: + name: Build example + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Build example + run: yarn example expo export --platform all build-docs: name: Build docs diff --git a/.gitignore b/.gitignore index 6ece42c049..ca1e88e763 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,11 @@ yarn-error.log # coverage/ cache/jest/ +__ts-tests__/ + +# ESLint +# +.eslintcache # BUCK # @@ -63,12 +68,10 @@ CHANGELOG.md # generated by bob lib/ -.expo - # Yarn .yarn/* !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/sdks -!.yarn/versions \ No newline at end of file +!.yarn/versions diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4cf09cd56..78b2b45937 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,9 +32,9 @@ Our pre-commit hooks verify that your commit message matches this format when co ### Linting and tests -We use `typescript` for type checking, `eslint` with `prettier` for linting and formatting the code, and `jest` for testing. Our pre-commit hooks verify that the linter and tests pass when commiting. You can also run the following commands manually: +We use `typescript` for type checking, `eslint` with `prettier` for linting and formatting the code, and `jest` for testing. Our pre-commit hooks verify that type checking, linting, and tests pass when committing. You can also run the following commands manually: -- `yarn typescript`: type-check files with `tsc`. +- `yarn typecheck`: type-check files with `tsc`. - `yarn lint`: lint files with `eslint` and `prettier`. - `yarn test`: run unit tests with `jest`. @@ -43,7 +43,7 @@ We use `typescript` for type checking, `eslint` with `prettier` for linting and When you're sending a pull request: - Prefer small pull requests focused on one change. -- Verify that `typescript`, `eslint` and all tests are passing. +- Verify that `typecheck`, `lint` and all tests are passing. - Preview the documentation to make sure it looks good. - Follow the pull request template when opening a pull request. @@ -58,7 +58,7 @@ When you're working on a component: The example app uses [Expo](https://expo.dev/) for the React Native example. You will need to install the Expo app for [Android](https://play.google.com/store/apps/details?id=host.exp.exponent) and [iOS](https://itunes.apple.com/app/apple-store/id982107779) to start developing. -> [!IMPORTANT] +> [!IMPORTANT] > The example app is built with `react-native@0.77.x` and Expo SDK 52, which isn’t compatible with Expo Go. To run the app, you have to create a [development build](https://docs.expo.dev/develop/development-builds/create-a-build/). After you're done, you can run `yarn example start` in the project root (or `npx expo start` in the `example/` folder) and scan the QR code to launch it on your device. @@ -72,7 +72,7 @@ If you want to test the changes brought by a pull request, you can do so by poin ```json { "dependencies": { - "react-native-paper": "git+https://github.com/callstack/react-native-paper.git#", + "react-native-paper": "git+https://github.com/callstack/react-native-paper.git#" } } ``` diff --git a/docs/.eslintrc b/docs/.eslintrc deleted file mode 100644 index bba456d680..0000000000 --- a/docs/.eslintrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "../.eslintrc", - - "settings": { - "import/core-modules": ["react-native-paper"] - }, - - "rules": { - "react/prop-types": "off", - "import/no-unresolved": [ - 2, - {"ignore": ["^@docusaurus", "^@theme", "@^site"]} - ] - } -} diff --git a/docs/component-docs-plugin/generatePageMDX.js b/docs/component-docs-plugin/generatePageMDX.js index 0165ecdb8d..5d07def6ef 100644 --- a/docs/component-docs-plugin/generatePageMDX.js +++ b/docs/component-docs-plugin/generatePageMDX.js @@ -5,7 +5,6 @@ const { baseUrl, customFields } = config; function renderBadge(annotation) { const [annotType, ...annotLabel] = annotation.split(' '); - // eslint-disable-next-line prettier/prettier return `${annotLabel.join(' ')}`; } diff --git a/docs/component-docs-plugin/index.js b/docs/component-docs-plugin/index.js index af66c75aa0..ae6f470b0e 100644 --- a/docs/component-docs-plugin/index.js +++ b/docs/component-docs-plugin/index.js @@ -8,7 +8,7 @@ const generatePageMDX = require('./generatePageMDX'); const pluginName = 'component-docs-plugin'; -async function componentsPlugin(_, options) { +function componentsPlugin(_, options) { const { docsRootDir, libsRootDir, pages } = options; function clean() { @@ -55,7 +55,7 @@ async function componentsPlugin(_, options) { return { name: pluginName, - async loadContent() { + loadContent() { // Clean up docs directory. clean(); // Create root components category. @@ -80,7 +80,7 @@ async function componentsPlugin(_, options) { return docs; }, - async contentLoaded({ content: docs, actions }) { + contentLoaded({ content: docs, actions }) { // Store component docs global data so it can be used in `PropsTable` component. actions.setGlobalData({ docs, diff --git a/docs/package.json b/docs/package.json index ded467eb2b..89c447287c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -20,6 +20,7 @@ "@docusaurus/core": "^2.3.6", "@docusaurus/preset-classic": "^2.3.6", "@docusaurus/remark-plugin-npm2yarn": "^2.3.6", + "@docusaurus/theme-common": "^2.3.6", "@easyops-cn/docusaurus-search-local": "^0.33.4", "@material/material-color-utilities": "0.2.4", "@mdx-js/react": "^1.6.22", @@ -39,13 +40,13 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "^2.3.6", - "@jest/globals": "^29.7.0", "@tsconfig/docusaurus": "^1.0.6", "@types/marked": "^4.0.7", "@types/react-color": "^3.0.6", "component-docs": "^0.24.0", "patch-package": "^6.5.0", - "typescript": "^4.8.4" + "typescript": "^4.8.4", + "webpack": "^5.73.0" }, "browserslist": { "production": [ diff --git a/docs/src/components/DynamicColorTheme.tsx b/docs/src/components/DynamicColorTheme.tsx index 19f37f8d6b..1203992e7d 100644 --- a/docs/src/components/DynamicColorTheme.tsx +++ b/docs/src/components/DynamicColorTheme.tsx @@ -3,7 +3,6 @@ import type { ReactNode } from 'react'; import Color from 'color'; //@ts-ignore -// eslint-disable-next-line import/no-unresolved import { BlockPicker } from 'react-color'; import Switch from './Switch'; @@ -161,7 +160,7 @@ const CodePreview = ({ }; const onCopy = () => { - navigator.clipboard.writeText(getColorScheme()); + void navigator.clipboard.writeText(getColorScheme()); setCopied(true); setTimeout(() => setCopied(false), 1000); diff --git a/docs/src/components/PropTable.tsx b/docs/src/components/PropTable.tsx index 39980fb703..9e073b14fe 100644 --- a/docs/src/components/PropTable.tsx +++ b/docs/src/components/PropTable.tsx @@ -1,5 +1,4 @@ // @ts-ignore -// eslint-disable-next-line import/no-unresolved import useDoc from '@site/component-docs-plugin/useDocs'; import Markdown from './Markdown'; @@ -27,7 +26,6 @@ const typeDefinitions = { const renderBadge = (annotation: string) => { const [annotType, ...annotLabel] = annotation.split(' '); - // eslint-disable-next-line prettier/prettier return ` { const parentStatus: Status = allChecked ? 'checked' : noneChecked - ? 'unchecked' - : 'indeterminate'; + ? 'unchecked' + : 'indeterminate'; const toggleAll = () => { const next = !allChecked; setChild1(next); diff --git a/example/src/RootNavigator.tsx b/example/src/RootNavigator.tsx index 5631de73d2..b64fd1d802 100644 --- a/example/src/RootNavigator.tsx +++ b/example/src/RootNavigator.tsx @@ -54,7 +54,7 @@ const Root = createNativeStackNavigator({ ( Object.entries(examplesWithoutParams) as [ ExampleRouteName, - (typeof examplesWithoutParams)[ExampleRouteName] + (typeof examplesWithoutParams)[ExampleRouteName], ][] ).map(([id, screen]) => [ id, diff --git a/example/src/index.tsx b/example/src/index.tsx index 688297b059..afa3941044 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -105,8 +105,8 @@ export default function PaperExample() { ? DynamicDarkTheme : DynamicLightTheme : isDarkMode - ? DarkTheme - : LightTheme; + ? DarkTheme + : LightTheme; const direction = rtl ? 'rtl' : 'ltr'; @@ -130,7 +130,7 @@ export default function PaperExample() { } }; - restorePrefs(); + void restorePrefs(); }, []); React.useEffect(() => { @@ -159,12 +159,12 @@ export default function PaperExample() { I18nManager.forceRTL(rtl); if (Platform.OS !== 'web') { - Updates.reloadAsync(); + await Updates.reloadAsync(); } } }; - savePrefs(); + void savePrefs(); }, [direction, isDarkMode, isReady, rtl]); const preferences = React.useMemo( @@ -230,7 +230,7 @@ export default function PaperExample() { }, }} onReady={() => { - SplashScreen.hideAsync(); + void SplashScreen.hideAsync(); }} /> {Platform.OS !== 'web' ? ( diff --git a/jest/jest-native.d.ts b/jest/jest-native.d.ts deleted file mode 100644 index d5dc396a76..0000000000 --- a/jest/jest-native.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import '@jest/expect'; -import type { JestNativeMatchers } from '@testing-library/jest-native/extend-expect'; -import 'expect'; - -declare module 'expect' { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Matchers, T = unknown> - extends JestNativeMatchers {} -} diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000000..ab4ecdb2f7 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,17 @@ +commit-msg: + jobs: + - name: commitlint + run: yarn commitlint --edit {1} + +pre-commit: + parallel: true + commands: + lint: + glob: '*.{js,mjs,ts,tsx}' + run: yarn lint {staged_files} + typecheck: + glob: '*.{json,js,mjs,ts,tsx}' + run: yarn typecheck + test: + glob: '*.{json,js,mjs,ts,tsx}' + run: yarn test diff --git a/package.json b/package.json index 570fbb066e..e183be6c56 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,11 @@ "docs" ], "scripts": { - "typescript": "tsc --noEmit", - "lint": "yarn lint-no-fix --fix", - "lint-no-fix": "eslint --ext '.js,.ts,.tsx' .", + "lint": "eslint", + "typecheck": "tsc", "test": "jest", - "prepack": "bob build && node ./scripts/generate-mappings.js", + "prepack": "bob build", + "generate-mappings": "node ./scripts/generate-mappings.ts", "release": "release-it --only-version", "docs": "yarn --cwd docs", "example": "yarn --cwd example" @@ -54,54 +54,43 @@ }, "devDependencies": { "@babel/core": "^7.29.0", + "@babel/parser": "^7.29.0", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/runtime": "^7.29.0", - "@callstack/eslint-config": "^13.0.2", + "@babel/types": "^7.29.0", + "@callstack/eslint-config": "^15.0.0", "@commitlint/config-conventional": "^8.3.4", + "@eslint/js": "9.39.4", "@jest/globals": "^29.7.0", "@react-native-vector-icons/material-design-icons": "^12.0.0", "@react-native/babel-preset": "^0.85.3", "@react-native/jest-preset": "^0.85.3", "@react-navigation/native": "^8.0.0-alpha.28", "@release-it/conventional-changelog": "^1.1.0", - "@testing-library/jest-native": "^5.4.1", - "@testing-library/react-native": "11.5.0", + "@testing-library/react-native": "^14.0.0", "@types/color": "^3.0.0", - "@types/jest": "^29.2.1", - "@types/node": "^13.1.0", - "@types/react-dom": "^19.1.1", - "@types/react-native-vector-icons": "^6.4.18", - "@types/react-test-renderer": "^19.1.0", - "@typescript-eslint/eslint-plugin": "^5.41.0", - "@typescript-eslint/parser": "^5.41.0", + "@types/node": "^24.0.0", + "@types/react": "^19.2.7", "all-contributors-cli": "^6.24.0", - "babel-cli": "^6.26.0", - "babel-core": "^7.0.0-bridge.0", "babel-jest": "^29.6.3", - "babel-loader": "^8.2.3", "babel-test": "^0.1.1", - "chalk": "^4.0.0", "commitlint": "^8.3.4", - "conventional-changelog-cli": "^2.0.11", - "dedent": "^0.7.0", - "eslint": "8.31.0", + "eslint": "9.39.4", "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-local-rules": "^1.3.2", - "glob": "^7.1.3", - "husky": "^1.3.1", + "eslint-plugin-testing-library": "^7.16.2", "jest": "^29.6.3", "jest-file-snapshot": "^0.3.2", + "lefthook": "^2.1.9", + "prettier": "^3.8.4", "react": "19.2.3", - "react-dom": "19.2.3", "react-native": "0.85.3", "react-native-builder-bob": "^0.42.1", "react-native-reanimated": "4.3.1", "react-native-safe-area-context": "5.7.0", "react-native-worklets": "0.8.3", - "react-test-renderer": "19.2.3", "release-it": "^13.4.0", - "rimraf": "^3.0.2", - "typescript": "5.8.3" + "test-renderer": "1.2.0", + "typescript": "5.8.3", + "typescript-eslint": "^8.61.1" }, "peerDependencies": { "react": "*", @@ -110,12 +99,6 @@ "react-native-safe-area-context": "*", "react-native-worklets": ">=0.8.1" }, - "husky": { - "hooks": { - "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", - "pre-commit": "yarn lint-no-fix && yarn typescript && yarn test" - } - }, "jest": { "preset": "@react-native/jest-preset", "setupFiles": [ @@ -123,7 +106,7 @@ ], "setupFilesAfterEnv": [ "/jest/jestSetupAfterEnv.js", - "@testing-library/jest-native/extend-expect" + "@testing-library/react-native" ], "cacheDirectory": "./cache/jest", "testPathIgnorePatterns": [ @@ -146,10 +129,7 @@ "react", "react-dom", "react-native", - "react-test-renderer", "@types/react-native", - "@types/jest", - "husky", "eslint" ] }, @@ -163,6 +143,13 @@ { "project": "tsconfig.build.json" } + ], + [ + "custom", + { + "script": "generate-mappings", + "clean": "lib/mappings.json" + } ] ] }, diff --git a/scripts/generate-component-docs.ts b/scripts/generate-component-docs.ts index cc0ff5d93d..c4adcbeb52 100644 --- a/scripts/generate-component-docs.ts +++ b/scripts/generate-component-docs.ts @@ -1,7 +1,9 @@ -const childProcess = require('child_process'); -const fs = require('fs'); -const os = require('os'); -const path = require('path'); +import * as childProcess from 'node:child_process'; +import * as fs from 'node:fs'; +import { createRequire } from 'node:module'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; type PluginOptions = { docsRootDir: string; @@ -21,6 +23,9 @@ type PluginFactory = ( const isRecord = (value: unknown): value is { [key: string]: unknown } => typeof value === 'object' && value !== null; +const isPluginFactory = (value: unknown): value is PluginFactory => + typeof value === 'function'; + const normalizeDocs = (value: unknown, sourceDir: string): unknown => { if (Array.isArray(value)) { return value.map((item) => normalizeDocs(item, sourceDir)); @@ -74,7 +79,11 @@ const main = async () => { return; } - const rootDir = path.resolve(__dirname, '..'); + const requireFromScript = createRequire(import.meta.url); + const rootDir = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..' + ); const tempDir = fs.mkdtempSync( path.join(os.tmpdir(), 'react-native-paper-docs-') ); @@ -118,7 +127,7 @@ const main = async () => { } const configPath = path.join(sourceDir, 'docs', 'docusaurus.config.js'); - const config = require(configPath); + const config: unknown = requireFromScript(configPath); if (!isRecord(config) || !Array.isArray(config.plugins)) { throw new Error(`Unable to read plugins from ${configPath}`); @@ -139,11 +148,19 @@ const main = async () => { ); } - const pluginFactory: PluginFactory = require(path.join( + const pluginFactoryPath = path.join( sourceDir, 'docs', 'component-docs-plugin' - )); + ); + const pluginFactory: unknown = requireFromScript(pluginFactoryPath); + + if (!isPluginFactory(pluginFactory)) { + throw new Error( + `Unable to read component docs plugin from ${pluginFactoryPath}` + ); + } + const plugin = await pluginFactory({}, pluginConfig[1]); const docs = await plugin.loadContent(); const destination = path.resolve(rootDir, outputPath); diff --git a/scripts/generate-mappings.js b/scripts/generate-mappings.js deleted file mode 100644 index 12ed00ffc2..0000000000 --- a/scripts/generate-mappings.js +++ /dev/null @@ -1,64 +0,0 @@ -const parser = require('@babel/parser'); -const types = require('babel-types'); -const fs = require('fs'); -const path = require('path'); - -const packageJson = require('../package.json'); -const root = path.resolve(__dirname, '..'); -const output = path.join(root, 'lib/mappings.json'); -const source = fs.readFileSync(path.resolve(root, 'src', 'index.tsx'), 'utf8'); -const ast = parser.parse(source, { - sourceType: 'module', - plugins: [ - 'jsx', - 'typescript', - 'objectRestSpread', - 'classProperties', - 'asyncGenerators', - ], -}); - -const index = packageJson.main; -const relative = (value /* : string */) => - path.relative(root, path.resolve(path.dirname(index), value)); - -const mappings = ast.program.body.reduce((acc, declaration, index, self) => { - if (types.isExportNamedDeclaration(declaration)) { - if (declaration.source) { - declaration.specifiers.forEach((specifier) => { - acc[specifier.exported.name] = { - path: relative(declaration.source.value), - name: specifier.local.name, - }; - }); - } else { - declaration.specifiers.forEach((specifier) => { - const name = specifier.exported.name; - - self.forEach((it) => { - if ( - types.isImportDeclaration(it) && - it.specifiers.some( - (s) => - types.isImportNamespaceSpecifier(s) && - s.local.name === specifier.local.name - ) - ) { - acc[name] = { - path: relative(it.source.value), - name: '*', - }; - } - }); - }); - } - } - - return acc; -}, {}); - -fs.existsSync(path.dirname(output)) || fs.mkdirSync(path.dirname(output)); -fs.writeFileSync( - output, - JSON.stringify({ name: packageJson.name, index, mappings }, null, 2) -); diff --git a/scripts/generate-mappings.ts b/scripts/generate-mappings.ts new file mode 100644 index 0000000000..b6a172e7c6 --- /dev/null +++ b/scripts/generate-mappings.ts @@ -0,0 +1,111 @@ +import { parse } from '@babel/parser'; +import * as types from '@babel/types'; +import type { Identifier, StringLiteral } from '@babel/types'; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { dirname, join, relative as relativePath, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +type PackageJson = { + name: string; + main: string; +}; + +type Mapping = { + path: string; + name: string; +}; + +const isRecord = (value: unknown): value is { [key: string]: unknown } => + typeof value === 'object' && value !== null; + +const readPackageJson = (path: string): PackageJson => { + const packageJson: unknown = JSON.parse(readFileSync(path, 'utf8')); + + if ( + !isRecord(packageJson) || + typeof packageJson.name !== 'string' || + typeof packageJson.main !== 'string' + ) { + throw new Error(`Unable to read package name and main from ${path}`); + } + + return { + name: packageJson.name, + main: packageJson.main, + }; +}; + +const getName = (node: Identifier | StringLiteral) => + types.isIdentifier(node) ? node.name : node.value; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const root = resolve(scriptDir, '..'); +const packageJson = readPackageJson(resolve(root, 'package.json')); +const output = join(root, 'lib/mappings.json'); +const source = readFileSync(resolve(root, 'src', 'index.tsx'), 'utf8'); +const ast = parse(source, { + sourceType: 'module', + plugins: ['jsx', 'typescript'], +}); + +const index = packageJson.main; +const relative = (value: string) => + relativePath(root, resolve(root, dirname(index), value)); + +const mappings = ast.program.body.reduce<{ [key: string]: Mapping }>( + (acc, declaration, _index, self) => { + if (types.isExportNamedDeclaration(declaration)) { + const declarationSource = declaration.source; + + if (declarationSource) { + declaration.specifiers.forEach((specifier) => { + if (!types.isExportSpecifier(specifier)) { + return; + } + + acc[getName(specifier.exported)] = { + path: relative(declarationSource.value), + name: getName(specifier.local), + }; + }); + } else { + declaration.specifiers.forEach((specifier) => { + if (!types.isExportSpecifier(specifier)) { + return; + } + + const name = getName(specifier.exported); + const localName = getName(specifier.local); + + self.forEach((it) => { + if ( + types.isImportDeclaration(it) && + it.specifiers.some( + (s) => + types.isImportNamespaceSpecifier(s) && + s.local.name === localName + ) + ) { + acc[name] = { + path: relative(it.source.value), + name: '*', + }; + } + }); + }); + } + } + + return acc; + }, + {} +); + +if (!existsSync(dirname(output))) { + mkdirSync(dirname(output)); +} + +writeFileSync( + output, + JSON.stringify({ name: packageJson.name, index, mappings }, null, 2) +); diff --git a/scripts/generate-ts-tests.js b/scripts/generate-ts-tests.js deleted file mode 100644 index f0e5ef98be..0000000000 --- a/scripts/generate-ts-tests.js +++ /dev/null @@ -1,51 +0,0 @@ -const { readFileSync, writeFileSync, mkdirSync } = require('fs'); -const glob = require('glob'); -const path = require('path'); -const rimraf = require('rimraf'); - -const SOURCE_FILES_PATTERN = './src/**/*.js'; -const DESTINATION_DIR = './__ts-tests__'; -const EXAMPLE_REGEX = /(## Usage\n \* ```js)([\S\s]*?)(```)/g; -const JS_EXT = '.js'; -const TS_TEST_EXT = '.test.tsx'; - -const transformContent = (content) => - content - .replace("'react-native-paper'", "'..'") - .split('\n') - .map((e) => e.slice(3)) - .join('\n'); - -const getFiles = () => - glob - .sync(SOURCE_FILES_PATTERN) - .map((filePath) => { - const content = readFileSync(filePath, 'utf-8'); - const match = EXAMPLE_REGEX.exec(content); - // JS regexp is stateful, you need to reset lastIndex each time - EXAMPLE_REGEX.lastIndex = 0; - return match - ? { - path: filePath, - content: transformContent(match[2]), - } - : null; - }) - .filter(Boolean); - -const writeFiles = (files) => { - mkdirSync(DESTINATION_DIR); - - files.forEach((f) => - writeFileSync( - path.join( - DESTINATION_DIR, - `${path.basename(f.path, JS_EXT)}${TS_TEST_EXT}` - ), - f.content - ) - ); -}; - -rimraf.sync(DESTINATION_DIR); -writeFiles(getFiles()); diff --git a/scripts/generate-ts-tests.ts b/scripts/generate-ts-tests.ts new file mode 100644 index 0000000000..2223cc7386 --- /dev/null +++ b/scripts/generate-ts-tests.ts @@ -0,0 +1,67 @@ +import { + mkdirSync, + readdirSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; +import { basename, join } from 'node:path'; + +type SourceFile = { + path: string; + content: string; +}; + +const SOURCE_DIR = './src'; +const DESTINATION_DIR = './__ts-tests__'; +const EXAMPLE_REGEX = /(## Usage\n \* ```js)([\S\s]*?)(```)/g; +const JS_EXT = '.js'; +const TS_TEST_EXT = '.test.tsx'; + +const transformContent = (content: string) => + content + .replace("'react-native-paper'", "'..'") + .split('\n') + .map((e) => e.slice(3)) + .join('\n'); + +const getSourceFiles = (directory: string): string[] => + readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const filePath = join(directory, entry.name); + + if (entry.isDirectory()) { + return getSourceFiles(filePath); + } + + return entry.isFile() && filePath.endsWith(JS_EXT) ? [filePath] : []; + }); + +const getFiles = () => + getSourceFiles(SOURCE_DIR) + .map((filePath) => { + const content = readFileSync(filePath, 'utf-8'); + const match = EXAMPLE_REGEX.exec(content); + // JS regexp is stateful, you need to reset lastIndex each time + EXAMPLE_REGEX.lastIndex = 0; + return match + ? { + path: filePath, + content: transformContent(match[2]), + } + : null; + }) + .filter((file): file is SourceFile => file !== null); + +const writeFiles = (files: SourceFile[]) => { + mkdirSync(DESTINATION_DIR); + + files.forEach((f) => + writeFileSync( + join(DESTINATION_DIR, `${basename(f.path, JS_EXT)}${TS_TEST_EXT}`), + f.content + ) + ); +}; + +rmSync(DESTINATION_DIR, { recursive: true, force: true }); +writeFiles(getFiles()); diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000000..3dbc1ca591 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/scripts/typescript-output-lint.js b/scripts/typescript-output-lint.ts similarity index 86% rename from scripts/typescript-output-lint.js rename to scripts/typescript-output-lint.ts index 16eacdf249..3781890211 100644 --- a/scripts/typescript-output-lint.js +++ b/scripts/typescript-output-lint.ts @@ -1,8 +1,9 @@ -const { promises: fs } = require('fs'); -const path = require('path'); +import { promises as fs } from 'node:fs'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; -const root = path.resolve(__dirname, '..'); -const output = path.join(root, 'lib', 'typescript'); +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const output = join(root, 'lib', 'typescript'); /** * List of React Native props not used by React Native Paper. @@ -76,10 +77,10 @@ const unusedViewProps = [ 'unstable_pressDelay', ]; -async function* getFiles(directory) { +async function* getFiles(directory: string): AsyncGenerator { const entries = await fs.readdir(directory, { withFileTypes: true }); for (const entry of entries) { - const res = path.resolve(directory, entry.name); + const res = resolve(directory, entry.name); if (entry.isDirectory()) { yield* getFiles(res); } else { @@ -94,7 +95,7 @@ async function main() { for (const prop of unusedViewProps) { if (content.includes(prop)) { throw new Error( - `Found text '${prop}' in '${file}'. Please use the wrapped 'forwardRef' in 'src/utils/forwardRef.ts', export some return types, or modify 'scripts/typescript-output-lint.js'` + `Found text '${prop}' in '${file}'. Please use the wrapped 'forwardRef' in 'src/utils/forwardRef.ts', export some return types, or modify 'scripts/typescript-output-lint.ts'` ); } } diff --git a/src/babel/.eslintrc b/src/babel/.eslintrc deleted file mode 100644 index 1aad9bfc83..0000000000 --- a/src/babel/.eslintrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "rules": { - "no-unused-vars": "off", - "import/no-duplicates": "off", - "import/no-unresolved": "off", - } -} diff --git a/src/babel/__tests__/index.js b/src/babel/__tests__/index.js index 113788881f..159e2a5f71 100644 --- a/src/babel/__tests__/index.js +++ b/src/babel/__tests__/index.js @@ -1,3 +1,4 @@ +const { expect } = require('@jest/globals'); const { create } = require('babel-test'); const { spawnSync } = require('child_process'); const { toMatchFile } = require('jest-file-snapshot'); @@ -6,7 +7,7 @@ const path = require('path'); expect.extend({ toMatchFile }); spawnSync('node', [ - path.resolve(__dirname, '../../../scripts/generate-mappings.js'), + path.resolve(__dirname, '../../../scripts/generate-mappings.ts'), ]); const { fixtures } = create({ diff --git a/src/babel/index.js b/src/babel/index.js index f93f07e01e..2fc478bacb 100644 --- a/src/babel/index.js +++ b/src/babel/index.js @@ -3,8 +3,9 @@ const SKIP = Symbol('SKIP'); module.exports = function rewire(babel, options) { const t = babel.types; - const { name, index, mappings } = require(options.mappings || - '../../mappings.json'); + const { name, index, mappings } = require( + options.mappings || '../../mappings.json' + ); return { visitor: { diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 9436979208..8faa54de58 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -130,8 +130,8 @@ const ActivityIndicator = ({ ? 24 : 48 : indicatorSize - ? indicatorSize - : 24; + ? indicatorSize + : 24; const frames = (60 * DURATION) / 1000; const easing = Easing.bezier(0.4, 0.0, 0.7, 1.0); diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index 0826372c8f..5d63fd642c 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -91,8 +91,8 @@ const AppbarAction = ({ const actionIconColor = iconColor ? iconColor : isLeading - ? colors.onSurface - : colors.onSurfaceVariant; + ? colors.onSurface + : colors.onSurfaceVariant; return ( {actions.map(({ label, ...others }, i) => ( ).toJSON(); +it('renders text button by default', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders text button with mode', () => { - const tree = render().toJSON(); +it('renders text button with mode', async () => { + const tree = ( + await render() + ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders outlined button with mode', () => { - const tree = render( - +it('renders outlined button with mode', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders contained contained with mode', () => { - const tree = render( - +it('renders contained contained with mode', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders button with icon', () => { - const tree = render().toJSON(); +it('renders button with icon', async () => { + const tree = ( + await render() + ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders button with icon in reverse order', () => { - const tree = render( - +it('renders button with icon in reverse order', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders loading button', () => { - const tree = render().toJSON(); +it('renders loading button', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders disabled button', () => { - const tree = render().toJSON(); +it('renders disabled button', async () => { + const tree = ( + await render() + ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders disabled button if there is no touch handler passed', () => { - const { getByTestId } = render( - - ); +it('renders disabled button if there is no touch handler passed', async () => { + await render(); - expect(getByTestId('disabled-button').props.accessibilityState).toMatchObject( - { - disabled: true, - } - ); + expect(screen.getByTestId('disabled-button')).toBeDisabled(); }); -it('renders active button if only onLongPress handler is passed', () => { - const { getByTestId } = render( +it('renders active button if only onLongPress handler is passed', async () => { + await render( ); - expect(getByTestId('active-button').props.accessibilityState).toMatchObject({ - disabled: false, - }); + expect(screen.getByTestId('active-button')).toBeEnabled(); }); -it('renders button with color', () => { - const tree = render( - +it('renders button with color', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders button with button color', () => { - const tree = render( - +it('renders button with button color', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders button with custom testID', () => { - const tree = render( - +it('renders button with custom testID', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders button with an accessibility label', () => { - const tree = render( - +it('renders button with an accessibility label', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders button with an accessibility hint', () => { - const tree = render( - +it('renders button with an accessibility hint', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders button with custom border radius', () => { - const { getByTestId } = render( +it('renders button with custom border radius', async () => { + await render( ); - expect(getByTestId('custom-radius-container')).toHaveStyle( + expect(screen.getByTestId('custom-radius-container')).toHaveStyle( styles.customRadius ); - expect(getByTestId('custom-radius')).toHaveStyle(styles.customRadius); + expect(screen.getByTestId('custom-radius')).toHaveStyle(styles.customRadius); }); -it('renders outlined button with custom border radius', () => { - const { getByTestId } = render( +it('renders outlined button with custom border radius', async () => { + await render( ); - expect(getByTestId('custom-radius-container')).toHaveStyle( + expect(screen.getByTestId('custom-radius-container')).toHaveStyle( styles.customRadius ); - expect(getByTestId('custom-radius')).toHaveStyle({ + expect(screen.getByTestId('custom-radius')).toHaveStyle({ borderTopLeftRadius: 15, // styles.customRadius - 1px outline borderTopRightRadius: 0, borderBottomLeftRadius: 0, @@ -184,77 +190,79 @@ it('renders outlined button with custom border radius', () => { }); }); -it('renders button without border radius', () => { - const { getByTestId } = render( +it('renders button without border radius', async () => { + await render( ); - expect(getByTestId('custom-radius-container')).toHaveStyle(styles.noRadius); - expect(getByTestId('custom-radius')).toHaveStyle(styles.noRadius); + expect(screen.getByTestId('custom-radius-container')).toHaveStyle( + styles.noRadius + ); + expect(screen.getByTestId('custom-radius')).toHaveStyle(styles.noRadius); }); -it('should execute onPressIn', () => { +it('should execute onPressIn', async () => { const onPressInMock = jest.fn(); const onPress = jest.fn(); - const { getByTestId } = render( + await render( ); - fireEvent(getByTestId('button'), 'onPressIn'); + await userEvent.press(screen.getByTestId('button')); expect(onPressInMock).toHaveBeenCalledTimes(1); }); -it('should execute onPressOut', () => { +it('should execute onPressOut', async () => { const onPressOutMock = jest.fn(); const onPress = jest.fn(); - const { getByTestId } = render( + await render( ); - fireEvent(getByTestId('button'), 'onPressOut'); + await userEvent.press(screen.getByTestId('button')); expect(onPressOutMock).toHaveBeenCalledTimes(1); }); describe('button text styles', () => { - it('applies uppercase styles if uppercase prop is truthy', () => { - const { getByTestId } = render( + it('applies uppercase styles if uppercase prop is truthy', async () => { + await render( ); - expect(getByTestId('button-text')).toHaveStyle({ + expect(screen.getByTestId('button-text')).toHaveStyle({ textTransform: 'uppercase', }); }); - it('does not apply uppercase styles if uppercase prop is falsy', () => { - const { getByTestId } = render( + it('does not apply uppercase styles if uppercase prop is falsy', async () => { + await render( ); - expect(getByTestId('button-text')).not.toHaveStyle({ + expect(screen.getByTestId('button-text')).not.toHaveStyle({ textTransform: 'uppercase', }); }); }); describe('button icon styles', () => { - it('should return correct icon styles for compact text button', () => { - const { getByTestId } = render( + it('should return correct icon styles for compact text button', async () => { + await render( ); - expect(getByTestId('compact-button-icon-container')).toHaveStyle({ + expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle({ marginLeft: 6, marginRight: 0, }); @@ -262,26 +270,28 @@ describe('button icon styles', () => { (['outlined', 'contained', 'contained-tonal', 'elevated'] as const).forEach( (mode) => - it(`should return correct icon styles for compact ${mode} button`, () => { - const { getByTestId } = render( + it(`should return correct icon styles for compact ${mode} button`, async () => { + await render( ); - expect(getByTestId('compact-button-icon-container')).toHaveStyle({ - marginLeft: 8, - marginRight: 0, - }); + expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle( + { + marginLeft: 8, + marginRight: 0, + } + ); }) ); - it('should return correct icon styles for text button', () => { - const { getByTestId } = render( + it('should return correct icon styles for text button', async () => { + await render( ); - expect(getByTestId('compact-button-icon-container')).toHaveStyle({ + expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle({ marginLeft: 12, marginRight: -8, }); @@ -289,16 +299,18 @@ describe('button icon styles', () => { (['outlined', 'contained', 'contained-tonal', 'elevated'] as const).forEach( (mode) => - it(`should return correct icon styles for compact ${mode} button`, () => { - const { getByTestId } = render( + it(`should return correct icon styles for compact ${mode} button`, async () => { + await render( ); - expect(getByTestId('compact-button-icon-container')).toHaveStyle({ - marginLeft: 16, - marginRight: -16, - }); + expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle( + { + marginLeft: 16, + marginRight: -16, + } + ); }) ); }); @@ -698,9 +710,9 @@ describe('getButtonColors - border width', () => { ); }); -it('animated value changes correctly', () => { +it('animated value changes correctly', async () => { const value = new Animated.Value(1); - const { getByTestId } = render( + await render( ); - expect(getByTestId('button-container-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('button-container-outer-layer')).toHaveStyle({ transform: [{ scale: 1 }], }); @@ -720,10 +732,10 @@ it('animated value changes correctly', () => { duration: 200, }).start(); - act(() => { + await act(() => { jest.advanceTimersByTime(200); }); - expect(getByTestId('button-container-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('button-container-outer-layer')).toHaveStyle({ transform: [{ scale: 1.5 }], }); }); diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index 267cb65abb..61659c8a24 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -4,7 +4,7 @@ import { describe, expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; import { getTheme } from '../../../core/theming'; -import { render } from '../../../test-utils'; +import { render, screen } from '../../../test-utils'; import { Palette } from '../../../theme/tokens'; import Button from '../../Button/Button'; import Card from '../../Card/Card'; @@ -26,14 +26,14 @@ const styles = StyleSheet.create({ }); describe('Card', () => { - it('renders an outlined card', () => { - const tree = render({null}).toJSON(); + it('renders an outlined card', async () => { + const tree = (await render({null})).toJSON(); expect(tree).toMatchSnapshot(); }); - it('renders an outlined card with custom border radius and color', () => { - const { getByTestId } = render( + it('renders an outlined card with custom border radius and color', async () => { + await render( { ); - expect(getByTestId('card-outline')).toHaveStyle({ + expect(screen.getByTestId('card-outline')).toHaveStyle({ borderRadius: 32, borderColor: 'purple', }); }); - it('renders an outlined card with custom border color', () => { - const { getByLabelText } = render( + it('renders an outlined card with custom border color', async () => { + await render( { ); - expect(getByLabelText('card')).toHaveStyle({ + expect(screen.getByLabelText('card')).toHaveStyle({ borderColor: Palette.error50, }); }); - it('renders with a custom theme', () => { - const { getByLabelText } = render( + it('renders with a custom theme', async () => { + await render( { ); - expect(getByLabelText('card')).toHaveStyle({ + expect(screen.getByLabelText('card')).toHaveStyle({ backgroundColor: '#0000FF', }); }); - it('renders with a content style', () => { - const { getByTestId } = render( + it('renders with a content style', async () => { + await render( Content ); - expect(getByTestId('card')).toHaveStyle(styles.contentStyle); + expect(screen.getByTestId('card')).toHaveStyle(styles.contentStyle); }); - it('does not render a disabled accessibility state', () => { - const { getByTestId } = render({null}); + it('does not render a disabled accessibility state', async () => { + await render({null}); - expect( - getByTestId('card').props.accessibilityState || {} - ).not.toMatchObject({ - disabled: true, - }); + expect(screen.getByTestId('card')).toBeEnabled(); }); - it('does render a disabled accessibility state', () => { - const { getByA11yState } = render( + it('does render a disabled accessibility state', async () => { + await render( {}} disabled> {null} ); - expect(getByA11yState({ disabled: true })).toBeOnTheScreen(); + expect(screen.getByTestId('card')).toBeDisabled(); }); }); describe('CardCover', () => { - it('renders with custom border radius', () => { - const { getByTestId } = render( + it('renders with custom border radius', async () => { + await render( { ); - expect(getByTestId('card-cover')).toHaveStyle(styles.customCoverRadius); + expect(screen.getByTestId('card-cover')).toHaveStyle( + styles.customCoverRadius + ); }); }); describe('CardActions', () => { - it('renders button with passed mode', () => { - const { getByTestId } = render( + it('renders button with passed mode', async () => { + await render( @@ -137,13 +135,14 @@ describe('CardActions', () => { ); - expect(getByTestId('card-actions').props.children[0].props.mode).toBe( - 'contained' - ); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('card-actions').props.children[0].props.mode + ).toBe('contained'); }); - it('renders button with custom styles', () => { - const { getByTestId } = render( + it('renders button with custom styles', async () => { + await render( ); - expect(getByTestId('button-cancel')).toBeDefined(); - expect(getByTestId('button-ok')).toBeDefined(); + expect(screen.getByTestId('button-cancel')).toBeOnTheScreen(); + expect(screen.getByTestId('button-ok')).toBeOnTheScreen(); }); - it('should apply default styles', () => { - const { getByTestId } = render( + it('should apply default styles', async () => { + await render( ); - const dialogActionsContainer = getByTestId('dialog-actions'); + const dialogActionsContainer = screen.getByTestId('dialog-actions'); const dialogActionButtons = dialogActionsContainer.children; expect(dialogActionsContainer).toHaveStyle({ @@ -138,15 +140,15 @@ describe('DialogActions', () => { expect(dialogActionButtons[1]).toHaveStyle({ marginRight: 0 }); }); - it('should apply custom styles', () => { - const { getByTestId } = render( + it('should apply custom styles', async () => { + await render( ); - const dialogActionsContainer = getByTestId('dialog-actions'); + const dialogActionsContainer = screen.getByTestId('dialog-actions'); const dialogActionButtons = dialogActionsContainer.children; expect(dialogActionButtons[0]).toHaveStyle({ margin: 10 }); diff --git a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx index 9538fcc56e..3df20be862 100644 --- a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx +++ b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx @@ -1,11 +1,11 @@ import { describe, expect, it } from '@jest/globals'; -import { render } from '../../../test-utils'; +import { render, screen } from '../../../test-utils'; import DrawerCollapsedItem from '../../Drawer/DrawerCollapsedItem'; describe('DrawerCollapsedItem', () => { - it('should have regular outline if label is specified', () => { - const { getByTestId } = render( + it('should have regular outline if label is specified', async () => { + await render( { /> ); - expect(getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ + expect(screen.getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ height: 32, }); }); - it('should have rounded outline if label is not specified', () => { - const { getByTestId } = render( + it('should have rounded outline if label is not specified', async () => { + await render( ); - expect(getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ + expect(screen.getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ height: 56, }); }); - it('should display unfocused icon in inactive state, if unfocused icon is specified', () => { - const { getByTestId } = render( + it('should display unfocused icon in inactive state, if unfocused icon is specified', async () => { + await render( ); expect( - getByTestId('drawer-collapsed-item-container').props.children[1].props - .source + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('drawer-collapsed-item-container').props.children[1] + .props.source ).toBe('star-outline'); }); - it('should display focused icon in inactive state, if unfocused icon is not specified', () => { - const { getByTestId } = render(); + it('should display focused icon in inactive state, if unfocused icon is not specified', async () => { + await render(); expect( - getByTestId('drawer-collapsed-item-container').props.children[1].props - .source + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('drawer-collapsed-item-container').props.children[1] + .props.source ).toBe('star'); }); - it('should display focused icon in active state', () => { - const { getByTestId } = render( + it('should display focused icon in active state', async () => { + await render( { ); expect( - getByTestId('drawer-collapsed-item-container').props.children[1].props - .source + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('drawer-collapsed-item-container').props.children[1] + .props.source ).toBe('star'); }); }); diff --git a/src/components/__tests__/Drawer/DrawerSection.test.tsx b/src/components/__tests__/Drawer/DrawerSection.test.tsx index 08283ba7a2..9b19f0ef25 100644 --- a/src/components/__tests__/Drawer/DrawerSection.test.tsx +++ b/src/components/__tests__/Drawer/DrawerSection.test.tsx @@ -6,11 +6,13 @@ import { render } from '../../../test-utils'; import DrawerSection from '../../Drawer/DrawerSection'; describe('DrawerSection', () => { - it('renders properly', () => { - const tree = render( - - - + it('renders properly', async () => { + const tree = ( + await render( + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/components/__tests__/DrawerItem.test.tsx b/src/components/__tests__/DrawerItem.test.tsx index 375d1c460b..a47bbc043b 100644 --- a/src/components/__tests__/DrawerItem.test.tsx +++ b/src/components/__tests__/DrawerItem.test.tsx @@ -3,25 +3,25 @@ import { expect, it } from '@jest/globals'; import { render } from '../../test-utils'; import DrawerItem from '../Drawer/DrawerItem'; -it('renders basic DrawerItem', () => { - const tree = render( - {}} label="Example item" /> +it('renders basic DrawerItem', async () => { + const tree = ( + await render( {}} label="Example item" />) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders DrawerItem with icon', () => { - const tree = render( - +it('renders DrawerItem with icon', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders active DrawerItem', () => { - const tree = render( - +it('renders active DrawerItem', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/components/__tests__/FAB.test.tsx b/src/components/__tests__/FAB.test.tsx index 38ee6308b6..2e233eaa63 100644 --- a/src/components/__tests__/FAB.test.tsx +++ b/src/components/__tests__/FAB.test.tsx @@ -1,94 +1,100 @@ import { expect, it, jest } from '@jest/globals'; -import { fireEvent } from '@testing-library/react-native'; +import { fireEvent, userEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import FAB from '../FAB'; -it('renders FAB with default props', () => { - const tree = render().toJSON(); +it('renders FAB with default props', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with primary variant', () => { - const tree = render().toJSON(); +it('renders FAB with primary variant', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with secondary variant', () => { - const tree = render().toJSON(); +it('renders FAB with secondary variant', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with tertiary variant', () => { - const tree = render().toJSON(); +it('renders FAB with tertiary variant', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with tonalSecondary variant', () => { - const tree = render().toJSON(); +it('renders FAB with tonalSecondary variant', async () => { + const tree = ( + await render() + ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with tonalTertiary variant', () => { - const tree = render().toJSON(); +it('renders FAB with tonalTertiary variant', async () => { + const tree = ( + await render() + ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB medium size', () => { - const tree = render().toJSON(); +it('renders FAB medium size', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB large size', () => { - const tree = render().toJSON(); +it('renders FAB large size', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with containerColor override', () => { - const tree = render().toJSON(); +it('renders FAB with containerColor override', async () => { + const tree = ( + await render() + ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with containerColor and contentColor overrides', () => { - const tree = render( - +it('renders FAB with containerColor and contentColor overrides', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB with accessibilityLabel', () => { - const tree = render( - +it('renders FAB with accessibilityLabel', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB transitioning to not visible', () => { - const { update, toJSON } = render(); - update(); +it('renders FAB transitioning to not visible', async () => { + const { rerender, toJSON } = await render(); + await rerender(); expect(toJSON()).toMatchSnapshot(); }); -it('renders FAB transitioning to visible', () => { - const { update, toJSON } = render(); - update(); +it('renders FAB transitioning to visible', async () => { + const { rerender, toJSON } = await render( + + ); + await rerender(); expect(toJSON()).toMatchSnapshot(); }); -it('calls onPress when FAB is pressed', () => { +it('calls onPress when FAB is pressed', async () => { const onPress = jest.fn(); - const { getByTestId } = render( - - ); - fireEvent.press(getByTestId('fab')); + await render(); + await userEvent.press(screen.getByTestId('fab')); expect(onPress).toHaveBeenCalledTimes(1); }); -it('forwards event object to onPress', () => { +it('forwards event object to onPress', async () => { const onPress = jest.fn(); - const { getByTestId } = render( - - ); - fireEvent(getByTestId('fab'), 'onPress', { key: 'value' }); + await render(); + await fireEvent(screen.getByTestId('fab'), 'onPress', { key: 'value' }); expect(onPress).toHaveBeenCalledWith({ key: 'value' }); }); diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx index e3c1490f65..2d24e0a038 100644 --- a/src/components/__tests__/FABExtended.test.tsx +++ b/src/components/__tests__/FABExtended.test.tsx @@ -1,54 +1,64 @@ import { expect, it, jest } from '@jest/globals'; -import { fireEvent } from '@testing-library/react-native'; +import { fireEvent, userEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import FAB from '../FAB'; -it('renders extended FAB expanded', () => { - const tree = render( - +it('renders extended FAB expanded', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders extended FAB collapsed', () => { - const tree = render( - +it('renders extended FAB collapsed', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders extended FAB not visible', () => { - const tree = render( - +it('renders extended FAB not visible', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders extended FAB medium size', () => { - const tree = render( - +it('renders extended FAB medium size', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders extended FAB large size', () => { - const tree = render( - +it('renders extended FAB large size', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders extended FAB transitioning to collapsed', () => { - const { update, toJSON } = render( +it('renders extended FAB transitioning to collapsed', async () => { + const { rerender, toJSON } = await render( ); - update(); + await rerender( + + ); expect(toJSON()).toMatchSnapshot(); }); -it('uses label as default accessibilityLabel', () => { - const { getByTestId } = render( +it('uses label as default accessibilityLabel', async () => { + await render( { testID="extended-fab" /> ); - expect(getByTestId('extended-fab').props.accessibilityLabel).toBe( - 'New message' - ); + + expect(screen.getByLabelText('New message')).toBeOnTheScreen(); }); -it('respects explicit accessibilityLabel', () => { - const { getByTestId } = render( +it('respects explicit accessibilityLabel', async () => { + await render( { testID="extended-fab" /> ); - expect(getByTestId('extended-fab').props.accessibilityLabel).toBe( - 'Create new message' - ); + + expect(screen.getByLabelText('Create new message')).toBeOnTheScreen(); }); -it('calls onPress when pressed', () => { +it('calls onPress when pressed', async () => { const onPress = jest.fn(); - const { getByTestId } = render( + await render( { testID="extended-fab" /> ); - fireEvent.press(getByTestId('extended-fab')); + await userEvent.press(screen.getByTestId('extended-fab')); expect(onPress).toHaveBeenCalledTimes(1); }); -it('forwards event object to onPress', () => { +it('forwards event object to onPress', async () => { const onPress = jest.fn(); - const { getByTestId } = render( + await render( { testID="extended-fab" /> ); - fireEvent(getByTestId('extended-fab'), 'onPress', { key: 'value' }); + await fireEvent(screen.getByTestId('extended-fab'), 'onPress', { + key: 'value', + }); expect(onPress).toHaveBeenCalledWith({ key: 'value' }); }); diff --git a/src/components/__tests__/FABMenu.test.tsx b/src/components/__tests__/FABMenu.test.tsx index 87b3b38f2f..50135634d7 100644 --- a/src/components/__tests__/FABMenu.test.tsx +++ b/src/components/__tests__/FABMenu.test.tsx @@ -1,119 +1,131 @@ import { expect, it, jest } from '@jest/globals'; -import { fireEvent } from '@testing-library/react-native'; +import { fireEvent, userEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import FAB from '../FAB'; +import type { MenuItemProps } from '../FAB/Menu'; const makeItems = ( onItemPress = jest.fn<(event?: unknown) => void>() -): [ - { label: string; onPress: typeof onItemPress; testID: string }, - { label: string; onPress: typeof onItemPress; testID: string } -] => [ +): [MenuItemProps, MenuItemProps] => [ { label: 'Send email', onPress: onItemPress, testID: 'item-0' }, { label: 'Set reminder', onPress: onItemPress, testID: 'item-1' }, ]; -it('renders FAB.Menu closed', () => { - const tree = render( - +it('renders FAB.Menu closed', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB.Menu open', () => { - const tree = render( - +it('renders FAB.Menu open', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB.Menu with 6 items', () => { - const tree = render( - +it('renders FAB.Menu with 6 items', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB.Menu with start alignment', () => { - const tree = render( - +it('renders FAB.Menu with start alignment', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB.Menu with center alignment', () => { - const tree = render( - +it('renders FAB.Menu with center alignment', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB.Menu with items having icons', () => { - const tree = render( - +it('renders FAB.Menu with items having icons', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders FAB.Menu not expanded when trigger is not visible', () => { - const tree = render( - +it('renders FAB.Menu not expanded when trigger is not visible', async () => { + const tree = ( + await render( + + ) ).toJSON(); // effectiveExpanded = visible && expanded = false expect(tree).toMatchSnapshot(); }); -it('calls item onPress when menu item is pressed', () => { +it('calls item onPress when menu item is pressed', async () => { const onItemPress = jest.fn(); - const { getByTestId } = render( + await render( { items={makeItems(onItemPress)} /> ); - fireEvent.press(getByTestId('item-0')); + await userEvent.press(screen.getByTestId('item-0')); expect(onItemPress).toHaveBeenCalledTimes(1); }); -it('forwards event object to item onPress', () => { +it('forwards event object to item onPress', async () => { const onItemPress = jest.fn(); - const { getByTestId } = render( + await render( { items={makeItems(onItemPress)} /> ); - fireEvent(getByTestId('item-0'), 'onPress', { key: 'value' }); + await fireEvent(screen.getByTestId('item-0'), 'onPress', { key: 'value' }); expect(onItemPress).toHaveBeenCalledWith({ key: 'value' }); }); -it('calls onDismiss when menu item is pressed', () => { +it('calls onDismiss when menu item is pressed', async () => { const onDismiss = jest.fn(); - const { getByTestId } = render( + await render( { items={makeItems()} /> ); - fireEvent.press(getByTestId('item-0')); + await userEvent.press(screen.getByTestId('item-0')); expect(onDismiss).toHaveBeenCalledTimes(1); }); -it('calls trigger onPress when menu is closed', () => { +it('calls trigger onPress when menu is closed', async () => { const onTriggerPress = jest.fn(); - const { getByTestId } = render( + await render( { /> ); // Shell's TouchableRipple uses the default testID 'fab-shell' - fireEvent.press(getByTestId('fab-shell')); + await userEvent.press(screen.getByTestId('fab-shell')); expect(onTriggerPress).toHaveBeenCalledTimes(1); }); -it('calls onDismiss when trigger is pressed while menu is open', () => { +it('calls onDismiss when trigger is pressed while menu is open', async () => { const onDismiss = jest.fn(); - const { getByTestId } = render( + await render( { items={makeItems()} /> ); - fireEvent.press(getByTestId('fab-shell')); + await userEvent.press(screen.getByTestId('fab-shell')); expect(onDismiss).toHaveBeenCalledTimes(1); }); diff --git a/src/components/__tests__/Icon.test.tsx b/src/components/__tests__/Icon.test.tsx index 940637e1a3..d1d280a63f 100644 --- a/src/components/__tests__/Icon.test.tsx +++ b/src/components/__tests__/Icon.test.tsx @@ -2,35 +2,37 @@ import { Image } from 'react-native'; import { describe, expect, it } from '@jest/globals'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import Icon from '../Icon'; const ICON_SIZE = 24; describe('Icon Component', () => { - it('renders correctly with image source', () => { + it('renders correctly with image source', async () => { const source = { uri: 'https://picsum.photos/700' }; - const { getByTestId } = render( - - ); - const imageIcon = getByTestId('image-icon'); + await render(); + const imageIcon = screen.getByTestId('image-icon', { + includeHiddenElements: true, + }); expect(imageIcon).toBeOnTheScreen(); expect(imageIcon).toHaveStyle({ width: ICON_SIZE, height: ICON_SIZE }); }); - it('renders correctly with string source', () => { + it('renders correctly with string source', async () => { const source = 'camera'; - const { getByTestId } = render( + await render( ); - const icon = getByTestId('camera-icon'); + const icon = screen.getByTestId('camera-icon', { + includeHiddenElements: true, + }); expect(icon).toBeOnTheScreen(); expect(icon).toHaveStyle({ fontSize: ICON_SIZE, lineHeight: ICON_SIZE }); }); - it('renders correctly with function source', () => { + it('renders correctly with function source', async () => { const source = ({ size, testID }: { size: number; testID: string }) => ( { testID={testID} /> ); - const { getByTestId } = render( + await render( ); - const functionIcon = getByTestId('function-icon'); + const functionIcon = screen.getByTestId('function-icon', { + includeHiddenElements: true, + }); expect(functionIcon).toHaveStyle({ width: ICON_SIZE, height: ICON_SIZE }); expect(functionIcon).toBeOnTheScreen(); diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index e61594509e..b28456c5ce 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -4,7 +4,7 @@ import { describe, expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import { pink500 } from '../../theme/colors'; import { tokens } from '../../theme/tokens'; import IconButton from '../IconButton/IconButton'; @@ -21,40 +21,40 @@ const styles = StyleSheet.create({ }, }); -it('renders icon button by default', () => { - const tree = render().toJSON(); +it('renders icon button by default', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders icon button with color', () => { - const tree = render( - +it('renders icon button with color', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders icon button with size', () => { - const tree = render().toJSON(); +it('renders icon button with size', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders disabled icon button', () => { - const tree = render().toJSON(); +it('renders disabled icon button', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders icon change animated', () => { - const tree = render().toJSON(); +it('renders icon change animated', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders icon button with custom border radius', () => { - const { getByTestId } = render( +it('renders icon button with custom border radius', async () => { + await render( { /> ); - expect(getByTestId('icon-button-container')).toHaveStyle({ borderRadius: 0 }); + expect(screen.getByTestId('icon-button-container')).toHaveStyle({ + borderRadius: 0, + }); }); -it('renders icon button with small border radius', () => { - const { getByTestId } = render( +it('renders icon button with small border radius', async () => { + await render( { /> ); - expect(getByTestId('icon-button-container')).toHaveStyle({ borderRadius: 4 }); + expect(screen.getByTestId('icon-button-container')).toHaveStyle({ + borderRadius: 4, + }); }); describe('getIconButtonColor - icon color', () => { @@ -315,16 +319,16 @@ describe('getIconButtonColor - border color', () => { }); }); -it('action animated value changes correctly', () => { +it('action animated value changes correctly', async () => { const value = new Animated.Value(1); - const { getByTestId } = render( + await render( ); - expect(getByTestId('icon-button-container-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('icon-button-container-outer-layer')).toHaveStyle({ transform: [{ scale: 1 }], }); @@ -334,10 +338,10 @@ it('action animated value changes correctly', () => { duration: 200, }).start(); - act(() => { + await act(() => { jest.advanceTimersByTime(200); }); - expect(getByTestId('icon-button-container-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('icon-button-container-outer-layer')).toHaveStyle({ transform: [{ scale: 1.5 }], }); }); diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index b1e4ac79d2..ad1783ec52 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -17,75 +17,85 @@ const styles = StyleSheet.create({ }, }); -it('renders list accordion with children', () => { - const tree = render( - } - title="Expandable list item" - > - - +it('renders list accordion with children', async () => { + const tree = ( + await render( + } + title="Expandable list item" + > + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders multiline list accordion', () => { - const tree = render( - - - +it('renders multiline list accordion', async () => { + const tree = ( + await render( + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list accordion with left items', () => { - const tree = render( - } - title="Accordion item 1" - > - } - title="List item 1" - /> - +it('renders list accordion with left items', async () => { + const tree = ( + await render( + } + title="Accordion item 1" + > + } + title="List item 1" + /> + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders expanded accordion', () => { - const tree = render( - - - +it('renders expanded accordion', async () => { + const tree = ( + await render( + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list accordion with custom title and description styles', () => { - const tree = render( - - - +it('renders list accordion with custom title and description styles', async () => { + const tree = ( + await render( + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); describe('ListAccordion', () => { - it('should not throw an error when id={0}', () => { + it('should not throw an error when id={0}', async () => { const ListAccordionTest = () => ( @@ -94,12 +104,10 @@ describe('ListAccordion', () => { ); - expect(() => render()).not.toThrow( - 'List.Accordion is used inside a List.AccordionGroup without specifying an id prop.' - ); + await expect(render()).resolves.toBeDefined(); }); - it('should throw an error when id={""}', () => { + it('should throw an error when id={""}', async () => { const ListAccordionTest = () => ( @@ -108,7 +116,7 @@ describe('ListAccordion', () => { ); - expect(() => render()).toThrow( + await expect(render()).rejects.toThrow( 'List.Accordion is used inside a List.AccordionGroup without specifying an id prop.' ); }); diff --git a/src/components/__tests__/ListImage.test.tsx b/src/components/__tests__/ListImage.test.tsx index 96c524467a..8aa7e9b54d 100644 --- a/src/components/__tests__/ListImage.test.tsx +++ b/src/components/__tests__/ListImage.test.tsx @@ -2,7 +2,7 @@ import { StyleSheet } from 'react-native'; import { expect, it } from '@jest/globals'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import ListImage from '../List/ListImage'; const styles = StyleSheet.create({ @@ -23,45 +23,49 @@ const styles = StyleSheet.create({ const testID = 'list-image'; -it('renders ListImage with default variant', () => { - const tree = render( - +it('renders ListImage with default variant', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders ListImage with default variant & styles', () => { - const tree = render( - +it('renders ListImage with default variant & styles', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders ListImage with `image` variant', () => { - const tree = render( +it('renders ListImage with `image` variant', async () => { + await render( ); - expect(tree.getByTestId(testID)).toHaveStyle(styles.image); + expect(screen.getByTestId(testID)).toHaveStyle(styles.image); }); -it('renders ListImage with `video` variant', () => { - const tree = render( +it('renders ListImage with `video` variant', async () => { + await render( ); - expect(tree.getByTestId(testID)).toHaveStyle(styles.video); + expect(screen.getByTestId(testID)).toHaveStyle(styles.video); }); diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index 088be073fc..b50f4e7d3f 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -3,9 +3,9 @@ import { Platform, StyleSheet } from 'react-native'; import { Text, View } from 'react-native'; import { expect, it, jest } from '@jest/globals'; -import { fireEvent } from '@testing-library/react-native'; +import { userEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import { red500 } from '../../theme/colors'; import Chip from '../Chip/Chip'; import IconButton from '../IconButton/IconButton'; @@ -26,117 +26,131 @@ const styles = StyleSheet.create({ const testID = 'list-item'; -it('renders list item with title and description', () => { - const tree = render( - +it('renders list item with title and description', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list item with left item', () => { - const tree = render( - } - /> +it('renders list item with left item', async () => { + const tree = ( + await render( + } + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list item with right item', () => { - const tree = render( - GG} - /> +it('renders list item with right item', async () => { + const tree = ( + await render( + GG} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list item with left and right items', () => { - const tree = render( - GG} - right={(props) => } - /> +it('renders list item with left and right items', async () => { + const tree = ( + await render( + GG} + right={(props) => } + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list item with custom title and description styles', () => { - const tree = render( - +it('renders list item with custom title and description styles', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list item with custom description', () => { - const tree = render( - ( - - - React Native Paper is a high-quality, standard-compliant Design - Design library that has you covered in all major use-cases. - +it('renders list item with custom description', async () => { + const tree = ( + await render( + ( - {}}> - DOCS.pdf - + + React Native Paper is a high-quality, standard-compliant Design + Design library that has you covered in all major use-cases. + + + {}}> + DOCS.pdf + + - - )} - testID={testID} - /> + )} + testID={testID} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders with a description with typeof number', () => { - const tree = render( - +it('renders with a description with typeof number', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('calling onPress on ListItem right component', () => { +it('calling onPress on ListItem right component', async () => { Platform.OS = 'web'; const onPress = jest.fn<(event: GestureResponderEvent) => void>(); - const { getByTestId } = render( + await render( { /> ); - fireEvent(getByTestId('icon-button'), 'onPress'); + await userEvent.press(screen.getByTestId('icon-button')); expect(onPress).toHaveBeenCalledTimes(1); }); -it('renders list item with custom content style', () => { - const { getByTestId } = render( +it('renders list item with custom content style', async () => { + await render( { /> ); - expect(getByTestId('list-item-content')).toHaveStyle(styles.content); + expect(screen.getByTestId('list-item-content')).toHaveStyle(styles.content); }); diff --git a/src/components/__tests__/ListSection.test.tsx b/src/components/__tests__/ListSection.test.tsx index 545b3b830e..7afe889182 100644 --- a/src/components/__tests__/ListSection.test.tsx +++ b/src/components/__tests__/ListSection.test.tsx @@ -17,59 +17,65 @@ const styles = StyleSheet.create({ const testID = 'list-item'; -it('renders list section without subheader', () => { - const tree = render( - - } - /> - } - /> - +it('renders list section without subheader', async () => { + const tree = ( + await render( + + } + /> + } + /> + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list section with subheader', () => { - const tree = render( - - Some title - } - /> - } - /> - +it('renders list section with subheader', async () => { + const tree = ( + await render( + + Some title + } + /> + } + /> + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders list section with custom title style', () => { - const tree = render( - - } - /> - } - /> - +it('renders list section with custom title style', async () => { + const tree = ( + await render( + + } + /> + } + /> + + ) ).toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 412b744047..128fb87cdb 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -17,63 +17,69 @@ const styles = StyleSheet.create({ }, }); -it('renders visible menu', () => { - const tree = render( - - Open menu} - > - - - - +it('renders visible menu', async () => { + const tree = ( + await render( + + Open menu} + > + + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders not visible menu', () => { - const tree = render( - - Open menu} - > - - - - +it('renders not visible menu', async () => { + const tree = ( + await render( + + Open menu} + > + + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders menu with content styles', () => { - const tree = render( - - Open menu} - contentStyle={styles.contentStyle} - > - - - - +it('renders menu with content styles', async () => { + const tree = ( + await render( + + Open menu} + contentStyle={styles.contentStyle} + > + + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); ([0, 1, 2, 3, 4, 5] as Elevation[]).forEach((elevation) => - it(`renders menu with background color based on elevation value = ${elevation}`, () => { + it(`renders menu with background color based on elevation value = ${elevation}`, async () => { const theme = getTheme(); - const { getByTestId } = render( + await render( { ); - expect(getByTestId('menu-surface')).toHaveStyle({ + expect(screen.getByTestId('menu-surface')).toHaveStyle({ backgroundColor: theme.colors.elevation[`level${elevation}`], }); }) @@ -124,14 +130,14 @@ it('uses the default anchorPosition of top', async () => { ); } - render(makeMenu(false)); + const { rerender } = await render(makeMenu(false)); // You must update instead of creating directly and using it because // componentDidUpdate isn't called by default in jest. Forcing the update // than triggers measureInWindow, which is how Menu decides where to show // itself. await act(async () => { - screen.update(makeMenu(true)); + await rerender(makeMenu(true)); // Menu waits a tick for Portal refs to be up-to-date. await Promise.resolve(); }); @@ -181,10 +187,10 @@ it('respects anchorPosition bottom', async () => { ); } - render(makeMenu(false)); + const { rerender } = await render(makeMenu(false)); await act(async () => { - screen.update(makeMenu(true)); + await rerender(makeMenu(true)); // Menu waits a tick for Portal refs to be up-to-date. await Promise.resolve(); }); @@ -202,9 +208,9 @@ it('respects anchorPosition bottom', async () => { dimensionsSpy.mockRestore(); }); -it('animated value changes correctly', () => { +it('animated value changes correctly', async () => { const value = new Animated.Value(1); - const { getByTestId } = render( + await render( { ); - expect(getByTestId('menu-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('menu-surface-outer-layer')).toHaveStyle({ transform: [{ scale: 1 }], }); @@ -227,16 +233,16 @@ it('animated value changes correctly', () => { duration: 200, }).start(); - act(() => { + await act(() => { jest.advanceTimersByTime(200); }); - expect(getByTestId('menu-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('menu-surface-outer-layer')).toHaveStyle({ transform: [{ scale: 1.5 }], }); }); -it('renders menu with mode "elevated"', () => { - const { getByTestId } = render( +it('renders menu with mode "elevated"', async () => { + await render( { ); - const menuSurface = getByTestId('menu-surface'); + const menuSurface = screen.getByTestId('menu-surface'); // Get flattened styles + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. const styles = StyleSheet.flatten(menuSurface.props.style); expect(styles).toHaveProperty('shadowColor'); expect(styles).toHaveProperty('shadowOpacity'); }); -it('renders menu with mode "flat"', () => { - const { getByTestId } = render( +it('renders menu with mode "flat"', async () => { + await render( { ); - const menuSurface = getByTestId('menu-surface'); + const menuSurface = screen.getByTestId('menu-surface'); // Get flattened styles + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. const styles = StyleSheet.flatten(menuSurface.props.style); expect(styles).not.toHaveProperty('shadowColor'); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index db7b30bc21..e4f683fa52 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -1,7 +1,7 @@ import { describe, expect, it } from '@jest/globals'; import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import { tokens } from '../../theme/tokens'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; @@ -9,34 +9,36 @@ import { getMenuItemColor } from '../Menu/utils'; const stateOpacity = tokens.md.sys.state.opacity; describe('Menu Item', () => { - it('renders menu item', () => { - const tree = render( - <> - {}} title="Redo" /> - {}} title="Undo" /> - {}} - title="Cut" - disabled - /> - {}} - title="Copy" - disabled - /> - {}} title="Paste" /> - + it('renders menu item', async () => { + const tree = ( + await render( + <> + {}} title="Redo" /> + {}} title="Undo" /> + {}} + title="Cut" + disabled + /> + {}} + title="Copy" + disabled + /> + {}} title="Paste" /> + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); - it('should have titleMaxFontSizeMultiplier passed to title', () => { + it('should have titleMaxFontSizeMultiplier passed to title', async () => { const labelMaxFontSizeMultiplier = 2; - const { getByTestId } = render( + await render( { /> ); - expect(getByTestId('menu-item-title').props.maxFontSizeMultiplier).toBe( - labelMaxFontSizeMultiplier - ); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('menu-item-title').props.maxFontSizeMultiplier + ).toBe(labelMaxFontSizeMultiplier); }); - it('accepts different values for accessibilityState', () => { - const { getByTestId } = render( + it('accepts different values for accessibilityState', async () => { + await render( { /> ); - expect(getByTestId('touchable').props.accessibilityState).toMatchObject({ + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('touchable').props.accessibilityState + ).toMatchObject({ checked: true, }); }); diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 2cf6020c36..2009f3d444 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -2,9 +2,9 @@ import { Animated, BackHandler as RNBackHandler, Text } from 'react-native'; import type { BackHandlerStatic as RNBackHandlerStatic } from 'react-native'; import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'; -import { act, fireEvent } from '@testing-library/react-native'; +import { act, userEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import Modal from '../Modal'; @@ -37,30 +37,30 @@ describe('Modal', () => { }); describe('by default', () => { - it('should render passed children', () => { - const { getByTestId } = render( + it('should render passed children', async () => { + await render( Children ); - expect(getByTestId('modal')).toHaveTextContent('Children'); + expect(screen.getByTestId('modal')).toHaveTextContent('Children'); }); - it("should render a backdrop in default theme's color", () => { - const { getByTestId } = render( + it("should render a backdrop in default theme's color", async () => { + await render( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ backgroundColor: LightTheme.colors.scrim, }); }); - it('should render a custom backdrop color if specified', () => { - const { getByTestId } = render( + it('should render a custom backdrop color if specified', async () => { + await render( { ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ backgroundColor: 'transparent', }); }); - it('should receive appropriate top and bottom insets', () => { - const { getByTestId } = render( + it('should receive appropriate top and bottom insets', async () => { + await render( {null} ); - expect(getByTestId('modal-wrapper')).toHaveStyle({ + expect(screen.getByTestId('modal-wrapper')).toHaveStyle({ marginTop: 37, marginBottom: 44, }); @@ -94,9 +94,9 @@ describe('Modal', () => { }); describe('when open', () => { describe('if backdrop touched', () => { - it('should invoke the onDismiss function immediately', () => { + it('should invoke the onDismiss function immediately', async () => { const onDismiss = jest.fn(); - const { getByTestId } = render( + await render( {null} @@ -104,25 +104,23 @@ describe('Modal', () => { expect(onDismiss).not.toHaveBeenCalled(); - act(() => { - fireEvent.press(getByTestId('modal-backdrop')); - }); + await userEvent.press(screen.getByTestId('modal-backdrop')); expect(onDismiss).toHaveBeenCalled(); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); @@ -130,78 +128,78 @@ describe('Modal', () => { }); }); - it('runs the closing animation if visible toggled', () => { - const { getByTestId, queryByTestId, rerender } = render( + it('runs the closing animation if visible toggled', async () => { + const { rerender } = await render( {}}> {null} ); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { - fireEvent.press(getByTestId('modal-backdrop')); - }); + await userEvent.press(screen.getByTestId('modal-backdrop')); - rerender( + await rerender( {}}> {null} ); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(queryByTestId('modal-surface-outer-layer')).not.toBeOnTheScreen(); + expect( + screen.queryByTestId('modal-surface-outer-layer') + ).not.toBeOnTheScreen(); - expect(queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); + expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); }); describe('if closed via Android back button', () => { it('invokes onDismiss', async () => { const onDismiss = jest.fn(); - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { BackHandler.mockPressBack(); }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); @@ -213,7 +211,7 @@ describe('Modal', () => { describe('when open as non-dismissible modal', () => { describe('if closed via touching backdrop', () => { it('will run the animation but not fade out', async () => { - const { getByTestId } = render( + await render( { ); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { - fireEvent.press(getByTestId('modal-backdrop')); - }); + await userEvent.press(screen.getByTestId('modal-backdrop')); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); }); - it('should not invoke onDismiss', () => { + it('should not invoke onDismiss', async () => { const onDismiss = jest.fn(); - const { getByTestId } = render( + await render( { expect(onDismiss).not.toHaveBeenCalled(); - act(() => { - fireEvent.press(getByTestId('modal-backdrop')); - }); + await userEvent.press(screen.getByTestId('modal-backdrop')); expect(onDismiss).not.toHaveBeenCalled(); - act(() => { + await act(() => { jest.runAllTimers(); }); @@ -280,7 +274,7 @@ describe('Modal', () => { describe('if closed via Android back button', () => { it('will run the animation but not fade out', async () => { - const { getByTestId } = render( + await render( { ); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { BackHandler.mockPressBack(); }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); }); - it('should not invoke onDismiss', () => { + it('should not invoke onDismiss', async () => { const onDismiss = jest.fn(); - render( + await render( { expect(onDismiss).not.toHaveBeenCalled(); - act(() => { + await act(() => { BackHandler.mockPressBack(); }); expect(onDismiss).not.toHaveBeenCalled(); - act(() => { + await act(() => { jest.runAllTimers(); }); @@ -349,36 +343,36 @@ describe('Modal', () => { describe('when visible prop changes', () => { describe('from false to true (closed to open)', () => { - it('should run fade-in animation on opening', () => { - const { queryByTestId, getByTestId, rerender } = render( + it('should run fade-in animation on opening', async () => { + const { rerender } = await render( {null} ); - expect(queryByTestId('modal')).toBe(null); + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); - rerender( + await rerender( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: 0, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 0, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); }); @@ -386,43 +380,43 @@ describe('Modal', () => { describe('from true to false (open to closed)', () => { it('should run fade-out animation on closing', async () => { - const { queryByTestId, getByTestId, rerender } = render( + const { rerender } = await render( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - rerender( + await rerender( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(queryByTestId('modal')).toBe(null); + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); }); it('should not invoke onDismiss', async () => { const onDismiss = jest.fn(); - const { rerender } = render( + const { rerender } = await render( {null} @@ -430,7 +424,7 @@ describe('Modal', () => { expect(onDismiss).not.toHaveBeenCalled(); - rerender( + await rerender( {null} @@ -438,7 +432,7 @@ describe('Modal', () => { expect(onDismiss).not.toHaveBeenCalled(); - act(() => { + await act(() => { jest.runAllTimers(); }); @@ -446,144 +440,144 @@ describe('Modal', () => { }); it('should close even if the dialog is not dismissible', async () => { - const { queryByTestId, getByTestId, rerender } = render( + const { rerender } = await render( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - rerender( + await rerender( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(queryByTestId('modal')).toBe(null); + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); }); }); }); describe('when visible prop changes again during the open/close animation', () => { describe('while closing, back to true (visible)', () => { - it('should keep the modal open', () => { - const { getByTestId, rerender } = render( + it('should keep the modal open', async () => { + const { rerender } = await render( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - rerender( + await rerender( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); - act(() => { + await act(() => { // Not a real seconds, this depends on how frequently // requestAnimationFrame is called jest.advanceTimersToNextTimer(1000); }); - rerender( + await rerender( {null} ); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 1, }); }); }); describe('while opening, back to false (hidden)', () => { - it('should keep the modal closed', () => { - const { queryByTestId, getByTestId, rerender } = render( + it('should keep the modal closed', async () => { + const { rerender } = await render( {null} ); - expect(queryByTestId('modal-backdrop')).toBe(null); + expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); - rerender( + await rerender( {null} ); - expect(getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: 0, }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ opacity: 0, }); - act(() => { + await act(() => { // Not a real seconds, this depends on how frequently // requestAnimationFrame is called jest.advanceTimersToNextTimer(1000); }); - expect(queryByTestId('modal-backdrop')).not.toBe(null); + expect(screen.getByTestId('modal-backdrop')).toBeOnTheScreen(); - rerender( + await rerender( {null} ); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(queryByTestId('modal-backdrop')).toBe(null); + expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); }); }); }); - it('animated value changes correctly', () => { + it('animated value changes correctly', async () => { const value = new Animated.Value(1); - const { getByTestId } = render( + await render( { {null} ); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ transform: [{ scale: 1 }], }); @@ -602,11 +596,11 @@ describe('Modal', () => { duration: 200, }).start(); - act(() => { + await act(() => { jest.runAllTimers(); }); - expect(getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ transform: [{ scale: 1.5 }], }); }); diff --git a/src/components/__tests__/Portal.test.tsx b/src/components/__tests__/Portal.test.tsx index afaabeac07..14f07c141b 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -1,15 +1,14 @@ import { Text } from 'react-native'; import { expect, it, jest } from '@jest/globals'; -import { waitFor } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import Portal from '../Portal/Portal'; jest.useRealTimers(); it('renders portal with siblings', async () => { - const { toJSON, getByTestId } = render( + const { toJSON } = await render( Outside content @@ -18,7 +17,7 @@ it('renders portal with siblings', async () => { ); - await waitFor(() => getByTestId('content')); + await screen.findByTestId('content'); expect(toJSON()).toMatchSnapshot(); }); diff --git a/src/components/__tests__/ProgressBar.test.tsx b/src/components/__tests__/ProgressBar.test.tsx index c3a89c8749..db2a2bf600 100644 --- a/src/components/__tests__/ProgressBar.test.tsx +++ b/src/components/__tests__/ProgressBar.test.tsx @@ -2,9 +2,9 @@ import * as React from 'react'; import { Animated, Platform, StyleSheet } from 'react-native'; import { afterEach, expect, it } from '@jest/globals'; -import { act } from '@testing-library/react-native'; +import { fireEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import ProgressBar from '../ProgressBar'; import type { Props } from '../ProgressBar'; @@ -23,12 +23,8 @@ const styles = StyleSheet.create({ }); const a11yRole = 'progressbar'; -const triggerLayout = async ( - tree: ReturnType -): Promise => { - await act(async () => { - tree.getByRole(a11yRole).props.onLayout(layoutEvent); - }); +const triggerLayout = async (): Promise => { + await fireEvent(screen.getByRole(a11yRole), 'layout', layoutEvent); }; class ClassProgressBar extends React.Component { @@ -44,59 +40,59 @@ afterEach(() => { }); it('renders progress bar with animated value', async () => { - const tree = render(); - await triggerLayout(tree); + const view = await render(); + await triggerLayout(); - tree.update(); + await view.rerender(); - expect(tree.getByRole(a11yRole)).toBeTruthy(); + expect(screen.getByRole(a11yRole)).toBeOnTheScreen(); }); it('renders progress bar with specific progress', async () => { - const tree = render(); - await triggerLayout(tree); + const view = await render(); + await triggerLayout(); - expect(tree.toJSON()).toMatchSnapshot(); + expect(view.toJSON()).toMatchSnapshot(); }); it('renders hidden progress bar', async () => { - const tree = render(); - await triggerLayout(tree); + const view = await render(); + await triggerLayout(); - expect(tree.toJSON()).toMatchSnapshot(); + expect(view.toJSON()).toMatchSnapshot(); }); it('renders colored progress bar', async () => { - const tree = render(); - await triggerLayout(tree); + const view = await render(); + await triggerLayout(); - expect(tree.toJSON()).toMatchSnapshot(); + expect(view.toJSON()).toMatchSnapshot(); }); it('renders indeterminate progress bar', async () => { - const tree = render(); - await triggerLayout(tree); + const view = await render(); + await triggerLayout(); - expect(tree.toJSON()).toMatchSnapshot(); + expect(view.toJSON()).toMatchSnapshot(); }); -it('renders progress bar with full height on web', () => { +it('renders progress bar with full height on web', async () => { Platform.OS = 'web'; - const tree = render(); + await render(); - expect(tree.getByRole(a11yRole)).toHaveStyle({ + expect(screen.getByRole(a11yRole)).toHaveStyle({ width: '100%', height: '100%', }); }); it('renders progress bar with custom style of filled part', async () => { - const tree = render( + await render( ); - await triggerLayout(tree); + await triggerLayout(); - expect(tree.getByTestId('progress-bar-fill')).toHaveStyle({ + expect(screen.getByTestId('progress-bar-fill')).toHaveStyle({ borderRadius: 4, }); }); diff --git a/src/components/__tests__/RadioButton/RadioButton.test.tsx b/src/components/__tests__/RadioButton/RadioButton.test.tsx index 75497ea77e..da8a04375d 100644 --- a/src/components/__tests__/RadioButton/RadioButton.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButton.test.tsx @@ -27,8 +27,8 @@ describe('RadioButton', () => { }); }); - it('renders properly', () => { - const tree = render().toJSON(); + it('renders properly', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); @@ -50,21 +50,23 @@ describe('RadioButton', () => { }); }); - it('renders properly', () => { - const tree = render().toJSON(); + it('renders properly', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); }); describe('when RadioButton is wrapped by RadioButtonContext.Provider', () => { - it('renders properly', () => { - const tree = render( - {} }} - > - - + it('renders properly', async () => { + const tree = ( + await render( + {} }} + > + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); @@ -72,9 +74,9 @@ describe('RadioButton', () => { }); describe('RadioButton with custom testID', () => { - it('renders properly', () => { - const tree = render( - + it('renders properly', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx b/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx index b4fe69463e..15bf4aa442 100644 --- a/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButtonGroup.test.tsx @@ -4,11 +4,13 @@ import { render } from '../../../test-utils'; import RadioButton from '../../RadioButton'; describe('RadioButtonGroup', () => { - it('renders properly', () => { - const tree = render( - {}}> - - + it('renders properly', async () => { + const tree = ( + await render( + {}}> + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); diff --git a/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx b/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx index b8714367bb..821d9ebc5b 100644 --- a/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx +++ b/src/components/__tests__/RadioButton/RadioButtonItem.test.tsx @@ -1,69 +1,77 @@ import { Platform } from 'react-native'; import { expect, it, jest } from '@jest/globals'; -import { act, fireEvent } from '@testing-library/react-native'; +import { userEvent } from '@testing-library/react-native'; -import { render } from '../../../test-utils'; +import { render, screen } from '../../../test-utils'; import RadioButton from '../../RadioButton'; -it('renders unchecked', () => { - const tree = render( - +it('renders unchecked', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('can render the iOS radio button on different platforms', () => { +it('can render the iOS radio button on different platforms', async () => { Platform.OS = 'android'; - const tree = render( - + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('can render the Android radio button on different platforms', () => { +it('can render the Android radio button on different platforms', async () => { Platform.OS = 'ios'; - const tree = render( - + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('can render leading radio button control', () => { +it('can render leading radio button control', async () => { Platform.OS = 'ios'; - const tree = render( - + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('should execute onLongPress', () => { +it('should execute onLongPress', async () => { const onLongPress = jest.fn(); - const { getByTestId } = render( + await render( { /> ); - act(() => { - fireEvent(getByTestId('radio-button-item'), 'longPress', { key: 'value' }); - }); + await userEvent.longPress(screen.getByTestId('radio-button-item')); expect(onLongPress).toHaveBeenCalledTimes(1); }); diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 12b66e5d11..0671263277 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -1,82 +1,87 @@ import { Animated } from 'react-native'; import { expect, it, jest } from '@jest/globals'; -import { act, fireEvent } from '@testing-library/react-native'; +import { act, userEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import * as Avatar from '../Avatar/Avatar'; import Searchbar from '../Searchbar'; -it('renders with placeholder', () => { - const tree = render().toJSON(); +it('renders with placeholder', async () => { + const tree = ( + await render() + ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders with text', () => { - const tree = render( - +it('renders with text', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('activity indicator snapshot test', () => { - const tree = render().toJSON(); +it('activity indicator snapshot test', async () => { + const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders with ActivityIndicator', () => { - const tree = render(); +it('renders with ActivityIndicator', async () => { + await render(); - expect(tree.getByTestId('activity-indicator')).toBeTruthy(); + expect(screen.getByTestId('activity-indicator')).toBeOnTheScreen(); }); -it('renders without ActivityIndicator', () => { - const { getByTestId } = render(); +it('renders without ActivityIndicator', async () => { + await render(); - expect(() => getByTestId('activity-indicator')).toThrow(); + expect(screen.queryByTestId('activity-indicator')).not.toBeOnTheScreen(); }); -it('renders clear icon with custom color', () => { - const { getByTestId } = render( +it('renders clear icon with custom color', async () => { + await render( ); - const iconComponent = getByTestId('search-bar-icon-wrapper').props.children; + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + const iconComponent = screen.getByTestId('search-bar-icon-wrapper').props + .children; + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(iconComponent.props.iconColor).toBe('purple'); }); -it('renders clear icon wrapper, which can be the target of touch events, if search has value', () => { - const { getByTestId } = render( - - ); +it('renders clear icon wrapper, which can be the target of touch events, if search has value', async () => { + await render(); - expect(getByTestId('search-bar-icon-wrapper').props.pointerEvents).toBe( - 'auto' - ); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('search-bar-icon-wrapper').props.pointerEvents + ).toBe('auto'); }); -it('renders clear icon wrapper, which is never target of touch events, if search has no value', () => { - const { getByTestId } = render(); +it('renders clear icon wrapper, which is never target of touch events, if search has no value', async () => { + await render(); - expect(getByTestId('search-bar-icon-wrapper').props.pointerEvents).toBe( - 'none' - ); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('search-bar-icon-wrapper').props.pointerEvents + ).toBe('none'); }); -it('animated value changes correctly', () => { +it('animated value changes correctly', async () => { const value = new Animated.Value(1); - const { getByTestId } = render( + await render( ); - expect(getByTestId('search-bar-container-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('search-bar-container-outer-layer')).toHaveStyle({ transform: [{ scale: 1 }], }); @@ -86,41 +91,37 @@ it('animated value changes correctly', () => { duration: 200, }).start(); - act(() => { + await act(() => { jest.advanceTimersByTime(200); }); - expect(getByTestId('search-bar-container-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('search-bar-container-outer-layer')).toHaveStyle({ transform: [{ scale: 1.5 }], }); }); -it('defines onClearIconPress action and checks if it is called when close button is pressed', () => { +it('defines onClearIconPress action and checks if it is called when close button is pressed', async () => { const onClearIconPressMock = jest.fn(); - const { getByTestId } = render( + await render( ); - const iconComponent = getByTestId('search-bar-icon-wrapper').props.children; - - fireEvent(iconComponent, 'onPress'); + await userEvent.press(screen.getByTestId('search-bar-clear-icon')); expect(onClearIconPressMock).toHaveBeenCalledTimes(1); }); -it('renders clear icon wrapper, with appropriate style for v3', () => { - const { getByTestId, update } = render( - - ); +it('renders clear icon wrapper, with appropriate style for v3', async () => { + const { rerender } = await render(); - expect(getByTestId('search-bar-icon-wrapper')).toHaveStyle({ + expect(screen.getByTestId('search-bar-icon-wrapper')).toHaveStyle({ position: 'absolute', right: 0, marginLeft: 16, }); - update( + await rerender( { /> ); - expect(getByTestId('search-bar-icon-wrapper')).toHaveStyle({ - display: 'none', - }); + expect( + screen.getByTestId('search-bar-icon-wrapper', { + includeHiddenElements: true, + }) + ).toHaveStyle({ display: 'none' }); }); -it('renders trailering icon when mode is set to "bar"', () => { - const { getByTestId } = render( +it('renders trailering icon when mode is set to "bar"', async () => { + await render( { /> ); - expect(getByTestId('search-bar-trailering-icon')).toBeTruthy(); + expect(screen.getByTestId('search-bar-trailering-icon')).toBeOnTheScreen(); }); -it('renders trailering icon with press functionality', () => { +it('renders trailering icon with press functionality', async () => { const onTraileringIconPressMock = jest.fn(); - const { getByTestId } = render( + await render( { /> ); - fireEvent(getByTestId('search-bar-trailering-icon'), 'onPress'); + await userEvent.press(screen.getByTestId('search-bar-trailering-icon')); expect(onTraileringIconPressMock).toHaveBeenCalledTimes(1); }); -it('renders clear icon instead of trailering icon', () => { - const { getByTestId, update, queryByTestId } = render( +it('renders clear icon instead of trailering icon', async () => { + const { rerender } = await render( { /> ); - expect(getByTestId('search-bar-trailering-icon')).toBeTruthy(); + expect(screen.getByTestId('search-bar-trailering-icon')).toBeOnTheScreen(); - update( + await rerender( { /> ); - expect(queryByTestId('search-bar-trailering-icon')).toBeNull(); - expect(getByTestId('search-bar-icon-wrapper')).toBeTruthy(); + expect( + screen.queryByTestId('search-bar-trailering-icon') + ).not.toBeOnTheScreen(); + expect(screen.getByTestId('search-bar-icon-wrapper')).toBeOnTheScreen(); }); -it('renders searchbar in "view" mode', () => { - const { getByTestId } = render( - - ); +it('renders searchbar in "view" mode', async () => { + await render(); - expect(getByTestId('search-bar-container')).toHaveStyle({ borderRadius: 0 }); + expect(screen.getByTestId('search-bar-container')).toHaveStyle({ + borderRadius: 0, + }); }); diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 144f12992e..ce950f671f 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -1,7 +1,7 @@ import { describe, expect, it, jest } from '@jest/globals'; import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import { tokens } from '../../theme/tokens'; import SegmentedButtons from '../SegmentedButtons/SegmentedButtons'; import { @@ -11,25 +11,29 @@ import { const stateOpacity = tokens.md.sys.state.opacity; -it('renders segmented button', () => { - const tree = render( - {}} - value={'walk'} - buttons={[{ value: 'walk' }, { value: 'ride' }]} - /> +it('renders segmented button', async () => { + const tree = ( + await render( + {}} + value={'walk'} + buttons={[{ value: 'walk' }, { value: 'ride' }]} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); it('renders disabled segmented button', async () => { - const tree = render( - {}} - value={'walk'} - buttons={[{ value: 'walk' }, { value: 'ride', disabled: true }]} - /> + const tree = ( + await render( + {}} + value={'walk'} + buttons={[{ value: 'walk' }, { value: 'ride', disabled: true }]} + /> + ) ).toJSON(); process.nextTick(() => { @@ -38,15 +42,17 @@ it('renders disabled segmented button', async () => { }); it('renders checked segmented button with selected check', async () => { - const tree = render( - {}} - value={'walk'} - buttons={[ - { value: 'walk', showSelectedCheck: true }, - { value: 'ride', disabled: true }, - ]} - /> + const tree = ( + await render( + {}} + value={'walk'} + buttons={[ + { value: 'walk', showSelectedCheck: true }, + { value: 'ride', disabled: true }, + ]} + /> + ) ).toJSON(); process.nextTick(() => { @@ -266,8 +272,8 @@ describe('getDisabledSegmentedButtonBorderWidth', () => { }); describe('should render icon when', () => { - it('icon prop is passed', () => { - const { getByTestId } = render( + it('icon prop is passed', async () => { + await render( { /> ); - expect(getByTestId('walking-button-icon')).toBeTruthy(); - expect(getByTestId('driving-button-icon')).toBeTruthy(); + expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); + expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); }); - it('icon prop is passed along with label, no matter if button is checked', () => { - const { getByTestId } = render( + it('icon prop is passed along with label, no matter if button is checked', async () => { + await render( { /> ); - expect(getByTestId('walking-button-icon')).toBeTruthy(); - expect(getByTestId('driving-button-icon')).toBeTruthy(); + expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); + expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); }); - it('icon prop is passed along with label, button is checked, showSelectedCheck is false', () => { - const { getByTestId } = render( + it('icon prop is passed along with label, button is checked, showSelectedCheck is false', async () => { + await render( { /> ); - expect(getByTestId('walking-button-icon')).toBeTruthy(); - expect(getByTestId('driving-button-icon')).toBeTruthy(); + expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); + expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); }); }); describe('should not render icon when', () => { - it('icon prop is not passed', () => { - const { queryByTestId } = render( + it('icon prop is not passed', async () => { + await render( { /> ); - expect(queryByTestId('walking-button-icon')).toBeNull(); - expect(queryByTestId('driving-button-icon')).toBeNull(); + expect(screen.queryByTestId('walking-button-icon')).not.toBeOnTheScreen(); + expect(screen.queryByTestId('driving-button-icon')).not.toBeOnTheScreen(); }); - it('icon prop is passed along with label, button is checked, showSelectedCheck is true', () => { - const { getByTestId, queryByTestId } = render( + it('icon prop is passed along with label, button is checked, showSelectedCheck is true', async () => { + await render( { /> ); - expect(queryByTestId('walking-button-icon')).toBeNull(); - expect(getByTestId('walking-button-check-icon')).toBeTruthy(); - expect(getByTestId('driving-button-icon')).toBeTruthy(); + expect(screen.queryByTestId('walking-button-icon')).not.toBeOnTheScreen(); + expect(screen.getByTestId('walking-button-check-icon')).toBeOnTheScreen(); + expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); }); }); describe('should have `accessibilityState={ checked: true }` when selected', () => { - it('should have two button selected', () => { + it('should have two button selected', async () => { const onValueChange = jest.fn(); - const { getAllByA11yState } = render( + await render( multiSelect value={['walk', 'transit']} @@ -414,14 +420,26 @@ describe('should have `accessibilityState={ checked: true }` when selected', () /> ); - const checkedButtons = getAllByA11yState({ checked: true }); - expect(checkedButtons).toHaveLength(2); + const buttons = screen.getAllByRole('button'); + + expect(buttons[0]).toHaveProp( + 'accessibilityState', + expect.objectContaining({ checked: true }) + ); + expect(buttons[1]).toHaveProp( + 'accessibilityState', + expect.objectContaining({ checked: true }) + ); + expect(buttons[2]).toHaveProp( + 'accessibilityState', + expect.objectContaining({ checked: false }) + ); }); - it('show selected check icon should be shown', () => { + it('show selected check icon should be shown', async () => { const onValueChange = jest.fn(); - const { getByTestId } = render( + await render( multiSelect value={['walk', 'transit']} @@ -439,13 +457,13 @@ describe('should have `accessibilityState={ checked: true }` when selected', () /> ); - expect(getByTestId('walking-check-icon')).toBeDefined(); + expect(screen.getByTestId('walking-check-icon')).toBeOnTheScreen(); }); }); describe('labelStyle is handled', () => { - it('when labelStyle is given', () => { - const { getByTestId } = render( + it('when labelStyle is given', async () => { + await render( { /> ); - expect(getByTestId('walking-button-label')).toHaveStyle({ fontSize: 10 }); - expect(getByTestId('driving-button-label')).toHaveStyle({ fontSize: 12 }); + expect(screen.getByTestId('walking-button-label')).toHaveStyle({ + fontSize: 10, + }); + expect(screen.getByTestId('driving-button-label')).toHaveStyle({ + fontSize: 12, + }); }); - it('when labelStyle is omitted', () => { - const { getByTestId } = render( + it('when labelStyle is omitted', async () => { + await render( { /> ); - expect(getByTestId('walking-button-label')).toHaveStyle({ fontSize: 14 }); + expect(screen.getByTestId('walking-button-label')).toHaveStyle({ + fontSize: 14, + }); }); }); diff --git a/src/components/__tests__/Snackbar.test.tsx b/src/components/__tests__/Snackbar.test.tsx index 129db058e5..16b118da09 100644 --- a/src/components/__tests__/Snackbar.test.tsx +++ b/src/components/__tests__/Snackbar.test.tsx @@ -3,7 +3,7 @@ import { Animated, StyleSheet, Text, View } from 'react-native'; import { expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import { red200, white } from '../../theme/colors'; import Snackbar from '../Snackbar'; @@ -23,69 +23,79 @@ jest.mock('react-native-safe-area-context', () => ({ useSafeAreaInsets: () => ({ bottom: 34, left: 0, right: 0, top: 47 }), })); -it('renders snackbar with content', () => { - const tree = render( - - Snackbar content - +it('renders snackbar with content', async () => { + const tree = ( + await render( + + Snackbar content + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders not visible snackbar with content wrapper but no actual content', () => { - const tree = render( - - Snackbar content - +it('renders not visible snackbar with content wrapper but no actual content', async () => { + const tree = ( + await render( + + Snackbar content + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders snackbar with Text as a child', () => { - const tree = render( - - Snackbar content - +it('renders snackbar with Text as a child', async () => { + const tree = ( + await render( + + Snackbar content + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders snackbar with action button', () => { - const tree = render( - {}} - action={{ label: 'Undo', onPress: jest.fn() }} - > - Snackbar content - +it('renders snackbar with action button', async () => { + const tree = ( + await render( + {}} + action={{ label: 'Undo', onPress: jest.fn() }} + > + Snackbar content + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders snackbar with View & Text as a child', () => { - const tree = render( - - - - - Error Message which is veryyyyyyyyyyyy longggggggg Error Message which - is veryyyyyyyyyyyy longggggggg - - - +it('renders snackbar with View & Text as a child', async () => { + const tree = ( + await render( + + + + + Error Message which is veryyyyyyyyyyyy longggggggg Error Message + which is veryyyyyyyyyyyy longggggggg + + + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('animated value changes correctly', () => { +it('animated value changes correctly', async () => { const value = new Animated.Value(1); - const { getByTestId } = render( + await render( { Snackbar content ); - expect(getByTestId('snack-bar-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('snack-bar-outer-layer')).toHaveStyle({ transform: [{ scale: 1 }], }); @@ -105,10 +115,10 @@ it('animated value changes correctly', () => { duration: 200, }).start(); - act(() => { + await act(() => { jest.advanceTimersByTime(200); }); - expect(getByTestId('snack-bar-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('snack-bar-outer-layer')).toHaveStyle({ transform: [{ scale: 1.5 }], }); }); diff --git a/src/components/__tests__/Surface.test.tsx b/src/components/__tests__/Surface.test.tsx index 879f5b48ee..1a9ebc8a45 100644 --- a/src/components/__tests__/Surface.test.tsx +++ b/src/components/__tests__/Surface.test.tsx @@ -5,7 +5,7 @@ import { Platform } from 'react-native'; import { describe, expect, it } from '@jest/globals'; import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import Surface from '../Surface'; type StyleCase = { @@ -14,14 +14,15 @@ type StyleCase = { }; describe('Surface', () => { - it('should properly render passed props', () => { + it('should properly render passed props', async () => { const testID = 'surface-container'; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId(testID).props.pointerEvents).toBe('box-none'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId(testID).props.pointerEvents).toBe('box-none'); }); describe('on iOS', () => { @@ -46,8 +47,8 @@ describe('Surface', () => { }, }); - it('should render Surface with appropriate bg color but without shadow, if mode is set to "flat"', () => { - const { getByTestId } = render( + it('should render Surface with appropriate bg color but without shadow, if mode is set to "flat"', async () => { + await render( { ); - expect(getByTestId('surface-test')).not.toHaveStyle({ + expect(screen.getByTestId('surface-test')).not.toHaveStyle({ shadowColor: '#000', shadowOpacity: 0.3, shadowOffset: { width: 0, height: 4 }, shadowRadius: 4, }); - expect(getByTestId('surface-test-outer-layer')).not.toHaveStyle({ + expect(screen.getByTestId('surface-test-outer-layer')).not.toHaveStyle({ shadowColor: '#000', shadowOpacity: 0.15, shadowOffset: { width: 0, height: 8 }, shadowRadius: 12, }); - expect(getByTestId('surface-test')).toHaveStyle({ + expect(screen.getByTestId('surface-test')).toHaveStyle({ backgroundColor: getTheme().colors.surfaceContainerHighest, }); }); @@ -98,17 +99,19 @@ describe('Surface', () => { { property: 'flex', value: 6 }, ] satisfies StyleCase[])( 'applies $property to outer layer only', - ({ property, value }) => { + async ({ property, value }) => { const style = { [property]: value }; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId('surface-test-outer-layer')).toHaveStyle(style); - expect(getByTestId('surface-test')).not.toHaveStyle(style); + expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( + style + ); + expect(screen.getByTestId('surface-test')).not.toHaveStyle(style); } ); @@ -124,17 +127,19 @@ describe('Surface', () => { { property: 'borderColor', value: 'black' }, ] satisfies StyleCase[])( 'applies $property to inner layer only', - ({ property, value }) => { + async ({ property, value }) => { const style = { [property]: value }; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId('surface-test-outer-layer')).not.toHaveStyle(style); - expect(getByTestId('surface-test')).toHaveStyle(style); + expect(screen.getByTestId('surface-test-outer-layer')).not.toHaveStyle( + style + ); + expect(screen.getByTestId('surface-test')).toHaveStyle(style); } ); @@ -147,81 +152,85 @@ describe('Surface', () => { { property: 'backgroundColor', value: 'rgb(4, 5, 6)' }, ] satisfies StyleCase[])( 'applies $property to every layer', - ({ property, value }) => { + async ({ property, value }) => { const style = { [property]: value }; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId('surface-test-outer-layer')).toHaveStyle(style); - expect(getByTestId('surface-test')).toHaveStyle(style); + expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( + style + ); + expect(screen.getByTestId('surface-test')).toHaveStyle(style); } ); describe('outer layer', () => { - it('should not render rest style', () => { + it('should not render rest style', async () => { const testID = 'surface-test'; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId(`${testID}-outer-layer`)).not.toHaveStyle( + expect(screen.getByTestId(`${testID}-outer-layer`)).not.toHaveStyle( styles.restStyle ); }); - it('should render absolute position properties on outer layer', () => { + it('should render absolute position properties on outer layer', async () => { const testID = 'surface-test'; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId(`${testID}-outer-layer`)).toHaveStyle( + expect(screen.getByTestId(`${testID}-outer-layer`)).toHaveStyle( styles.absoluteStyles ); }); - it('should render absolute position properties on the outer layer', () => { + it('should render absolute position properties on the outer layer', async () => { const testID = 'surface-test'; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId(`${testID}-outer-layer`)).toHaveStyle( + expect(screen.getByTestId(`${testID}-outer-layer`)).toHaveStyle( styles.absoluteStyles ); }); }); describe('inner layer', () => { - it('should render inner layer styles on the inner layer', () => { + it('should render inner layer styles on the inner layer', async () => { const testID = 'surface-test'; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId(testID)).toHaveStyle(styles.innerLayerViewStyle); + expect(screen.getByTestId(testID)).toHaveStyle( + styles.innerLayerViewStyle + ); }); }); - it('applies backgroundColor to every layer', () => { + it('applies backgroundColor to every layer', async () => { const backgroundColor = 'rgb(1, 2, 3)'; - const { getByTestId } = render( + await render( { ); const style = { backgroundColor }; - expect(getByTestId('surface-test-outer-layer')).toHaveStyle(style); - expect(getByTestId('surface-test')).toHaveStyle(style); + expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle(style); + expect(screen.getByTestId('surface-test')).toHaveStyle(style); }); describe('children wrapper', () => { - it('should render rest styles', () => { + it('should render rest styles', async () => { const testID = 'surface-test'; const combinedStyles = [styles.innerLayerViewStyle, styles.restStyle]; - const { getByTestId } = render( + await render( {null} ); - expect(getByTestId(testID)).toHaveStyle(combinedStyles); + expect(screen.getByTestId(testID)).toHaveStyle(combinedStyles); }); }); }); describe('on Android', () => { - it('should render Surface with appropriate bg color but without shadow, if mode is set to "flat"', () => { + it('should render Surface with appropriate bg color but without shadow, if mode is set to "flat"', async () => { Platform.OS = 'android'; const testID = 'surface-container'; - const { getByTestId } = render( + await render( { ); - expect(getByTestId(testID)).not.toHaveStyle({ elevation: 5 }); - expect(getByTestId(testID)).toHaveStyle({ + expect(screen.getByTestId(testID)).not.toHaveStyle({ elevation: 5 }); + expect(screen.getByTestId(testID)).toHaveStyle({ backgroundColor: getTheme().colors.surfaceContainerHighest, }); }); diff --git a/src/components/__tests__/Switch.test.tsx b/src/components/__tests__/Switch.test.tsx index bdc34ca48a..5e1f44f174 100644 --- a/src/components/__tests__/Switch.test.tsx +++ b/src/components/__tests__/Switch.test.tsx @@ -1,67 +1,65 @@ import { describe, expect, it, jest } from '@jest/globals'; -import { fireEvent, render } from '../../test-utils'; +import { render, screen, userEvent } from '../../test-utils'; import Switch from '../Switch/Switch'; describe('Switch render', () => { - it('renders on', () => { - expect(render().toJSON()).toMatchSnapshot(); + it('renders on', async () => { + expect((await render()).toJSON()).toMatchSnapshot(); }); - it('renders off', () => { - expect(render().toJSON()).toMatchSnapshot(); + it('renders off', async () => { + expect((await render()).toJSON()).toMatchSnapshot(); }); - it('renders disabled on', () => { - expect(render().toJSON()).toMatchSnapshot(); + it('renders disabled on', async () => { + expect( + (await render()).toJSON() + ).toMatchSnapshot(); }); - it('renders disabled off', () => { + it('renders disabled off', async () => { expect( - render().toJSON() + (await render()).toJSON() ).toMatchSnapshot(); }); - it('renders with checked icon', () => { + it('renders with checked icon', async () => { expect( - render().toJSON() + (await render()).toJSON() ).toMatchSnapshot(); }); - it('renders with per-state icons', () => { + it('renders with per-state icons', async () => { expect( - render( - + ( + await render() ).toJSON() ).toMatchSnapshot(); }); }); describe('Switch interaction', () => { - it('toggles to true when off and pressed', () => { + it('toggles to true when off and pressed', async () => { const onValueChange = jest.fn(); - const { getByRole } = render( - - ); - fireEvent.press(getByRole('switch')); + await render(); + await userEvent.press(screen.getByRole('switch')); expect(onValueChange).toHaveBeenCalledWith(true); }); - it('toggles to false when on and pressed', () => { + it('toggles to false when on and pressed', async () => { const onValueChange = jest.fn(); - const { getByRole } = render( - - ); - fireEvent.press(getByRole('switch')); + await render(); + await userEvent.press(screen.getByRole('switch')); expect(onValueChange).toHaveBeenCalledWith(false); }); - it('does not fire onValueChange when disabled', () => { + it('does not fire onValueChange when disabled', async () => { const onValueChange = jest.fn(); - const { getByRole } = render( + await render( ); - fireEvent.press(getByRole('switch')); + await userEvent.press(screen.getByRole('switch')); expect(onValueChange).not.toHaveBeenCalled(); }); }); diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index 14ded21b21..6a0fe19768 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -1,10 +1,5 @@ import * as React from 'react'; -import { - I18nManager, - StyleSheet, - TextInput as NativeTextInput, - View, -} from 'react-native'; +import { I18nManager, TextInput as NativeTextInput, View } from 'react-native'; import type { GestureResponderEvent } from 'react-native'; import { @@ -15,8 +10,9 @@ import { it, jest, } from '@jest/globals'; +import type { TestInstance } from 'test-renderer'; -import { act, fireEvent, render } from '../../test-utils'; +import { act, fireEvent, render, screen, userEvent } from '../../test-utils'; import { tokens } from '../../theme/tokens'; import TextInput from '../TextInput'; import type { @@ -28,6 +24,24 @@ import type { TextInputAccessoryProps } from '../TextInput/TextInputIcon'; const stateOpacity = tokens.md.sys.state.opacity; const defaultI18nIsRTL = I18nManager.isRTL; +const includeHiddenElements = { includeHiddenElements: true }; + +const getOuterTextInputPressable = (root: TestInstance | null) => { + const [pressable] = + // eslint-disable-next-line no-restricted-syntax -- TODO: replace non-accessible outer Pressable lookup with a public behavior assertion. + root?.queryAll( + (instance) => + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + instance.props.role === 'none' && instance.props.accessible === false, + { includeSelf: true } + ) ?? []; + + if (!pressable) { + throw new Error('Expected outer TextInput Pressable'); + } + + return pressable; +}; const getConstantsOriginal = I18nManager.getConstants.bind(I18nManager); @@ -57,106 +71,118 @@ function firstIndexOfTextChildArrayInTree(tree: unknown, text: string): number { return JSON.stringify(tree).indexOf(JSON.stringify([text])); } -it('renders filled TextInput with label and value', () => { - const tree = render( - {}} /> +it('renders filled TextInput with label and value', async () => { + const tree = ( + await render( + {}} /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders outlined TextInput with label and value', () => { - const tree = render( - {}} - /> +it('renders outlined TextInput with label and value', async () => { + const tree = ( + await render( + {}} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders filled TextInput with TextInput.Icon accessories', () => { - const tree = render( - {}} - startAccessory={(props: TextInputAccessoryProps) => ( - - )} - endAccessory={(props: TextInputAccessoryProps) => ( - - )} - /> +it('renders filled TextInput with TextInput.Icon accessories', async () => { + const tree = ( + await render( + {}} + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + + )} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders outlined TextInput with TextInput.Icon accessories', () => { - const tree = render( - {}} - startAccessory={(props: TextInputAccessoryProps) => ( - - )} - endAccessory={(props: TextInputAccessoryProps) => ( - - )} - /> +it('renders outlined TextInput with TextInput.Icon accessories', async () => { + const tree = ( + await render( + {}} + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + + )} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders filled TextInput with TextInput.Icon accessories when error is true', () => { - const tree = render( - {}} - error - startAccessory={(props: TextInputAccessoryProps) => ( - - )} - endAccessory={(props: TextInputAccessoryProps) => ( - {}} /> - )} - /> +it('renders filled TextInput with TextInput.Icon accessories when error is true', async () => { + const tree = ( + await render( + {}} + error + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders outlined TextInput with TextInput.Icon accessories when error is true', () => { - const tree = render( - {}} - error - startAccessory={(props: TextInputAccessoryProps) => ( - - )} - endAccessory={(props: TextInputAccessoryProps) => ( - {}} /> - )} - /> +it('renders outlined TextInput with TextInput.Icon accessories when error is true', async () => { + const tree = ( + await render( + {}} + error + startAccessory={(props: TextInputAccessoryProps) => ( + + )} + endAccessory={(props: TextInputAccessoryProps) => ( + {}} /> + )} + /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('fires onPress on TextInput.Icon end accessory', () => { +it('fires onPress on TextInput.Icon end accessory', async () => { const onClear = jest.fn<(event: GestureResponderEvent) => void>(); - const { getAllByTestId } = render( + await render( { /> ); - fireEvent.press(getAllByTestId('icon-button')[1]); + await userEvent.press(screen.getAllByTestId('icon-button')[1]); expect(onClear).toHaveBeenCalledTimes(1); }); -it('disables TextInput.Icon when the field is disabled', () => { - const { getAllByTestId } = render( +it('disables TextInput.Icon when the field is disabled', async () => { + await render( { /> ); - const buttons = getAllByTestId('icon-button'); + const buttons = screen.getAllByTestId('icon-button'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(buttons[0].props.accessibilityState?.disabled).toBe(true); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(buttons[1].props.accessibilityState?.disabled).toBe(true); }); -it('does not disable TextInput.Icon when the field is read-only (editable false)', () => { - const { getAllByTestId } = render( +it('does not disable TextInput.Icon when the field is read-only (editable false)', async () => { + await render( ); - const buttons = getAllByTestId('icon-button'); + const buttons = screen.getAllByTestId('icon-button'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(buttons[0].props.accessibilityState?.disabled).not.toBe(true); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(buttons[1].props.accessibilityState?.disabled).not.toBe(true); }); -it('renders supporting text below the field', () => { - const { getByText } = render( +it('renders supporting text below the field', async () => { + await render( { /> ); - expect(getByText('Use a valid address')).toBeTruthy(); + expect( + screen.getByText('Use a valid address', includeHiddenElements) + ).toBeOnTheScreen(); }); -it('uses polite aria-live on error supporting text', () => { - const { getByText, getByTestId } = render( +it('uses polite aria-live on error supporting text', async () => { + await render( { /> ); - expect(getByText('Invalid').props['aria-live']).toBe('polite'); - expect(getByTestId('tf-input').props.accessibilityState?.invalid).toBe(true); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByText('Invalid').props['aria-live']).toBe('polite'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-input').props.accessibilityState?.invalid).toBe( + true + ); }); -it('marks the input invalid when error is true without supporting text', () => { - const { getByTestId } = render( +it('marks the input invalid when error is true without supporting text', async () => { + await render( { /> ); - expect(getByTestId('tf-input').props.accessibilityState?.invalid).toBe(true); - expect(getByTestId('tf-input').props.accessibilityHint).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-input').props.accessibilityState?.invalid).toBe( + true + ); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('tf-input').props.accessibilityHint + ).toBeUndefined(); }); -it('hides helper supporting text from the accessibility tree and omits aria-live', () => { - const { getByText, getByTestId } = render( +it('hides helper supporting text from the accessibility tree and omits aria-live', async () => { + await render( ); - expect(getByText('Optional').props['aria-hidden']).toBe(true); - expect(getByText('Optional').props['aria-live']).toBeUndefined(); - expect(getByTestId('tf-input').props['aria-label']).toBe('Email, Optional'); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByText('Optional', includeHiddenElements).props['aria-hidden'] + ).toBe(true); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByText('Optional', includeHiddenElements).props['aria-live'] + ).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-input').props['aria-label']).toBe( + 'Email, Optional' + ); }); -it('includes supporting text in aria-label when label is omitted', () => { - const { getByTestId } = render( +it('includes supporting text in aria-label when label is omitted', async () => { + await render( {}} @@ -292,11 +343,14 @@ it('includes supporting text in aria-label when label is omitted', () => { /> ); - expect(getByTestId('tf-input').props['aria-label']).toBe('Helper only'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-input').props['aria-label']).toBe( + 'Helper only' + ); }); -it('does not mark the input as aria-disabled when editable is false (read-only)', () => { - const { getByTestId } = render( +it('does not mark the input as aria-disabled when editable is false (read-only)', async () => { + await render( ); - expect(getByTestId('tf-input').props.accessibilityState?.disabled).not.toBe( - true - ); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('tf-input').props.accessibilityState?.disabled + ).not.toBe(true); }); -it('marks the input as disabled in accessibilityState when disabled is true', () => { - const { getByTestId } = render( +it('marks the input as disabled in accessibilityState when disabled is true', async () => { + await render( ); - expect(getByTestId('tf-input').props.accessibilityState?.disabled).toBe(true); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('tf-input').props.accessibilityState?.disabled + ).toBe(true); }); -it('renders the input via render with merged props', () => { +it('renders the input via render with merged props', async () => { const renderInput = jest.fn((props: TextInputRenderProps) => ( )); - const { getByTestId } = render( + await render( { /> ); - expect(getByTestId('custom-input')).toBeTruthy(); + expect(screen.getByTestId('custom-input')).toBeOnTheScreen(); expect(renderInput).toHaveBeenCalled(); const merged = renderInput.mock.calls[0]?.[0] as TextInputRenderProps; expect(merged['aria-label']).toBe('Pin'); expect(merged.value).toBe('12'); }); -it('does not apply disabled opacity to the TextInput when editable is false (filled)', () => { - const { getByTestId } = render( +it('does not apply disabled opacity to the TextInput when editable is false (filled)', async () => { + await render( ); - expect( - StyleSheet.flatten(getByTestId('tf-input-ro').props.style) - ).not.toMatchObject({ opacity: stateOpacity.disabled }); + expect(screen.getByTestId('tf-input-ro')).not.toHaveStyle({ + opacity: stateOpacity.disabled, + }); }); -it('does not apply disabled opacity to the TextInput when editable is false (outlined)', () => { - const { getByTestId } = render( +it('does not apply disabled opacity to the TextInput when editable is false (outlined)', async () => { + await render( ); - expect( - StyleSheet.flatten(getByTestId('tf-input-ro-out').props.style) - ).not.toMatchObject({ opacity: stateOpacity.disabled }); + expect(screen.getByTestId('tf-input-ro-out')).not.toHaveStyle({ + opacity: stateOpacity.disabled, + }); }); -it('applies disabled opacity to the TextInput when disabled is true (filled)', () => { - const { getByTestId } = render( +it('applies disabled opacity to the TextInput when disabled is true (filled)', async () => { + await render( ); - expect( - StyleSheet.flatten(getByTestId('tf-input-dis').props.style) - ).toMatchObject({ opacity: stateOpacity.disabled }); + expect(screen.getByTestId('tf-input-dis')).toHaveStyle({ + opacity: stateOpacity.disabled, + }); }); -it('applies disabled opacity to the TextInput when disabled is true (outlined)', () => { - const { getByTestId } = render( +it('applies disabled opacity to the TextInput when disabled is true (outlined)', async () => { + await render( ); - expect( - StyleSheet.flatten(getByTestId('tf-input-dis-out').props.style) - ).toMatchObject({ opacity: stateOpacity.disabled }); + expect(screen.getByTestId('tf-input-dis-out')).toHaveStyle({ + opacity: stateOpacity.disabled, + }); }); -it('forwards TextInput props such as testID', () => { - const { getByTestId } = render( +it('forwards TextInput props such as testID', async () => { + await render( { /> ); - expect(getByTestId('email-input')).toBeTruthy(); + expect(screen.getByTestId('email-input')).toBeOnTheScreen(); }); /* TextInput peels these before spreading onto TextInput (see TextInput.tsx). * Custom layout / sub-component styling props are intentionally not supported. */ -it('does not pass TextInput-only props through to TextInput', () => { - const { getByTestId } = render( +it('does not pass TextInput-only props through to TextInput', async () => { + await render( { /> ); - const input = getByTestId('tf-native'); + const input = screen.getByTestId('tf-native'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.variant).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.theme).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.startAccessory).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.endAccessory).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.label).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.supportingText).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.prefix).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.suffix).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.counter).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.error).toBeUndefined(); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.disabled).toBeUndefined(); }); -it('shows a character counter when counter is true and maxLength is set (filled)', () => { - const { getByText, queryByText } = render( +it('shows a character counter when counter is true and maxLength is set (filled)', async () => { + await render( ); - expect(getByText('5/100')).toBeTruthy(); - expect(queryByText('0/100')).toBeNull(); + expect(screen.getByText('5/100')).toBeOnTheScreen(); + expect(screen.queryByText('0/100')).not.toBeOnTheScreen(); }); -it('shows a character counter when counter is true and maxLength is set (outlined)', () => { - const { getByText } = render( +it('shows a character counter when counter is true and maxLength is set (outlined)', async () => { + await render( ); - expect(getByText('0/50')).toBeTruthy(); + expect(screen.getByText('0/50')).toBeOnTheScreen(); }); -it('updates the character counter when the value changes', () => { - const { getByText, rerender } = render( +it('updates the character counter when the value changes', async () => { + const { rerender } = await render( { /> ); - expect(getByText('1/10')).toBeTruthy(); + expect(screen.getByText('1/10')).toBeOnTheScreen(); - rerender( + await rerender( { /> ); - expect(getByText('4/10')).toBeTruthy(); + expect(screen.getByText('4/10')).toBeOnTheScreen(); }); -it('does not show a character counter when counter is false', () => { - const { queryByText } = render( +it('does not show a character counter when counter is false', async () => { + await render( { /> ); - expect(queryByText('5/100')).toBeNull(); + expect(screen.queryByText('5/100')).not.toBeOnTheScreen(); }); -it('does not show a character counter when maxLength is missing', () => { - const { queryByText } = render( +it('does not show a character counter when maxLength is missing', async () => { + await render( {}} counter /> ); - expect(queryByText('5/100')).toBeNull(); - expect(queryByText(/\//)).toBeNull(); + expect(screen.queryByText('5/100')).not.toBeOnTheScreen(); + expect(screen.queryByText(/\//)).not.toBeOnTheScreen(); }); -it('invokes onFocus and onBlur on the TextInput', () => { +it('invokes onFocus and onBlur on the TextInput', async () => { const onFocus = jest.fn(); const onBlur = jest.fn(); - const { getByTestId } = render( + await render( { /> ); - const input = getByTestId('tf-input'); - fireEvent(input, 'focus'); - fireEvent(input, 'blur'); + const input = screen.getByTestId('tf-input'); + await fireEvent(input, 'focus'); + await fireEvent(input, 'blur'); expect(onFocus).toHaveBeenCalledTimes(1); expect(onBlur).toHaveBeenCalledTimes(1); }); -it('focuses the TextInput when the outer Pressable is pressed', () => { +it('focuses the TextInput when the outer Pressable is pressed', async () => { const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); - const { UNSAFE_getByProps, getByTestId } = render( + const { root } = await render( { /> ); - expect(getByTestId('tf-input')).toBeTruthy(); + expect(screen.getByTestId('tf-input')).toBeOnTheScreen(); - /* Pressable is not exposed as a distinct type in the test renderer; match its props. */ - const pressable = UNSAFE_getByProps({ role: 'none', accessible: false }); - fireEvent.press(pressable); + await userEvent.press(getOuterTextInputPressable(root)); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); }); -it('does not focus the TextInput when disabled and the Pressable is pressed', () => { +it('does not focus the TextInput when disabled and the Pressable is pressed', async () => { const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); - const { UNSAFE_getByProps } = render( + const { root } = await render( {}} disabled /> ); - const pressable = UNSAFE_getByProps({ role: 'none', accessible: false }); - fireEvent.press(pressable); + await userEvent.press(getOuterTextInputPressable(root)); expect(focusSpy).not.toHaveBeenCalled(); focusSpy.mockRestore(); }); -it('focuses the TextInput when read-only and the Pressable is pressed', () => { +it('focuses the TextInput when read-only and the Pressable is pressed', async () => { const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); - const { UNSAFE_getByProps } = render( + const { root } = await render( { /> ); - const pressable = UNSAFE_getByProps({ role: 'none', accessible: false }); - fireEvent.press(pressable); + await userEvent.press(getOuterTextInputPressable(root)); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); }); -it('exposes the TextInput instance via ref prop', () => { +it('exposes the TextInput instance via ref prop', async () => { const ref = React.createRef(); - render( + await render( { expect(typeof ref.current?.setSelection).toBe('function'); }); -it('passes error, disabled, and multiline to accessories', () => { +it('passes error, disabled, and multiline to accessories', async () => { const startAccessoryProps: TextInputAccessoryProps[] = []; const endAccessoryProps: TextInputAccessoryProps[] = []; @@ -645,7 +710,7 @@ it('passes error, disabled, and multiline to accessories', () => { return ; } - const { getByTestId } = render( + await render( { /> ); - expect(getByTestId('start-accessory')).toBeTruthy(); - expect(getByTestId('end-accessory')).toBeTruthy(); + expect(screen.getByTestId('start-accessory')).toBeOnTheScreen(); + expect(screen.getByTestId('end-accessory')).toBeOnTheScreen(); expect(startAccessoryProps[0]).toMatchObject({ error: true, disabled: true, @@ -672,7 +737,7 @@ it('passes error, disabled, and multiline to accessories', () => { }); }); -it('passes error to accessories when the field is disabled', () => { +it('passes error to accessories when the field is disabled', async () => { const startAccessoryProps: TextInputAccessoryProps[] = []; function StartAccessory(props: TextInputAccessoryProps) { @@ -680,7 +745,7 @@ it('passes error to accessories when the field is disabled', () => { return ; } - const { getByTestId } = render( + await render( { /> ); - expect(getByTestId('start-acc-error-disabled')).toBeTruthy(); + expect(screen.getByTestId('start-acc-error-disabled')).toBeOnTheScreen(); expect(startAccessoryProps[0].error).toBe(true); expect(startAccessoryProps[0].disabled).toBe(true); }); -it('renders supporting text as a Text child', () => { - const { getByText } = render( +it('renders supporting text as a Text child', async () => { + await render( { /> ); - expect(getByText('Hint')).toBeTruthy(); + expect(screen.getByText('Hint', includeHiddenElements)).toBeOnTheScreen(); }); -it('renders the counter as a Text child', () => { - const { getByText } = render( +it('renders the counter as a Text child', async () => { + await render( { /> ); - expect(getByText('2/80')).toBeTruthy(); + expect(screen.getByText('2/80')).toBeOnTheScreen(); }); -it('renders supporting text and counter separately when both are shown', () => { - const { getByText } = render( +it('renders supporting text and counter separately when both are shown', async () => { + await render( { /> ); - expect(getByText('Help text')).toBeTruthy(); - expect(getByText('1/10')).toBeTruthy(); + expect( + screen.getByText('Help text', includeHiddenElements) + ).toBeOnTheScreen(); + expect(screen.getByText('1/10')).toBeOnTheScreen(); }); -it('applies RTL text alignment and writing direction to the TextInput (filled)', () => { +it('applies RTL text alignment and writing direction to the TextInput (filled)', async () => { I18nManager.isRTL = true; - const { getByTestId } = render( + await render( ); - expect(StyleSheet.flatten(getByTestId('tf-input-rtl').props.style)).toEqual( - expect.objectContaining({ - textAlign: 'right', - writingDirection: 'rtl', - }) - ); + expect(screen.getByTestId('tf-input-rtl')).toHaveStyle({ + textAlign: 'right', + writingDirection: 'rtl', + }); }); -it('applies RTL text alignment and writing direction to the TextInput (outlined)', () => { +it('applies RTL text alignment and writing direction to the TextInput (outlined)', async () => { I18nManager.isRTL = true; - const { getByTestId } = render( + await render( ); - expect( - StyleSheet.flatten(getByTestId('tf-input-rtl-outlined').props.style) - ).toEqual( - expect.objectContaining({ - textAlign: 'right', - writingDirection: 'rtl', - }) - ); + expect(screen.getByTestId('tf-input-rtl-outlined')).toHaveStyle({ + textAlign: 'right', + writingDirection: 'rtl', + }); }); -it('applies RTL writing direction to supporting text', () => { +it('applies RTL writing direction to supporting text', async () => { I18nManager.isRTL = true; - const { getByText } = render( + await render( { /> ); - expect(StyleSheet.flatten(getByText('Hint').props.style)).toEqual( - expect.objectContaining({ - writingDirection: 'rtl', - }) - ); + expect(screen.getByText('Hint', includeHiddenElements)).toHaveStyle({ + writingDirection: 'rtl', + }); }); -it('places EndAccessory before StartAccessory in the tree when RTL', () => { +it('places EndAccessory before StartAccessory in the tree when RTL', async () => { I18nManager.isRTL = true; function StartAccessory() { @@ -812,7 +871,7 @@ it('places EndAccessory before StartAccessory in the tree when RTL', () => { return ; } - const { toJSON } = render( + const { toJSON } = await render( { ); }); -it('places StartAccessory before EndAccessory in the tree when LTR', () => { +it('places StartAccessory before EndAccessory in the tree when LTR', async () => { I18nManager.isRTL = false; function StartAccessory() { @@ -840,7 +899,7 @@ it('places StartAccessory before EndAccessory in the tree when LTR', () => { return ; } - const { toJSON } = render( + const { toJSON } = await render( { ).toBeLessThan(firstIndexOfTestIdInTree(tree, 'ltr-acc-from-end-prop')); }); -it('does not expose the placeholder string when the TextInput is not focused', () => { - const { getByTestId } = render( +it('does not expose the placeholder string when the TextInput is not focused', async () => { + await render( { - const { getByTestId } = render( +it('shows placeholder when unfocused and no label is given', async () => { + await render( {}} @@ -882,11 +942,14 @@ it('shows placeholder when unfocused and no label is given', () => { /> ); - expect(getByTestId('tf-input-no-label').props.placeholder).toBe('Search'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-input-no-label').props.placeholder).toBe( + 'Search' + ); }); -it('shows placeholder when the TextInput is focused', () => { - const { getByTestId } = render( +it('shows placeholder when the TextInput is focused', async () => { + await render( { /> ); - fireEvent(getByTestId('tf-input'), 'focus'); + await fireEvent(screen.getByTestId('tf-input'), 'focus'); - expect(getByTestId('tf-input').props.placeholder).toBe( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-input').props.placeholder).toBe( 'e.g. user@example.com' ); }); -it('shows placeholder on multiline TextInput when focused', () => { - const { getByTestId } = render( +it('shows placeholder on multiline TextInput when focused', async () => { + await render( { /> ); - expect(getByTestId('tf-multiline').props.placeholder).toBe(' '); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-multiline').props.placeholder).toBe(' '); - fireEvent(getByTestId('tf-multiline'), 'focus'); + await fireEvent(screen.getByTestId('tf-multiline'), 'focus'); - expect(getByTestId('tf-multiline').props.placeholder).toBe('Add a note…'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-multiline').props.placeholder).toBe( + 'Add a note…' + ); }); -it('does not expose the placeholder string again after the TextInput loses focus', () => { - const { getByTestId } = render( +it('does not expose the placeholder string again after the TextInput loses focus', async () => { + await render( ); - fireEvent(getByTestId('tf-input'), 'focus'); - fireEvent(getByTestId('tf-input'), 'blur'); + await fireEvent(screen.getByTestId('tf-input'), 'focus'); + await fireEvent(screen.getByTestId('tf-input'), 'blur'); - expect(getByTestId('tf-input').props.placeholder).toBe(' '); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('tf-input').props.placeholder).toBe(' '); }); -it('maps a lone StartAccessory to leading in LTR and trailing in RTL (tree order)', () => { +it('maps a lone StartAccessory to leading in LTR and trailing in RTL (tree order)', async () => { function LoneStartAccessory() { return ; } I18nManager.isRTL = false; - const { toJSON: toJsonLtr } = render( + const { toJSON: toJsonLtr } = await render( { - const { getByTestId, getByText, queryByText, rerender } = render( +it('shows prefix and suffix when the field is floating and hides them after value is cleared while blurred', async () => { + const { rerender } = await render( ); - expect(getByText('$')).toBeTruthy(); - expect(getByText('/100')).toBeTruthy(); + expect(screen.getByText('$')).toBeOnTheScreen(); + expect(screen.getByText('/100')).toBeOnTheScreen(); - rerender( + await rerender( ); - expect(queryByText('$')).toBeNull(); - expect(queryByText('/100')).toBeNull(); - expect(getByTestId('tf-ps')).toBeTruthy(); + expect(screen.queryByText('$')).not.toBeOnTheScreen(); + expect(screen.queryByText('/100')).not.toBeOnTheScreen(); + expect(screen.getByTestId('tf-ps')).toBeOnTheScreen(); }); -it('renders prefix and suffix while focused even when value is empty', () => { - const { getByTestId, getByText, queryByText } = render( +it('renders prefix and suffix while focused even when value is empty', async () => { + await render( { /> ); - expect(queryByText('$')).toBeNull(); - expect(queryByText(' kg')).toBeNull(); + expect(screen.queryByText('$')).not.toBeOnTheScreen(); + expect(screen.queryByText(' kg')).not.toBeOnTheScreen(); - fireEvent(getByTestId('tf-ps-focus'), 'focus'); + await fireEvent(screen.getByTestId('tf-ps-focus'), 'focus'); - expect(getByText('$')).toBeTruthy(); - expect(getByText(' kg')).toBeTruthy(); + expect(screen.getByText('$')).toBeOnTheScreen(); + expect(screen.getByText(' kg')).toBeOnTheScreen(); }); -it('places prefix Text before the TextInput and suffix Text after it', () => { - const { toJSON } = render( +it('places prefix Text before the TextInput and suffix Text after it', async () => { + const { toJSON } = await render( { ); }); -it('aligns input text toward the suffix when suffix is active (LTR)', () => { - const { getByTestId } = render( +it('aligns input text toward the suffix when suffix is active (LTR)', async () => { + await render( { /> ); - expect( - StyleSheet.flatten(getByTestId('tf-suffix-align-ltr').props.style) - ).toEqual( - expect.objectContaining({ - textAlign: 'right', - writingDirection: 'ltr', - }) - ); + expect(screen.getByTestId('tf-suffix-align-ltr')).toHaveStyle({ + textAlign: 'right', + writingDirection: 'ltr', + }); }); -it('aligns input text toward the suffix when suffix is active (RTL)', () => { +it('aligns input text toward the suffix when suffix is active (RTL)', async () => { I18nManager.isRTL = true; - const { getByTestId } = render( + await render( { /> ); - expect( - StyleSheet.flatten(getByTestId('tf-suffix-align-rtl').props.style) - ).toEqual( - expect.objectContaining({ - textAlign: 'left', - writingDirection: 'rtl', - }) - ); + expect(screen.getByTestId('tf-suffix-align-rtl')).toHaveStyle({ + textAlign: 'left', + writingDirection: 'rtl', + }); }); -it('uses default horizontal alignment when suffix prop exists but suffix is not shown yet (LTR)', () => { - const { getByTestId } = render( +it('uses default horizontal alignment when suffix prop exists but suffix is not shown yet (LTR)', async () => { + await render( ); - expect( - StyleSheet.flatten(getByTestId('tf-no-suffix-yet').props.style) - ).toEqual( - expect.objectContaining({ - textAlign: 'left', - writingDirection: 'ltr', - }) - ); + expect(screen.getByTestId('tf-no-suffix-yet')).toHaveStyle({ + textAlign: 'left', + writingDirection: 'ltr', + }); }); -it('does not apply the TextInput style prop to prefix or suffix Text', () => { - const { getByTestId, getByText } = render( +it('does not apply the TextInput style prop to prefix or suffix Text', async () => { + await render( { /> ); - const inputFlat = StyleSheet.flatten( - getByTestId('tf-input-style').props.style - ); - expect(inputFlat).toEqual( - expect.objectContaining({ fontSize: 40, letterSpacing: 9 }) - ); - - const prefixFlat = StyleSheet.flatten(getByText('$').props.style); - const suffixFlat = StyleSheet.flatten(getByText(']').props.style); + expect(screen.getByTestId('tf-input-style')).toHaveStyle({ + fontSize: 40, + letterSpacing: 9, + }); - expect(prefixFlat.fontSize).not.toBe(40); - expect(prefixFlat.letterSpacing).toBeUndefined(); - expect(suffixFlat.fontSize).not.toBe(40); - expect(suffixFlat.letterSpacing).toBeUndefined(); + expect(screen.getByText('$')).not.toHaveStyle({ fontSize: 40 }); + expect(screen.getByText('$')).not.toHaveStyle({ letterSpacing: 9 }); + expect(screen.getByText(']')).not.toHaveStyle({ fontSize: 40 }); + expect(screen.getByText(']')).not.toHaveStyle({ letterSpacing: 9 }); }); -it('passes defaultValue to the native input when uncontrolled without counter', () => { - const { getByTestId } = render( +it('passes defaultValue to the native input when uncontrolled without counter', async () => { + await render( ); - const input = getByTestId('tf-uncontrolled'); + const input = screen.getByTestId('tf-uncontrolled'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.defaultValue).toBe('hello'); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.value).toBeUndefined(); }); -it('updates the character counter for an uncontrolled field with counter enabled', () => { +it('updates the character counter for an uncontrolled field with counter enabled', async () => { const onChangeText = jest.fn(); - const { getByTestId, getByText } = render( + await render( ); - expect(getByText('1/10')).toBeTruthy(); + expect(screen.getByText('1/10')).toBeOnTheScreen(); - fireEvent.changeText(getByTestId('tf-uncontrolled-counter'), 'abcd'); + await userEvent.type(screen.getByTestId('tf-uncontrolled-counter'), 'bcd'); expect(onChangeText).toHaveBeenCalledWith('abcd'); - expect(getByText('4/10')).toBeTruthy(); + expect(screen.getByText('4/10')).toBeOnTheScreen(); }); -it('resets counter and hides prefix/suffix when clear() is called on uncontrolled field while blurred', () => { +it('resets counter and hides prefix/suffix when clear() is called on uncontrolled field while blurred', async () => { const ref = React.createRef(); - const { getByText, queryByText } = render( + await render( ); - expect(getByText('3/200')).toBeTruthy(); - expect(getByText('$')).toBeTruthy(); - expect(getByText('/100')).toBeTruthy(); + expect(screen.getByText('3/200')).toBeOnTheScreen(); + expect(screen.getByText('$')).toBeOnTheScreen(); + expect(screen.getByText('/100')).toBeOnTheScreen(); - act(() => { + await act(() => { ref.current?.clear(); }); - expect(getByText('0/200')).toBeTruthy(); - expect(queryByText('$')).toBeNull(); - expect(queryByText('/100')).toBeNull(); + expect(screen.getByText('0/200', includeHiddenElements)).toBeOnTheScreen(); + expect(screen.queryByText('$')).not.toBeOnTheScreen(); + expect(screen.queryByText('/100')).not.toBeOnTheScreen(); }); -it('resets counter but keeps prefix/suffix visible when clear() is called on uncontrolled field while focused', () => { +it('resets counter but keeps prefix/suffix visible when clear() is called on uncontrolled field while focused', async () => { const ref = React.createRef(); - const { getByTestId, getByText } = render( + await render( ); - expect(getByText('2/100')).toBeTruthy(); - expect(getByText('$')).toBeTruthy(); - expect(getByText(' kg')).toBeTruthy(); + expect(screen.getByText('2/100', includeHiddenElements)).toBeOnTheScreen(); + expect(screen.getByText('$', includeHiddenElements)).toBeOnTheScreen(); + expect(screen.getByText(' kg', includeHiddenElements)).toBeOnTheScreen(); - fireEvent(getByTestId('tf-clear-focused'), 'focus'); + await fireEvent(screen.getByTestId('tf-clear-focused'), 'focus'); - act(() => { + await act(() => { ref.current?.clear(); }); - expect(getByText('0/100')).toBeTruthy(); - expect(getByText('$')).toBeTruthy(); - expect(getByText(' kg')).toBeTruthy(); + expect(screen.getByText('0/100', includeHiddenElements)).toBeOnTheScreen(); + expect(screen.getByText('$', includeHiddenElements)).toBeOnTheScreen(); + expect(screen.getByText(' kg', includeHiddenElements)).toBeOnTheScreen(); }); -it('notifies the parent via onChangeText when clear() is called on a controlled field', () => { +it('notifies the parent via onChangeText when clear() is called on a controlled field', async () => { const ref = React.createRef(); const onChangeText = jest.fn(); - const { getByTestId } = render( + await render( ); - const input = getByTestId('tf-controlled'); + const input = screen.getByTestId('tf-controlled', includeHiddenElements); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(input.props.value).toBe('test@example.com'); - act(() => { + await act(() => { ref.current?.clear(); }); @@ -1263,9 +1318,9 @@ it('notifies the parent via onChangeText when clear() is called on a controlled expect(onChangeText).toHaveBeenCalledTimes(1); }); -it('hides prefix/suffix when blurring after clear() was called while focused', () => { +it('hides prefix/suffix when blurring after clear() was called while focused', async () => { const ref = React.createRef(); - const { getByTestId, getByText, queryByText } = render( + await render( ); - expect(getByText('$')).toBeTruthy(); - expect(getByText('/100')).toBeTruthy(); + expect(screen.getByText('$', includeHiddenElements)).toBeOnTheScreen(); + expect(screen.getByText('/100', includeHiddenElements)).toBeOnTheScreen(); - fireEvent(getByTestId('tf-clear-then-blur'), 'focus'); + await fireEvent(screen.getByTestId('tf-clear-then-blur'), 'focus'); - act(() => { + await act(() => { ref.current?.clear(); }); // While focused, prefix/suffix stay visible - expect(getByText('$')).toBeTruthy(); - expect(getByText('/100')).toBeTruthy(); + expect(screen.getByText('$', includeHiddenElements)).toBeOnTheScreen(); + expect(screen.getByText('/100', includeHiddenElements)).toBeOnTheScreen(); - fireEvent(getByTestId('tf-clear-then-blur'), 'blur'); + await fireEvent(screen.getByTestId('tf-clear-then-blur'), 'blur'); // After blur with no text, prefix/suffix should be hidden - expect(queryByText('$')).toBeNull(); - expect(queryByText('/100')).toBeNull(); + expect(screen.queryByText('$')).not.toBeOnTheScreen(); + expect(screen.queryByText('/100')).not.toBeOnTheScreen(); }); diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx index 994b976523..1ea9e20bae 100644 --- a/src/components/__tests__/ToggleButton.test.tsx +++ b/src/components/__tests__/ToggleButton.test.tsx @@ -4,29 +4,33 @@ import { describe, expect, it, jest } from '@jest/globals'; import { act } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import ToggleButton from '../ToggleButton'; import { getToggleButtonColor } from '../ToggleButton/utils'; -it('renders toggle button', () => { - const tree = render( - {}} icon="heart" /> +it('renders toggle button', async () => { + const tree = ( + await render( + {}} icon="heart" /> + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders disabled toggle button', () => { - const tree = render( - +it('renders disabled toggle button', async () => { + const tree = ( + await render( + + ) ).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders unchecked toggle button', () => { - const tree = render( - +it('renders unchecked toggle button', async () => { + const tree = ( + await render() ).toJSON(); expect(tree).toMatchSnapshot(); @@ -52,9 +56,9 @@ describe('getToggleButtonColor', () => { }); }); -it('animated value changes correctly', () => { +it('animated value changes correctly', async () => { const value = new Animated.Value(1); - const { getByTestId } = render( + await render( { style={[{ transform: [{ scale: value }] }]} /> ); - expect(getByTestId('toggle-button-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); + expect(screen.getByTestId('toggle-button-container-outer-layer')).toHaveStyle( + { + transform: [{ scale: 1 }], + } + ); Animated.timing(value, { toValue: 1.5, @@ -73,10 +79,12 @@ it('animated value changes correctly', () => { duration: 200, }).start(); - act(() => { + await act(() => { jest.advanceTimersByTime(200); }); - expect(getByTestId('toggle-button-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); + expect(screen.getByTestId('toggle-button-container-outer-layer')).toHaveStyle( + { + transform: [{ scale: 1.5 }], + } + ); }); diff --git a/src/components/__tests__/Tooltip.test.tsx b/src/components/__tests__/Tooltip.test.tsx index 62666e9d7c..75b4a18cf7 100644 --- a/src/components/__tests__/Tooltip.test.tsx +++ b/src/components/__tests__/Tooltip.test.tsx @@ -11,8 +11,7 @@ import { it, jest, } from '@jest/globals'; -import { act, fireEvent } from '@testing-library/react-native'; -import type { ReactTestInstance } from 'react-test-renderer'; +import { act, fireEvent, userEvent } from '@testing-library/react-native'; import PaperProvider from '../../core/PaperProvider'; import { render } from '../../test-utils'; @@ -38,11 +37,22 @@ const DummyComponent = ({ ); describe('Tooltip', () => { - const getTrigger = (getByText: (text: string) => ReactTestInstance) => - getByText('dummy component').parent as ReactTestInstance; + const getTrigger = ( + getByText: Awaited>['getByText'] + ) => { + const trigger = getByText('dummy component', { + includeHiddenElements: true, + }).parent; + + if (!trigger) { + throw new Error('Expected Tooltip trigger wrapper'); + } - const runTimers = (ms?: number) => { - act(() => { + return trigger; + }; + + const runTimers = async (ms?: number) => { + await act(() => { if (ms === undefined) { jest.runOnlyPendingTimers(); } else { @@ -51,7 +61,7 @@ describe('Tooltip', () => { }); }; - const setup = ( + const setup = async ( propOverrides?: Partial>, measure = {} ) => { @@ -75,7 +85,7 @@ describe('Tooltip', () => { .spyOn(View.prototype, 'measure') .mockImplementation((cb) => cb(x, y, width, height, pageX, pageY)); - const wrapper = render( + const wrapper = await render( @@ -102,11 +112,11 @@ describe('Tooltip', () => { it('removes hideTooltipTimer when the component unmounts', async () => { const { wrapper: { getByText, unmount }, - } = setup({ enterTouchDelay: 5000 }); + } = await setup({ enterTouchDelay: 5000 }); - fireEvent(getTrigger(getByText), 'pressOut'); + await fireEvent(getTrigger(getByText), 'pressOut'); - unmount(); + await unmount(); expect(global.clearTimeout).toHaveBeenCalledTimes(1); }); @@ -114,13 +124,13 @@ describe('Tooltip', () => { it('removes Dimensions listener when the component unmount', async () => { const { wrapper: { getByText, findByText, unmount }, - } = setup(); + } = await setup(); - fireEvent(getTrigger(getByText), 'longPress'); + await userEvent.longPress(getTrigger(getByText)); await findByText('some tooltip text'); - unmount(); + await unmount(); expect(mockedRemoveEventListener).toHaveBeenCalled(); }); @@ -134,37 +144,34 @@ describe('Tooltip', () => { jest.clearAllMocks(); }); - it('clears the hide timer when the user start pressing the component again', () => { + it('clears the hide timer when the user start pressing the component again', async () => { jest.spyOn(global, 'clearTimeout'); const { wrapper: { getByText }, - } = setup(); + } = await setup(); const trigger = getTrigger(getByText); - fireEvent(trigger, 'longPress'); - fireEvent(trigger, 'pressOut'); - fireEvent(trigger, 'longPress'); + await userEvent.longPress(trigger); + await userEvent.longPress(trigger); expect(global.clearTimeout).toHaveBeenCalledTimes(1); }); }); describe('pressOut', () => { - // eslint-disable-next-line jest/valid-title it('hides the tooltip when the user stop pressing the component', async () => { const { wrapper: { queryByText, getByText, findByText }, - } = setup({ enterTouchDelay: 50, leaveTouchDelay: 0 }); + } = await setup({ enterTouchDelay: 50, leaveTouchDelay: 0 }); - fireEvent(getTrigger(getByText), 'longPress'); + await userEvent.longPress(getTrigger(getByText)); await findByText('some tooltip text'); - fireEvent(getTrigger(getByText), 'pressOut'); - runTimers(); + await runTimers(); - expect(queryByText('some tooltip text')).toBeNull(); + expect(queryByText('some tooltip text')).not.toBeOnTheScreen(); }); }); @@ -187,23 +194,20 @@ describe('Tooltip', () => { it('centers the tooltip in the middle of the children component', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup(); + } = await setup(); - fireEvent(getTrigger(getByText), 'longPress'); + await userEvent.longPress(getTrigger(getByText)); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 - top: 250, // pageY (200) + height (50) - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 + top: 250, // pageY (200) + height (50) + }); }); }); @@ -211,23 +215,20 @@ describe('Tooltip', () => { it('renders the tooltip with the right placement', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord + } = await setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord - fireEvent(getTrigger(getByText), 'longPress'); + await userEvent.longPress(getTrigger(getByText)); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 0, // Tooltip renders starting from children's x coord - top: 250, - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 0, // Tooltip renders starting from children's x coord + top: 250, + }); }); }); @@ -235,23 +236,20 @@ describe('Tooltip', () => { it('renders the tooltip with the right placement', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit + } = await setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit - fireEvent(getTrigger(getByText), 'longPress'); + await userEvent.longPress(getTrigger(getByText)); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen - top: 250, - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen + top: 250, + }); }); }); @@ -259,23 +257,20 @@ describe('Tooltip', () => { it('renders the tooltip with the right placement', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup({}, { pageY: 600, height: 50 }); + } = await setup({}, { pageY: 600, height: 50 }); - fireEvent(getTrigger(getByText), 'longPress'); + await userEvent.longPress(getTrigger(getByText)); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 210, - top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 210, + top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, + }); }); }); }); @@ -296,11 +291,11 @@ describe('Tooltip', () => { it('removes showTooltipTimer when the component unmounts', async () => { const { wrapper: { getByText, unmount }, - } = setup({ enterTouchDelay: 5000 }); + } = await setup({ enterTouchDelay: 5000 }); - fireEvent(getTrigger(getByText), 'hoverIn'); + await fireEvent(getTrigger(getByText), 'hoverIn'); - unmount(); + await unmount(); expect(global.clearTimeout).toHaveBeenCalledTimes(1); }); @@ -308,11 +303,11 @@ describe('Tooltip', () => { it('removes hideTooltipTimer when the component unmounts', async () => { const { wrapper: { getByText, unmount }, - } = setup({ enterTouchDelay: 5000 }); + } = await setup({ enterTouchDelay: 5000 }); - fireEvent(getTrigger(getByText), 'hoverOut'); + await fireEvent(getTrigger(getByText), 'hoverOut'); - unmount(); + await unmount(); expect(global.clearTimeout).toHaveBeenCalledTimes(1); }); @@ -320,14 +315,14 @@ describe('Tooltip', () => { it('removes Dimensions listener when the component unmount', async () => { const { wrapper: { getByText, findByText, unmount }, - } = setup(); + } = await setup(); - fireEvent(getTrigger(getByText), 'hoverIn'); - runTimers(500); + await fireEvent(getTrigger(getByText), 'hoverIn'); + await runTimers(500); await findByText('some tooltip text'); - unmount(); + await unmount(); expect(mockedRemoveEventListener).toHaveBeenCalled(); }); @@ -341,38 +336,37 @@ describe('Tooltip', () => { jest.clearAllMocks(); }); - it('clears the hide timer when the user start hovering the component again', () => { + it('clears the hide timer when the user start hovering the component again', async () => { jest.spyOn(global, 'clearTimeout'); const { wrapper: { getByText }, - } = setup(); + } = await setup(); const trigger = getTrigger(getByText); - fireEvent(trigger, 'hoverIn'); - fireEvent(trigger, 'hoverOut'); - fireEvent(trigger, 'hoverIn'); + await fireEvent(trigger, 'hoverIn'); + await fireEvent(trigger, 'hoverOut'); + await fireEvent(trigger, 'hoverIn'); expect(global.clearTimeout).toHaveBeenCalledTimes(2); }); }); describe('hoverOut', () => { - // eslint-disable-next-line jest/valid-title it('hides the tooltip when the user stops hovering the component', async () => { const { wrapper: { queryByText, getByText, findByText }, - } = setup({ enterTouchDelay: 50, leaveTouchDelay: 0 }); + } = await setup({ enterTouchDelay: 50, leaveTouchDelay: 0 }); - fireEvent(getTrigger(getByText), 'hoverIn'); - runTimers(50); + await fireEvent(getTrigger(getByText), 'hoverIn'); + await runTimers(50); await findByText('some tooltip text'); - fireEvent(getTrigger(getByText), 'hoverOut'); - runTimers(); + await fireEvent(getTrigger(getByText), 'hoverOut'); + await runTimers(); - expect(queryByText('some tooltip text')).toBeNull(); + expect(queryByText('some tooltip text')).not.toBeOnTheScreen(); }); }); @@ -395,24 +389,21 @@ describe('Tooltip', () => { it('centers the tooltip in the middle of the children component', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup(); + } = await setup(); - fireEvent(getTrigger(getByText), 'hoverIn'); - runTimers(500); + await fireEvent(getTrigger(getByText), 'hoverIn'); + await runTimers(500); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 - top: 250, // pageY (200) + height (50) - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 + top: 250, // pageY (200) + height (50) + }); }); }); @@ -420,24 +411,21 @@ describe('Tooltip', () => { it('renders the tooltip with the right placement', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord + } = await setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord - fireEvent(getTrigger(getByText), 'hoverIn'); - runTimers(500); + await fireEvent(getTrigger(getByText), 'hoverIn'); + await runTimers(500); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 0, // Tooltip renders starting from children's x coord - top: 250, - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 0, // Tooltip renders starting from children's x coord + top: 250, + }); }); }); @@ -445,24 +433,21 @@ describe('Tooltip', () => { it('renders the tooltip with the right placement', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit + } = await setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit - fireEvent(getTrigger(getByText), 'hoverIn'); - runTimers(500); + await fireEvent(getTrigger(getByText), 'hoverIn'); + await runTimers(500); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen - top: 250, - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen + top: 250, + }); }); }); @@ -470,24 +455,21 @@ describe('Tooltip', () => { it('renders the tooltip with the right placement', async () => { const { wrapper: { getByText, getByTestId, findByText }, - } = setup({}, { pageY: 600, height: 50 }); + } = await setup({}, { pageY: 600, height: 50 }); - fireEvent(getTrigger(getByText), 'hoverIn'); - runTimers(500); + await fireEvent(getTrigger(getByText), 'hoverIn'); + await runTimers(500); - fireEvent(await findByText('some tooltip text'), 'layout', { + await fireEvent(await findByText('some tooltip text'), 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container').props.style).toMatchObject([ - {}, - { - left: 210, - top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, - }, - ]); + expect(getByTestId('tooltip-container')).toHaveStyle({ + left: 210, + top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, + }); }); }); }); diff --git a/src/components/__tests__/TouchableRipple.test.tsx b/src/components/__tests__/TouchableRipple.test.tsx index 4448301244..f3c4cb1168 100644 --- a/src/components/__tests__/TouchableRipple.test.tsx +++ b/src/components/__tests__/TouchableRipple.test.tsx @@ -2,44 +2,44 @@ import { Platform, Text } from 'react-native'; import type { GestureResponderEvent } from 'react-native'; import { describe, expect, it, jest } from '@jest/globals'; -import { fireEvent } from '@testing-library/react-native'; +import { userEvent } from '@testing-library/react-native'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import TouchableRipple from '../TouchableRipple/TouchableRipple.native'; describe('TouchableRipple', () => { - it('renders children correctly', () => { - const { getByText } = render( + it('renders children correctly', async () => { + await render( Button ); - expect(getByText('Button')).toBeTruthy(); + expect(screen.getByText('Button')).toBeOnTheScreen(); }); - it('calls onPress when pressed', () => { + it('calls onPress when pressed', async () => { const onPress = jest.fn<(event: GestureResponderEvent) => void>(); - const { getByText } = render( + await render( Button ); - fireEvent.press(getByText('Button')); + await userEvent.press(screen.getByText('Button')); expect(onPress).toHaveBeenCalledTimes(1); }); - it('disables the button when disabled prop is true', () => { + it('disables the button when disabled prop is true', async () => { const onPress = jest.fn<(event: GestureResponderEvent) => void>(); - const { getByText } = render( + await render( Button ); - fireEvent.press(getByText('Button')); + await userEvent.press(screen.getByText('Button')); expect(onPress).not.toHaveBeenCalled(); }); @@ -47,25 +47,25 @@ describe('TouchableRipple', () => { describe('on iOS', () => { Platform.OS = 'ios'; - it('displays the underlay when pressed', () => { - const { getByTestId } = render( + it('displays the underlay when pressed', async () => { + await render( Press me! ); - const underlay = getByTestId('touchable-ripple-underlay'); - expect(underlay).toBeDefined(); + const underlay = screen.getByTestId('touchable-ripple-underlay'); + expect(underlay).toBeOnTheScreen(); }); - it('renders custom underlay color', () => { - const { getByTestId } = render( + it('renders custom underlay color', async () => { + await render( Press me! ); - const underlay = getByTestId('touchable-ripple-underlay'); + const underlay = screen.getByTestId('touchable-ripple-underlay'); expect(underlay).toHaveStyle({ backgroundColor: 'purple' }); }); }); diff --git a/src/components/__tests__/Typography/Text.test.tsx b/src/components/__tests__/Typography/Text.test.tsx index c861343135..b470f47f32 100644 --- a/src/components/__tests__/Typography/Text.test.tsx +++ b/src/components/__tests__/Typography/Text.test.tsx @@ -1,7 +1,7 @@ import { expect, it, jest } from '@jest/globals'; import PaperProvider from '../../../core/PaperProvider'; -import { render } from '../../../test-utils'; +import { render, screen } from '../../../test-utils'; import configureFonts from '../../../theme/fonts'; import { LightTheme } from '../../../theme/schemes'; import { tokens } from '../../../theme/tokens'; @@ -9,7 +9,7 @@ import Text, { customText } from '../../Typography/Text'; const content = 'Something rendered as a child content'; -it('renders every variant of Text with children as content', () => { +it('renders every variant of Text with children as content', async () => { const variants = ( <> {content} @@ -34,24 +34,22 @@ it('renders every variant of Text with children as content', () => { ); - const tree = render(variants).toJSON(); + const tree = (await render(variants)).toJSON(); expect(tree).toMatchSnapshot(); }); -it('renders v3 Text component without variant with default fontWeight and fontFamily', () => { - const { getByTestId } = render( - {content} - ); +it('renders v3 Text component without variant with default fontWeight and fontFamily', async () => { + await render({content}); const { brandRegular, weightRegular } = tokens.md.ref.typeface; - expect(getByTestId('text-without-variant')).toHaveStyle({ + expect(screen.getByTestId('text-without-variant')).toHaveStyle({ fontFamily: brandRegular, fontWeight: weightRegular, }); }); -it('renders v3 Text component with custom variant correctly', () => { +it('renders v3 Text component with custom variant correctly', async () => { const fontConfig = { customVariant: { fontFamily: 'Montserrat-Regular', @@ -67,7 +65,7 @@ it('renders v3 Text component with custom variant correctly', () => { fonts: configureFonts({ config: fontConfig }), }; const Text = customText<'customVariant'>(); - const { getByTestId } = render( + await render( {content} @@ -75,49 +73,56 @@ it('renders v3 Text component with custom variant correctly', () => { ); - expect(getByTestId('text-with-custom-variant').props.style).toMatchSnapshot(); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('text-with-custom-variant').props.style + ).toMatchSnapshot(); }); -it("nested text with variant should override parent's variant", () => { - const { getByTestId } = render( +it("nested text with variant should override parent's variant", async () => { + await render( Test ); - expect(getByTestId('parent-text')).toHaveStyle(LightTheme.fonts.displayLarge); + expect(screen.getByTestId('parent-text')).toHaveStyle( + LightTheme.fonts.displayLarge + ); }); -it("nested non-text component should not override parent's variant", () => { +it("nested non-text component should not override parent's variant", async () => { const ChildComponent = () => <>{content}; - const { getByTestId } = render( + await render( ); - expect(getByTestId('parent-text')).toHaveStyle(LightTheme.fonts.displayLarge); + expect(screen.getByTestId('parent-text')).toHaveStyle( + LightTheme.fonts.displayLarge + ); }); -it("nested text without variant, but with styles, should override parent's styles", () => { +it("nested text without variant, but with styles, should override parent's styles", async () => { const customStyle = { fontSize: 50, lineHeight: 70 }; - const { getByTestId } = render( + await render( Test ); - expect(getByTestId('parent-text')).toHaveStyle(customStyle); + expect(screen.getByTestId('parent-text')).toHaveStyle(customStyle); }); -it('throws when custom variant not provided', () => { +it('throws when custom variant not provided', async () => { jest.spyOn(console, 'error').mockImplementation(() => {}); const Text = customText<'myCustomVariant'>(); - expect(() => + await expect( render({content}) - ).toThrow(/myCustomVariant was not provided/); + ).rejects.toThrow(/myCustomVariant was not provided/); jest.clearAllMocks(); }); diff --git a/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap b/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap index 21130dee07..a69b0061c6 100644 --- a/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap +++ b/src/components/__tests__/Typography/__snapshots__/Text.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders every variant of Text with children as content 1`] = ` -[ +<> Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , + Something rendered as a child content - , -] + + `; exports[`renders v3 Text component with custom variant correctly 1`] = ` diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index 7b5d8ad45e..b772a50b60 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -66,7 +66,9 @@ exports[`allows customizing Route's type via generics 1`] = ` } } > - First + + First + @@ -733,7 +735,9 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` } } > - Route: 0 + + Route: 0 + @@ -1474,7 +1478,9 @@ exports[`hides labels in shifting bottom navigation 1`] = ` } } > - Route: 0 + + Route: 0 + @@ -2215,7 +2221,9 @@ exports[`renders bottom navigation with getLazy 1`] = ` } } > - Route: 0 + + Route: 0 + - Route: 1 + + Route: 1 + - Route: 3 + + Route: 3 + - Route: 4 + + Route: 4 + @@ -4076,7 +4090,9 @@ exports[`renders bottom navigation with scene animation 1`] = ` } } > - Route: 0 + + Route: 0 + @@ -5550,7 +5566,9 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` } } > - Route: 0 + + Route: 0 + @@ -5789,11 +5807,15 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` } } > - Route: 0 - + - Route: 0 - + @@ -5970,11 +5996,15 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` } } > - Route: 1 - + - Route: 1 - + @@ -6151,11 +6185,15 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` } } > - Route: 2 - + - Route: 2 - + @@ -6252,7 +6294,9 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` } } > - Route: 0 + + Route: 0 + @@ -6496,11 +6540,15 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` } } > - Route: 0 - + @@ -6663,11 +6711,15 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` } } > - Route: 1 - + @@ -6830,11 +6882,15 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` } } > - Route: 2 - + @@ -6997,11 +7053,15 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` } } > - Route: 3 - + @@ -7164,11 +7224,15 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` } } > - Route: 4 - + @@ -7246,7 +7310,9 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom } } > - Route: 0 + + Route: 0 + @@ -8347,7 +8413,9 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav } } > - Route: 0 + + Route: 0 + @@ -9295,7 +9363,9 @@ exports[`renders non-shifting bottom navigation 1`] = ` } } > - Route: 0 + + Route: 0 + @@ -10396,7 +10466,9 @@ exports[`renders shifting bottom navigation 1`] = ` } } > - Route: 0 + + Route: 0 + diff --git a/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap b/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap index 48d20d29eb..7f33de6094 100644 --- a/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders extended FAB collapsed 1`] = ` -[ +<> - , + New message - , -] + + `; exports[`renders extended FAB expanded 1`] = ` -[ +<> - , + New message - , -] + + `; exports[`renders extended FAB large size 1`] = ` -[ +<> - , + New message - , -] + + `; exports[`renders extended FAB medium size 1`] = ` -[ +<> - , + New message - , -] + + `; exports[`renders extended FAB not visible 1`] = ` -[ +<> - , + New message - , -] + + `; exports[`renders extended FAB transitioning to collapsed 1`] = ` -[ +<> - , + New message - , -] + + `; diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index ee711c93f8..90a52d83a0 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders menu with content styles 1`] = ` -[ +<> - , + - , -] + + `; exports[`renders not visible menu 1`] = ` @@ -732,7 +732,7 @@ exports[`renders not visible menu 1`] = ` `; exports[`renders visible menu 1`] = ` -[ +<> - , + - , -] + + `; diff --git a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap index 9b1babef34..d5101a1b26 100644 --- a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Menu Item renders menu item 1`] = ` -[ +<> - , + - , + - , + - , + - , -] + + `; diff --git a/src/components/__tests__/__snapshots__/Portal.test.tsx.snap b/src/components/__tests__/__snapshots__/Portal.test.tsx.snap index d4a8f49b9d..7168f606d8 100644 --- a/src/components/__tests__/__snapshots__/Portal.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Portal.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders portal with siblings 1`] = ` -[ +<> Outside content - , + Portal content - , -] + + `; diff --git a/src/core/PaperProvider.tsx b/src/core/PaperProvider.tsx index 8c1343dbd9..5149efcd6f 100644 --- a/src/core/PaperProvider.tsx +++ b/src/core/PaperProvider.tsx @@ -32,7 +32,9 @@ const PaperProvider = (props: Props) => { const theme = React.useMemo(() => { const isDark = props.theme?.dark ?? colorScheme === 'dark'; const base = defaultThemes[isDark ? 'dark' : 'light']; - const scale = resolvedReduceMotion ? 0 : props.theme?.animation?.scale ?? 1; + const scale = resolvedReduceMotion + ? 0 + : (props.theme?.animation?.scale ?? 1); return { ...base, diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 70d90a3ecf..1c5d821ebe 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -8,7 +8,7 @@ import { it, jest as mockJest, } from '@jest/globals'; -import { render, act } from '@testing-library/react-native'; +import { act, render, screen } from '@testing-library/react-native'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { DarkTheme, DynamicLightTheme, LightTheme } from '../../theme/schemes'; @@ -96,7 +96,7 @@ const mockAccessibilityInfo = () => { removeEventListener: mockJest.fn((cb: (enabled: boolean) => void) => { listeners.push(cb); }), - isReduceMotionEnabled: mockJest.fn(() => Promise.resolve(false)), + isReduceMotionEnabled: mockJest.fn(() => Promise.resolve(undefined)), __internalListeners: listeners, }, }; @@ -111,7 +111,7 @@ const FakeChild = () => { const createProvider = (theme?: ThemeProp) => { return ( - + ); @@ -130,12 +130,14 @@ describe('PaperProvider', () => { it('handles theme change', async () => { mockAppearance(); - const { getByTestId } = render(createProvider()); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( + await render(createProvider()); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('provider-child-view').props.theme).toStrictEqual( ExtendedLightTheme ); - act(() => Appearance.__internalListeners[0]({ colorScheme: 'dark' })); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( + await act(() => Appearance.__internalListeners[0]({ colorScheme: 'dark' })); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('provider-child-view').props.theme).toStrictEqual( ExtendedDarkTheme ); }); @@ -144,13 +146,18 @@ describe('PaperProvider', () => { mockAppearance(); mockAccessibilityInfo(); - const { getByTestId } = render(createProvider()); + await render( + + + + ); expect(AccessibilityInfo.addEventListener).toHaveBeenCalled(); - act(() => AccessibilityInfo.__internalListeners[0](true)); + await act(() => AccessibilityInfo.__internalListeners[0](true)); expect( - getByTestId('provider-child-view').props.theme.animation.scale + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('provider-child-view').props.theme.animation.scale ).toStrictEqual(0); }); @@ -163,26 +170,32 @@ describe('PaperProvider', () => { return ; }; - const { getByTestId, rerender } = render( + const { rerender } = await render( ); - expect(getByTestId('reduce-motion-probe').props.reduceMotion).toBe(true); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('reduce-motion-probe').props.reduceMotion).toBe( + true + ); - rerender( + await rerender( ); - expect(getByTestId('reduce-motion-probe').props.reduceMotion).toBe(false); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('reduce-motion-probe').props.reduceMotion).toBe( + false + ); }); it('removes the AccessibilityInfo listener when reduceMotion switches from "auto" to "off"', async () => { mockAppearance(); mockAccessibilityInfo(); - const { rerender } = render( + const { rerender } = await render( @@ -191,7 +204,7 @@ describe('PaperProvider', () => { expect(AccessibilityInfo.addEventListener).toHaveBeenCalledTimes(1); expect(AccessibilityInfo.removeEventListener).not.toHaveBeenCalled(); - rerender( + await rerender( @@ -203,7 +216,7 @@ describe('PaperProvider', () => { it('does not subscribe to AccessibilityInfo when reduceMotion is "off"', async () => { mockAppearance(); mockAccessibilityInfo(); - const { getByTestId } = render( + await render( @@ -211,14 +224,15 @@ describe('PaperProvider', () => { expect(AccessibilityInfo.addEventListener).not.toHaveBeenCalled(); expect( - getByTestId('provider-child-view').props.theme.animation.scale + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('provider-child-view').props.theme.animation.scale ).toStrictEqual(1); }); it('forces animation.scale to 0 when reduceMotion is "on" without subscribing', async () => { mockAppearance(); mockAccessibilityInfo(); - const { getByTestId } = render( + await render( @@ -226,7 +240,8 @@ describe('PaperProvider', () => { expect(AccessibilityInfo.addEventListener).not.toHaveBeenCalled(); expect( - getByTestId('provider-child-view').props.theme.animation.scale + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('provider-child-view').props.theme.animation.scale ).toStrictEqual(0); }); @@ -237,22 +252,24 @@ describe('PaperProvider', () => { it('should set Appearance listeners, if there is no theme', async () => { mockAppearance(); - const { getByTestId } = render(createProvider()); + await render(createProvider()); expect(Appearance.addChangeListener).toHaveBeenCalled(); - act(() => Appearance.__internalListeners[0]({ colorScheme: 'dark' })); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( + await act(() => Appearance.__internalListeners[0]({ colorScheme: 'dark' })); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('provider-child-view').props.theme).toStrictEqual( ExtendedDarkTheme ); }); it('should not set Appearance listeners, if the theme is passed', async () => { mockAppearance(); - const { getByTestId } = render(createProvider(ExtendedLightTheme)); + await render(createProvider(ExtendedLightTheme)); expect(Appearance.addChangeListener).not.toHaveBeenCalled(); expect(Appearance.removeChangeListener).not.toHaveBeenCalled(); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('provider-child-view').props.theme).toStrictEqual( ExtendedLightTheme ); }); @@ -261,9 +278,10 @@ describe('PaperProvider', () => { mockJest.mock('react-native/Libraries/Utilities/Appearance', () => { return null; }); - const { getByTestId } = render(createProvider()); + await render(createProvider()); expect(Appearance).toEqual(null); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('provider-child-view').props.theme).toStrictEqual( ExtendedLightTheme ); }); @@ -288,10 +306,11 @@ describe('PaperProvider', () => { async ({ theme, colorScheme }) => { mockAppearance(); mockJest.mocked(Appearance.getColorScheme).mockReturnValue(colorScheme); - const { getByTestId } = render(createProvider()); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( - theme - ); + await render(createProvider()); + expect( + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + screen.getByTestId('provider-child-view').props.theme + ).toStrictEqual(theme); } ); @@ -304,8 +323,9 @@ describe('PaperProvider', () => { primary: 'tomato', }, } as ThemeProp; - const { getByTestId } = render(createProvider(customTheme)); - expect(getByTestId('provider-child-view').props.theme).toStrictEqual( + await render(createProvider(customTheme)); + // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. + expect(screen.getByTestId('provider-child-view').props.theme).toStrictEqual( customTheme ); }); diff --git a/src/core/useSystemColorScheme.ts b/src/core/useSystemColorScheme.ts index 3dd7a50821..12c2ab793c 100644 --- a/src/core/useSystemColorScheme.ts +++ b/src/core/useSystemColorScheme.ts @@ -12,7 +12,7 @@ import type { ColorSchemeName } from 'react-native'; */ export function useSystemColorScheme(enabled: boolean): ColorSchemeName { const [colorScheme, setColorScheme] = React.useState(() => - enabled ? Appearance?.getColorScheme() ?? 'light' : 'light' + enabled ? (Appearance?.getColorScheme() ?? 'light') : 'light' ); React.useEffect(() => { diff --git a/src/theme/__tests__/fonts.test.js b/src/theme/__tests__/fonts.test.js index 6650c4f098..23e0f794ae 100644 --- a/src/theme/__tests__/fonts.test.js +++ b/src/theme/__tests__/fonts.test.js @@ -1,3 +1,5 @@ +import { afterEach, describe, expect, it, jest } from '@jest/globals'; + const customFont = { displayLarge: { fontFamily: 'NotoSans', diff --git a/src/theme/fonts.tsx b/src/theme/fonts.tsx index e380655486..847f62637c 100644 --- a/src/theme/fonts.tsx +++ b/src/theme/fonts.tsx @@ -38,7 +38,6 @@ function configureFontsConfig( ); } -// eslint-disable-next-line no-redeclare export default function configureFonts(params?: { config?: Partial; }): Typescale; diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index a8ecf34c34..b4101eaa0f 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -81,7 +81,6 @@ export const getTheme = ( return defaultThemes[scheme]; }; -// eslint-disable-next-line no-redeclare export function adaptNavigationTheme(themes: { reactNavigationLight: T; materialLight?: Theme; @@ -98,7 +97,7 @@ export function adaptNavigationTheme(themes: { // eslint-disable-next-line no-redeclare export function adaptNavigationTheme< TLight extends NavigationTheme, - TDark extends NavigationTheme + TDark extends NavigationTheme, >(themes: { reactNavigationLight: TLight; reactNavigationDark: TDark; diff --git a/src/theme/schemes/DynamicTheme.android.tsx b/src/theme/schemes/DynamicTheme.android.tsx index 097109ea09..07cc9f93d2 100644 --- a/src/theme/schemes/DynamicTheme.android.tsx +++ b/src/theme/schemes/DynamicTheme.android.tsx @@ -7,6 +7,7 @@ import type { Theme, ThemeColors } from '../types'; const apiLevel = Platform.Version as number; +// eslint-disable-next-line @react-native/platform-colors const ac = (name: string) => PlatformColor(`@android:color/${name}`); /** @@ -23,8 +24,8 @@ const pick = (api34: string, api31: string | null, ref: string): ColorValue => apiLevel >= 34 ? ac(api34) : apiLevel >= 31 && api31 !== null - ? ac(api31) - : ref; + ? ac(api31) + : ref; // Known limitation: surface/container roles on API 31-33 use // @color/m3_ref_palette_dynamic_neutral_variant* (MCL resources that require a diff --git a/src/theme/tokens/sys/elevation.ts b/src/theme/tokens/sys/elevation.ts index 9fcc7cb4fe..de797183d4 100644 --- a/src/theme/tokens/sys/elevation.ts +++ b/src/theme/tokens/sys/elevation.ts @@ -74,7 +74,6 @@ const getShadowColor = (shadowColor: ColorValue, shadowOpacity: number) => { const getBoxShadowValue = (elevation: number, shadowColor: string) => `0px ${shadowLayers[0].height[elevation]}px ${shadowLayers[0].shadowRadius[elevation]}px ${shadowColor}`; -// eslint-disable-next-line no-redeclare export function shadow(elevation: number, shadowColor: ColorValue): ViewStyle; // eslint-disable-next-line no-redeclare export function shadow( diff --git a/src/utils/__tests__/addEventListener.js b/src/utils/__tests__/addEventListener.js deleted file mode 100644 index 6446ccf733..0000000000 --- a/src/utils/__tests__/addEventListener.js +++ /dev/null @@ -1,62 +0,0 @@ -import { BackHandler, Keyboard } from 'react-native'; - -import { addEventListener, addListener } from '../addEventListener'; - -const mockModule = jest.fn(); -const handler = jest.fn(); - -describe('addEventListener', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('assigns subscription', () => { - BackHandler.addEventListener = mockModule; - addEventListener(BackHandler, 'hardwareBackPress', handler); - expect(BackHandler.addEventListener).toHaveBeenCalledWith( - 'hardwareBackPress', - handler - ); - }); - - it('removes subscription', () => { - const subscription = addEventListener( - BackHandler, - 'hardwareBackPress', - handler - ); - BackHandler.removeEventListener = mockModule; - subscription.remove(); - - expect(BackHandler.removeEventListener).toHaveBeenCalledWith( - 'hardwareBackPress', - handler - ); - }); -}); - -describe('addListener', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('assigns subscription', () => { - Keyboard.addListener = mockModule; - addListener(Keyboard, 'keyboardWillShow', handler); - expect(Keyboard.addListener).toHaveBeenCalledWith( - 'keyboardWillShow', - handler - ); - }); - - it('removes subscription', () => { - const subscription = addListener(Keyboard, 'keyboardWillShow', handler); - Keyboard.removeEventListener = mockModule; - subscription.remove(); - - expect(Keyboard.removeEventListener).toHaveBeenCalledWith( - 'keyboardWillShow', - handler - ); - }); -}); diff --git a/src/utils/__tests__/addEventListener.test.tsx b/src/utils/__tests__/addEventListener.test.tsx new file mode 100644 index 0000000000..af1a33cc11 --- /dev/null +++ b/src/utils/__tests__/addEventListener.test.tsx @@ -0,0 +1,77 @@ +import { beforeEach, describe, expect, it, jest } from '@jest/globals'; + +import { addEventListener, addListener } from '../addEventListener'; + +type LegacyEventModule = { + addEventListener: (eventName: string, handler: () => void) => undefined; + removeEventListener: (eventName: string, handler: () => void) => void; +}; + +type LegacyListenerModule = { + addListener: (eventName: string, handler: () => void) => undefined; + removeEventListener: (eventName: string, handler: () => void) => void; +}; + +const handler = jest.fn(); + +describe('addEventListener', () => { + const module: LegacyEventModule = { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('assigns subscription', () => { + addEventListener(module, 'hardwareBackPress', handler); + + expect(module.addEventListener).toHaveBeenCalledWith( + 'hardwareBackPress', + handler + ); + }); + + it('removes subscription', () => { + const subscription = addEventListener(module, 'hardwareBackPress', handler); + + subscription.remove(); + + expect(module.removeEventListener).toHaveBeenCalledWith( + 'hardwareBackPress', + handler + ); + }); +}); + +describe('addListener', () => { + const module: LegacyListenerModule = { + addListener: jest.fn(), + removeEventListener: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('assigns subscription', () => { + addListener(module, 'keyboardWillShow', handler); + + expect(module.addListener).toHaveBeenCalledWith( + 'keyboardWillShow', + handler + ); + }); + + it('removes subscription', () => { + const subscription = addListener(module, 'keyboardWillShow', handler); + + subscription.remove(); + + expect(module.removeEventListener).toHaveBeenCalledWith( + 'keyboardWillShow', + handler + ); + }); +}); diff --git a/src/utils/addEventListener.tsx b/src/utils/addEventListener.tsx index 40bb4508b3..dce970c6b7 100644 --- a/src/utils/addEventListener.tsx +++ b/src/utils/addEventListener.tsx @@ -7,10 +7,10 @@ export function addEventListener< T extends { addEventListener: ( ...args: any - ) => NativeEventSubscription | EmitterSubscription; + ) => NativeEventSubscription | EmitterSubscription | undefined; } & { removeEventListener?: (...args: any) => void } & { remove?: (...args: any) => void; - } + }, >(Module: T, ...rest: Parameters) { const [eventName, handler] = rest; @@ -33,9 +33,9 @@ export function addEventListener< export function addListener< T extends { - addListener: (...args: any) => EmitterSubscription; + addListener: (...args: any) => EmitterSubscription | undefined; removeEventListener: (...args: any) => void; - } + }, >(Module: T, ...rest: Parameters) { const [eventName, handler] = rest; diff --git a/src/utils/splitStyles.ts b/src/utils/splitStyles.ts index 9c1f42df34..d6b490e45a 100644 --- a/src/utils/splitStyles.ts +++ b/src/utils/splitStyles.ts @@ -55,6 +55,6 @@ export function splitStyles( // Convert arrays of entries into objects return newStyles.map((styles) => Object.fromEntries(styles)) as unknown as [ ViewStyle, - ...MappedTuple + ...MappedTuple, ]; } diff --git a/tsconfig.json b/tsconfig.json index 9241305a80..2c3227b757 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,9 +2,7 @@ "compilerOptions": { "rootDir": ".", "paths": { - "react-native-paper": [ - "./src/index" - ] + "react-native-paper": ["./src/index"] }, "noEmit": true, "customConditions": ["react-native-paper-source"], @@ -14,10 +12,7 @@ "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "jsx": "react-jsx", - "lib": [ - "esnext", - "dom" - ], + "lib": ["esnext", "dom"], "moduleSuffixes": [".ios", ".native", ".android", ""], "module": "preserve", "moduleResolution": "bundler", @@ -35,7 +30,5 @@ "types": ["node"], "verbatimModuleSyntax": true }, - "exclude": [ - "lib/**/*" - ] + "exclude": ["lib/**/*"] } diff --git a/yarn.lock b/yarn.lock index cdea0b5acd..e74b5f06e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -398,6 +398,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.27.2, @babel/compat-data@npm:^7.27.7, @babel/compat-data@npm:^7.28.5": version: 7.28.5 resolution: "@babel/compat-data@npm:7.28.5" @@ -412,6 +423,13 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/compat-data@npm:7.29.7" + checksum: 10c0/47913f05e08a45a1c9df38c02b4b49e391005085b489432647a1abe112e5d9c75e3be8ea5972b7f6da4ec5d1339922ceb9ea02b8a25d4ed1cb8636e5261f344e + languageName: node + linkType: hard + "@babel/core@npm:7.12.9": version: 7.12.9 resolution: "@babel/core@npm:7.12.9" @@ -436,7 +454,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.16.0, @babel/core@npm:^7.18.6, @babel/core@npm:^7.19.6, @babel/core@npm:^7.20.0, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2, @babel/core@npm:^7.7.5": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.6, @babel/core@npm:^7.19.6, @babel/core@npm:^7.20.0, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2, @babel/core@npm:^7.7.5": version: 7.28.5 resolution: "@babel/core@npm:7.28.5" dependencies: @@ -459,6 +477,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.22.20": + version: 7.29.7 + resolution: "@babel/core@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helpers": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/112fb09c24de7a1de64d1de2c31fe65c4e6af4cb2fb6e6d99ea5373e6fc51e75b88581c0efae4c4c68f119a02a988c7106e95011a41530a2fb8ed793c7eaa07b + languageName: node + linkType: hard + "@babel/core@npm:^7.29.0": version: 7.29.0 resolution: "@babel/core@npm:7.29.0" @@ -482,9 +523,9 @@ __metadata: languageName: node linkType: hard -"@babel/eslint-parser@npm:^7.16.3": - version: 7.28.5 - resolution: "@babel/eslint-parser@npm:7.28.5" +"@babel/eslint-parser@npm:^7.22.15": + version: 7.29.7 + resolution: "@babel/eslint-parser@npm:7.29.7" dependencies: "@nicolo-ribaudo/eslint-scope-5-internals": "npm:5.1.1-v1" eslint-visitor-keys: "npm:^2.1.0" @@ -492,7 +533,7 @@ __metadata: peerDependencies: "@babel/core": ^7.11.0 eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - checksum: 10c0/4d13f765434b6be83ab3917f06ad712dedf0d5bfa80fe54cd6cea44adac6a0d2519020ad307d66b4490e46a435874829eac6a9fd3a9cad54d7616c47d288aaed + checksum: 10c0/c24dcd941b0a149b197a9e9c3297a01d621775ac71cdf2e1134b1097c1691b46ce969355e1be3676d83b88f62c43ed33cf3a499da21ea11a5eb07339ce64ac20 languageName: node linkType: hard @@ -522,6 +563,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/generator@npm:7.29.7" + dependencies: + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/9bf72b01b5bd0ea5b1288a0e37dbd360bff2f2b1ce73342c0d40fb3db2ec3dc004ada5ffa925c5e12939a416eed59e600d562b8ecd938ce0d27dfd0eb6c6c2b7 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.27.1, @babel/helper-annotate-as-pure@npm:^7.27.3": version: 7.27.3 resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" @@ -531,6 +585,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-annotate-as-pure@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + checksum: 10c0/c56536b52d17632d89d49db2063ed6102f0e3bbadf6a0ccb74e6599d6a77173b644c7fe8c3ef17c7a162709d55b75ee5145ef6db917d16ba7f375fbffcf2e942 + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2": version: 7.27.2 resolution: "@babel/helper-compilation-targets@npm:7.27.2" @@ -557,6 +620,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-compilation-targets@npm:7.29.7" + dependencies: + "@babel/compat-data": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/4c15fd4c69a0a7047799a28a88460c19cede0a0ee8af994ea169114986f4af48b92c7393a4a3fee0456c11a656eece3448a6ed06354453d6c27cccf17195453b + languageName: node + linkType: hard + "@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.27.1, @babel/helper-create-class-features-plugin@npm:^7.28.3, @babel/helper-create-class-features-plugin@npm:^7.28.5": version: 7.28.5 resolution: "@babel/helper-create-class-features-plugin@npm:7.28.5" @@ -641,6 +717,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-globals@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-globals@npm:7.29.7" + checksum: 10c0/f38417c40b1129a1b2b519ca961b9040c8827d1444fd74068702286b91b77089431dc76b6b9d5c1496e5da2a4f3ad329c6946e688ba3fa0d1d0b3d2b4f34f36a + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.27.1, @babel/helper-member-expression-to-functions@npm:^7.28.5": version: 7.28.5 resolution: "@babel/helper-member-expression-to-functions@npm:7.28.5" @@ -671,6 +754,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-imports@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/6adf60d97356027413342a092f818d9678c4f5caff716a33e3284b5ae14e47a9e88059d421dde4ee4894691260039a12602c0e7becadc175602194b40dfa345d + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.27.1, @babel/helper-module-transforms@npm:^7.28.3": version: 7.28.3 resolution: "@babel/helper-module-transforms@npm:7.28.3" @@ -697,6 +790,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-transforms@npm:7.29.7" + dependencies: + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/ee5a2172c24a42be696836f4b0d947489c9729d8adf5821885cf77d1ad5333e3c447368e9a71f67df1099570490553dccf9f888ef0a92a48aa63cb086bd8c7e1 + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-optimise-call-expression@npm:7.27.1" @@ -727,6 +833,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-plugin-utils@npm:7.29.7" + checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-remap-async-to-generator@npm:7.27.1" @@ -783,6 +896,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.27.1, @babel/helper-validator-identifier@npm:^7.28.5": version: 7.28.5 resolution: "@babel/helper-validator-identifier@npm:7.28.5" @@ -790,6 +910,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-validator-option@npm:7.27.1" @@ -797,6 +924,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-option@npm:7.29.7" + checksum: 10c0/d2a06c6d0ac40ba4a2f219fc2cab249c7a94bacdb2686273b7f9598571c908809b48468ff588915a346e6cc7296f60b581023d1d498b747fed06f779d335c2cc + languageName: node + linkType: hard + "@babel/helper-wrap-function@npm:^7.27.1": version: 7.28.3 resolution: "@babel/helper-wrap-function@npm:7.28.3" @@ -828,6 +962,16 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helpers@npm:7.29.7" + dependencies: + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/218e8d10953647c9f44775f5a022b227a182674853b5ea8631889deb7e1a3e4bc870388aaecf59bb8bd92a87f9a96220ed3f70a35bffec6bcf9169ecb67891ac + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.18.8, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": version: 7.28.5 resolution: "@babel/parser@npm:7.28.5" @@ -850,6 +994,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/parser@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/65133038f80b54a714d6027cb77cee3f9a6b5c4c6842ce674301e13947cbcbfa8055e63acaf1b84c085d34226a14425b2c2b97b829e0e226d2e8f1299942a51d + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.28.5": version: 7.28.5 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.28.5" @@ -1106,7 +1261,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-flow@npm:^7.12.1, @babel/plugin-syntax-flow@npm:^7.18.6, @babel/plugin-syntax-flow@npm:^7.27.1": +"@babel/plugin-syntax-flow@npm:^7.12.1, @babel/plugin-syntax-flow@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-syntax-flow@npm:7.27.1" dependencies: @@ -1117,6 +1272,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-flow@npm:^7.22.5": + version: 7.29.7 + resolution: "@babel/plugin-syntax-flow@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/b2e330dd0dc535c7364937ce2b29a06065e60b1d523db75f36ab4fb89e4ba664e2230cbb1937b35712c9a0ebafc3358f323d6e6b22247d5ebad12fdd3e1f6e98 + languageName: node + linkType: hard + "@babel/plugin-syntax-import-assertions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-syntax-import-assertions@npm:7.27.1" @@ -1216,6 +1382,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-syntax-jsx@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/1736000de183538ba8eef34520105508860e48b0c763254ba9158af5e814ed8bbceeedbb4281fbda33de787ae5b3870e92f60c6ae7131e7d322e451d57387896 + languageName: node + linkType: hard + "@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" @@ -2139,7 +2316,22 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx@npm:^7.18.10, @babel/plugin-transform-react-jsx@npm:^7.25.2, @babel/plugin-transform-react-jsx@npm:^7.27.1": +"@babel/plugin-transform-react-jsx@npm:^7.22.15": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/plugin-syntax-jsx": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/ae6487c3deafcffda8a2c1b1eb77e0b7121630fa1a9646cecd73dbbdd8271532a0f7f0e8c01f69b6d39a36d8d79eb67e2d51031369c9055f18f7d8e70d9b5446 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx@npm:^7.25.2, @babel/plugin-transform-react-jsx@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-react-jsx@npm:7.27.1" dependencies: @@ -2668,13 +2860,6 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.29.0": - version: 7.29.2 - resolution: "@babel/runtime@npm:7.29.2" - checksum: 10c0/30b80a0140d16467792e1bbeb06f655b0dab70407da38dfac7fedae9c859f9ae9d846ef14ad77bd3814c064295fe9b1bc551f1541ea14646ae9f22b71a8bc17a - languageName: node - linkType: hard - "@babel/template@npm:>=7, @babel/template@npm:^7.12.7, @babel/template@npm:^7.27.1, @babel/template@npm:^7.27.2, @babel/template@npm:^7.3.3": version: 7.27.2 resolution: "@babel/template@npm:7.27.2" @@ -2697,6 +2882,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/template@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8bb7f900dcab0e9e1c5ffbc33ca10e0d26b7b2e2ca804becb73ee771b9c4ed6e2908a4ae4a14c08560febb45d2b6b9a173955e42ad404d05f8b04840a14d9c58 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.18.8, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4, @babel/traverse@npm:^7.28.5": version: 7.28.5 resolution: "@babel/traverse@npm:7.28.5" @@ -2727,6 +2923,21 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/traverse@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + debug: "npm:^4.3.1" + checksum: 10c0/e256a1fbdb956555b76f3c285b1e453f6bedec8b3afb61751d99d933efd11c7d79caf5ddf2493570058a9f7deaa1b48324380d7c1aa1443fd9508becbf56331a + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.7, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" @@ -2747,6 +2958,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/types@npm:7.29.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/b6623994c69717fa27294f5fa46d59140338e2d86c6c1c13085c84ef7d53086ee357fbf4fe9abe3dd3da75734dc77c4c0df2f90fb29e667558bb3b3fb705e88f + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -2754,31 +2975,34 @@ __metadata: languageName: node linkType: hard -"@callstack/eslint-config@npm:^13.0.2": - version: 13.0.2 - resolution: "@callstack/eslint-config@npm:13.0.2" +"@callstack/eslint-config@npm:^15.0.0": + version: 15.0.0 + resolution: "@callstack/eslint-config@npm:15.0.0" dependencies: - "@babel/core": "npm:^7.16.0" - "@babel/eslint-parser": "npm:^7.16.3" - "@babel/plugin-syntax-flow": "npm:^7.18.6" - "@babel/plugin-transform-react-jsx": "npm:^7.18.10" - "@typescript-eslint/eslint-plugin": "npm:^5.36.2" - "@typescript-eslint/parser": "npm:^5.36.2" - eslint-config-prettier: "npm:^8.3.0" + "@babel/core": "npm:^7.22.20" + "@babel/eslint-parser": "npm:^7.22.15" + "@babel/plugin-syntax-flow": "npm:^7.22.5" + "@babel/plugin-transform-react-jsx": "npm:^7.22.15" + "@eslint/compat": "npm:^1.1.1" + "@react-native/eslint-plugin": "npm:^0.74.0 || ^0.73.0 || ^0.72.0" + "@typescript-eslint/eslint-plugin": "npm:^8.0.1" + "@typescript-eslint/parser": "npm:^8.0.1" + eslint-config-prettier: "npm:^9.0.0" eslint-plugin-flowtype: "npm:^8.0.3" - eslint-plugin-import: "npm:^2.25.3" - eslint-plugin-jest: "npm:^27.0.1" - eslint-plugin-prettier: "npm:^4.0.0" - eslint-plugin-promise: "npm:^6.0.1" - eslint-plugin-react: "npm:^7.27.1" - eslint-plugin-react-hooks: "npm:^4.3.0" - eslint-plugin-react-native: "npm:^4.0.0" - eslint-plugin-react-native-a11y: "npm:^3.2.1" + eslint-plugin-import: "npm:^2.28.1" + eslint-plugin-jest: "npm:^28.7.0" + eslint-plugin-prettier: "npm:^5.2.1" + eslint-plugin-promise: "npm:^7.1.0" + eslint-plugin-react: "npm:^7.35.0" + eslint-plugin-react-hooks: "npm:^4.6.0" + eslint-plugin-react-native: "npm:^4.1.0" + eslint-plugin-react-native-a11y: "npm:^3.4.1" eslint-restricted-globals: "npm:^0.2.0" - prettier: "npm:^2.4.1" + globals: "npm:^15.9.0" + prettier: "npm:^3.0.3" peerDependencies: eslint: ">=8.1.0" - checksum: 10c0/12daf8fe2cd96d8a1ef72a4af5b789b979036063aac02f571c5d3bcd56537db3f43be412d79a14fedba599f8fac9a3c2e5bd54244ae4b786aab0aabc18a0d5fc + checksum: 10c0/f99d38f625f00d9224519872a8a657474d044540f79de024cec0e90d2d5f3c30dff7310e8cba49740b6a15dac443af12491be4c3a00b7ae679024d2e7961826a languageName: node linkType: hard @@ -3406,7 +3630,7 @@ __metadata: languageName: node linkType: hard -"@docusaurus/theme-common@npm:2.4.3": +"@docusaurus/theme-common@npm:2.4.3, @docusaurus/theme-common@npm:^2.3.6": version: 2.4.3 resolution: "@docusaurus/theme-common@npm:2.4.3" dependencies: @@ -3636,7 +3860,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.2.0": +"@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": version: 4.9.1 resolution: "@eslint-community/eslint-utils@npm:4.9.1" dependencies: @@ -3647,27 +3871,94 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.4.0": +"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": version: 4.12.2 resolution: "@eslint-community/regexpp@npm:4.12.2" checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.4.1": +"@eslint/compat@npm:^1.1.1": version: 1.4.1 - resolution: "@eslint/eslintrc@npm:1.4.1" + resolution: "@eslint/compat@npm:1.4.1" dependencies: - ajv: "npm:^6.12.4" + "@eslint/core": "npm:^0.17.0" + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + checksum: 10c0/46f5ff884873c2e2366df55dd7b2d6b12f7f852bfba8e2a48dae4819cc5e58756deefa9b7f87f1b107af725ee883a05fcc02caf969b58fb142e790c6036a0450 + languageName: node + linkType: hard + +"@eslint/config-array@npm:^0.21.2": + version: 0.21.2 + resolution: "@eslint/config-array@npm:0.21.2" + dependencies: + "@eslint/object-schema": "npm:^2.1.7" + debug: "npm:^4.3.1" + minimatch: "npm:^3.1.5" + checksum: 10c0/89dfe815d18456177c0a1f238daf4593107fd20298b3598e0103054360d3b8d09d967defd8318f031185d68df1f95cfa68becf1390a9c5c6887665f1475142e3 + languageName: node + linkType: hard + +"@eslint/config-helpers@npm:^0.4.2": + version: 0.4.2 + resolution: "@eslint/config-helpers@npm:0.4.2" + dependencies: + "@eslint/core": "npm:^0.17.0" + checksum: 10c0/92efd7a527b2d17eb1a148409d71d80f9ac160b565ac73ee092252e8bf08ecd08670699f46b306b94f13d22e88ac88a612120e7847570dd7cdc72f234d50dcb4 + languageName: node + linkType: hard + +"@eslint/core@npm:^0.17.0": + version: 0.17.0 + resolution: "@eslint/core@npm:0.17.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10c0/9a580f2246633bc752298e7440dd942ec421860d1946d0801f0423830e67887e4aeba10ab9a23d281727a978eb93d053d1922a587d502942a713607f40ed704e + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^3.3.5": + version: 3.3.5 + resolution: "@eslint/eslintrc@npm:3.3.5" + dependencies: + ajv: "npm:^6.14.0" debug: "npm:^4.3.2" - espree: "npm:^9.4.0" - globals: "npm:^13.19.0" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" ignore: "npm:^5.2.0" import-fresh: "npm:^3.2.1" - js-yaml: "npm:^4.1.0" - minimatch: "npm:^3.1.2" + js-yaml: "npm:^4.1.1" + minimatch: "npm:^3.1.5" strip-json-comments: "npm:^3.1.1" - checksum: 10c0/1030e1a4a355f8e4629e19d3d45448a05a8e65ecf49154bebc66599d038f155e830498437cbfc7246e8084adc1f814904f696c2461707cc8c73be961e2e8ae5a + checksum: 10c0/9fb9f1ca65e46d6173966e3aaa5bd353e3a65d7f1f582bebf77f578fab7d7960a399fac1ecfb1e7d52bd61f5cefd6531087ca52a3a3c388f2e1b4f1ebd3da8b7 + languageName: node + linkType: hard + +"@eslint/js@npm:9.39.4": + version: 9.39.4 + resolution: "@eslint/js@npm:9.39.4" + checksum: 10c0/5aa7dea2cbc5decf7f5e3b0c6f86a084ccee0f792d288ca8e839f8bc1b64e03e227068968e49b26096e6f71fd857ab6e42691d1b993826b9a3883f1bdd7a0e46 + languageName: node + linkType: hard + +"@eslint/object-schema@npm:^2.1.7": + version: 2.1.7 + resolution: "@eslint/object-schema@npm:2.1.7" + checksum: 10c0/936b6e499853d1335803f556d526c86f5fe2259ed241bc665000e1d6353828edd913feed43120d150adb75570cae162cf000b5b0dfc9596726761c36b82f4e87 + languageName: node + linkType: hard + +"@eslint/plugin-kit@npm:^0.4.1": + version: 0.4.1 + resolution: "@eslint/plugin-kit@npm:0.4.1" + dependencies: + "@eslint/core": "npm:^0.17.0" + levn: "npm:^0.4.1" + checksum: 10c0/51600f78b798f172a9915dffb295e2ffb44840d583427bc732baf12ecb963eb841b253300e657da91d890f4b323d10a1bd12934bf293e3018d8bb66fdce5217b languageName: node linkType: hard @@ -3777,6 +4068,27 @@ __metadata: languageName: node linkType: hard +"@expo/config-plugins@npm:~56.0.8": + version: 56.0.9 + resolution: "@expo/config-plugins@npm:56.0.9" + dependencies: + "@expo/config-types": "npm:^56.0.6" + "@expo/json-file": "npm:~10.2.0" + "@expo/plist": "npm:^0.7.0" + "@expo/require-utils": "npm:^56.1.3" + "@expo/sdk-runtime-versions": "npm:^1.0.0" + chalk: "npm:^4.1.2" + debug: "npm:^4.3.5" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + semver: "npm:^7.5.4" + slugify: "npm:^1.6.6" + xcode: "npm:^3.0.1" + xml2js: "npm:0.6.0" + checksum: 10c0/401be0071d0bdc1e86431a8dd2b19dfa117ad04b2c58c583b62a5ab5ebb93be3eed26857bdbec9433cb9f554771dda32ea2b7aa5aabf45b8037e6ce7cd705dee + languageName: node + linkType: hard + "@expo/config-types@npm:^56.0.5": version: 56.0.5 resolution: "@expo/config-types@npm:56.0.5" @@ -3784,6 +4096,13 @@ __metadata: languageName: node linkType: hard +"@expo/config-types@npm:^56.0.6": + version: 56.0.6 + resolution: "@expo/config-types@npm:56.0.6" + checksum: 10c0/fa5d8b6a755a5ffa4ee4a138e7a3273f2f3fb7d1a84c4986ecc804f40656e18da89209cf85f3f93d4b3ca730c1a2688e5dc620a1ffd20dc9781834f3b426853f + languageName: node + linkType: hard + "@expo/config@npm:~56.0.8": version: 56.0.8 resolution: "@expo/config@npm:56.0.8" @@ -3802,6 +4121,24 @@ __metadata: languageName: node linkType: hard +"@expo/config@npm:~56.0.9": + version: 56.0.9 + resolution: "@expo/config@npm:56.0.9" + dependencies: + "@expo/config-plugins": "npm:~56.0.8" + "@expo/config-types": "npm:^56.0.5" + "@expo/json-file": "npm:^10.2.0" + "@expo/require-utils": "npm:^56.1.3" + deepmerge: "npm:^4.3.1" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + resolve-workspace-root: "npm:^2.0.0" + semver: "npm:^7.6.0" + slugify: "npm:^1.3.4" + checksum: 10c0/ef643ee063229e1cfba67998f2fd63c5e75781110af0bef99e8730e541fe5599cc8616c571d03918f2b21988652d108e6d0116b8a0d0599f21e7988d3e59f9c4 + languageName: node + linkType: hard + "@expo/devcert@npm:^1.2.1": version: 1.2.1 resolution: "@expo/devcert@npm:1.2.1" @@ -3939,6 +4276,42 @@ __metadata: languageName: node linkType: hard +"@expo/metro-config@npm:~56.0.0": + version: 56.0.14 + resolution: "@expo/metro-config@npm:56.0.14" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + "@babel/core": "npm:^7.20.0" + "@babel/generator": "npm:^7.20.5" + "@expo/config": "npm:~56.0.9" + "@expo/env": "npm:~2.3.0" + "@expo/json-file": "npm:~10.2.0" + "@expo/metro": "npm:~56.0.0" + "@expo/require-utils": "npm:^56.1.3" + "@expo/spawn-async": "npm:^1.8.0" + "@jridgewell/gen-mapping": "npm:^0.3.13" + "@jridgewell/remapping": "npm:^2.3.5" + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + browserslist: "npm:^4.25.0" + chalk: "npm:^4.1.0" + debug: "npm:^4.3.2" + getenv: "npm:^2.0.0" + glob: "npm:^13.0.0" + hermes-parser: "npm:^0.33.3" + jsc-safe-url: "npm:^0.2.4" + lightningcss: "npm:^1.30.1" + picomatch: "npm:^4.0.4" + postcss: "npm:^8.5.14" + resolve-from: "npm:^5.0.0" + peerDependencies: + expo: "*" + peerDependenciesMeta: + expo: + optional: true + checksum: 10c0/3abbcd28d746be0afd21dfbf9be21ecea8471fded8df98bd381bf16c555c21a11617deda38b2586856a09eec1dd9f5edcea04a24ddcf7fca734fd3da1373e9db + languageName: node + linkType: hard + "@expo/metro-config@npm:~56.0.11": version: 56.0.11 resolution: "@expo/metro-config@npm:56.0.11" @@ -4079,6 +4452,22 @@ __metadata: languageName: node linkType: hard +"@expo/require-utils@npm:^56.1.3": + version: 56.1.3 + resolution: "@expo/require-utils@npm:56.1.3" + dependencies: + "@babel/code-frame": "npm:^7.20.0" + "@babel/core": "npm:^7.25.2" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/b0fc61db4e45507508bf01524f42770a5bf1f53fab9b244a7dc6c9d8acce0e912940d9945716b8f0c747a07262d34128150c78d00bbe996b2efc5d522c45d874 + languageName: node + linkType: hard + "@expo/router-server@npm:^56.0.10": version: 56.0.10 resolution: "@expo/router-server@npm:56.0.10" @@ -4173,14 +4562,30 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.8": - version: 0.11.14 - resolution: "@humanwhocodes/config-array@npm:0.11.14" +"@humanfs/core@npm:^0.19.2": + version: 0.19.2 + resolution: "@humanfs/core@npm:0.19.2" dependencies: - "@humanwhocodes/object-schema": "npm:^2.0.2" - debug: "npm:^4.3.1" - minimatch: "npm:^3.0.5" - checksum: 10c0/66f725b4ee5fdd8322c737cb5013e19fac72d4d69c8bf4b7feb192fcb83442b035b92186f8e9497c220e58b2d51a080f28a73f7899bc1ab288c3be172c467541 + "@humanfs/types": "npm:^0.15.0" + checksum: 10c0/d0a1d52d7b30c27d49475a53072d1510b81c5803e44b342fb8faf3887f1aa27593a1e6dc76a45268e7892d3f4e198146659281f6b6d55eacf3fd5a38bac30c5c + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.6": + version: 0.16.8 + resolution: "@humanfs/node@npm:0.16.8" + dependencies: + "@humanfs/core": "npm:^0.19.2" + "@humanfs/types": "npm:^0.15.0" + "@humanwhocodes/retry": "npm:^0.4.0" + checksum: 10c0/56140579db811af4e160b195d45d0f29acf644d192c93fe24c9e594ebf06f19dfc157494a07c84540b8a071c0e4b37209c2362765d31734f4d0be869c2422e25 + languageName: node + linkType: hard + +"@humanfs/types@npm:^0.15.0": + version: 0.15.0 + resolution: "@humanfs/types@npm:0.15.0" + checksum: 10c0/fc26b9a024b0e55f7eaf64036df94345bf5d36d6a41ef80ef38e78f1f7430ce26cf435af736adae58913baae18eac3f38c18739054a3d379102015978eae862e languageName: node linkType: hard @@ -4191,10 +4596,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.2": - version: 2.0.3 - resolution: "@humanwhocodes/object-schema@npm:2.0.3" - checksum: 10c0/80520eabbfc2d32fe195a93557cef50dfe8c8905de447f022675aaf66abc33ae54098f5ea78548d925aa671cd4ab7c7daa5ad704fe42358c9b5e7db60f80696c +"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2": + version: 0.4.3 + resolution: "@humanwhocodes/retry@npm:0.4.3" + checksum: 10c0/3775bb30087d4440b3f7406d5a057777d90e4b9f435af488a4923ef249e93615fb78565a85f173a186a076c7706a81d0d57d563a2624e4de2c5c9c66c486ce42 languageName: node linkType: hard @@ -4337,6 +4742,13 @@ __metadata: languageName: node linkType: hard +"@jest/diff-sequences@npm:30.4.0": + version: 30.4.0 + resolution: "@jest/diff-sequences@npm:30.4.0" + checksum: 10c0/b4358b1b885098b905cb777f58788ddd45f90c4ebc3ce2c04fb1d4c9516f35ac2d9daef8263cd21c537bd7a52ab320f03e4ba9521677959ae20e3d405356b420 + languageName: node + linkType: hard + "@jest/environment@npm:^29.7.0": version: 29.7.0 resolution: "@jest/environment@npm:29.7.0" @@ -4382,6 +4794,13 @@ __metadata: languageName: node linkType: hard +"@jest/get-type@npm:30.1.0": + version: 30.1.0 + resolution: "@jest/get-type@npm:30.1.0" + checksum: 10c0/3e65fd5015f551c51ec68fca31bbd25b466be0e8ee8075d9610fa1c686ea1e70a942a0effc7b10f4ea9a338c24337e1ad97ff69d3ebacc4681b7e3e80d1b24ac + languageName: node + linkType: hard + "@jest/globals@npm:^29.7.0": version: 29.7.0 resolution: "@jest/globals@npm:29.7.0" @@ -4431,6 +4850,15 @@ __metadata: languageName: node linkType: hard +"@jest/schemas@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/schemas@npm:30.4.1" + dependencies: + "@sinclair/typebox": "npm:^0.34.0" + checksum: 10c0/96f388ebfc1974457fcbde2ad36c40a0b549cba3f624fe8d9d6e5903a152dc75e4043f4ac9ac7668622f2ecb0f9a4dcb9a38edf3bc0d52b82045b2bb2b69b72a + languageName: node + linkType: hard + "@jest/schemas@npm:^29.6.3": version: 29.6.3 resolution: "@jest/schemas@npm:29.6.3" @@ -4904,7 +5332,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": +"@nodelib/fs.walk@npm:^1.2.3": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -5067,6 +5495,13 @@ __metadata: languageName: node linkType: hard +"@pkgr/core@npm:^0.3.6": + version: 0.3.6 + resolution: "@pkgr/core@npm:0.3.6" + checksum: 10c0/153f0f4563f505faeba13c733efa0e05e467ce1c6b941055a5fd3b4560da60fbf1dff4b11da0075f034ddda11f2842b90395f60895dde5825875b616edccc11c + languageName: node + linkType: hard + "@polka/url@npm:^1.0.0-next.24": version: 1.0.0-next.29 resolution: "@polka/url@npm:1.0.0-next.29" @@ -5265,6 +5700,13 @@ __metadata: languageName: node linkType: hard +"@react-native/eslint-plugin@npm:^0.74.0 || ^0.73.0 || ^0.72.0": + version: 0.74.89 + resolution: "@react-native/eslint-plugin@npm:0.74.89" + checksum: 10c0/7e24c13d9869320b011bcef7901b080438d6a182720135f443c62dd565ee8e8b57e8bb709fee86095f5956cdaeb45a96a4c2334e22b048b20eb8bdea12cfd34d + languageName: node + linkType: hard + "@react-native/gradle-plugin@npm:0.85.3": version: 0.85.3 resolution: "@react-native/gradle-plugin@npm:0.85.3" @@ -5378,7 +5820,7 @@ __metadata: languageName: node linkType: hard -"@react-navigation/elements@npm:^3.0.0-alpha.31": +"@react-navigation/elements@npm:3.0.0-alpha.31, @react-navigation/elements@npm:^3.0.0-alpha.31": version: 3.0.0-alpha.31 resolution: "@react-navigation/elements@npm:3.0.0-alpha.31" dependencies: @@ -5488,6 +5930,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.34.0": + version: 0.34.49 + resolution: "@sinclair/typebox@npm:0.34.49" + checksum: 10c0/16b7d87f039a49b68c10bb4cdcae2ce5242b2472228851fd6483731616aba4ef977690aa517b230a8d20da8185bb416eb34e326f30568b3963c1cf26b05d1ad8 + languageName: node + linkType: hard + "@sindresorhus/is@npm:^0.14.0": version: 0.14.0 resolution: "@sindresorhus/is@npm:0.14.0" @@ -5712,37 +6161,23 @@ __metadata: languageName: node linkType: hard -"@testing-library/jest-native@npm:^5.4.1": - version: 5.4.3 - resolution: "@testing-library/jest-native@npm:5.4.3" +"@testing-library/react-native@npm:^14.0.0": + version: 14.0.0 + resolution: "@testing-library/react-native@npm:14.0.0" dependencies: - chalk: "npm:^4.1.2" - jest-diff: "npm:^29.0.1" - jest-matcher-utils: "npm:^29.0.1" - pretty-format: "npm:^29.0.3" + jest-matcher-utils: "npm:^30.4.1" + picocolors: "npm:^1.1.1" + pretty-format: "npm:^30.4.1" redent: "npm:^3.0.0" peerDependencies: - react: ">=16.0.0" - react-native: ">=0.59" - react-test-renderer: ">=16.0.0" - checksum: 10c0/9315989dc0377a778f7ec9317b98c1dab9a7e2ebe0ca20a5c75f9dba3a4b486c02452b4399dd1aeef9ebf0007247594c6357f62089f095fde5280ce0344f5e65 - languageName: node - linkType: hard - -"@testing-library/react-native@npm:11.5.0": - version: 11.5.0 - resolution: "@testing-library/react-native@npm:11.5.0" - dependencies: - pretty-format: "npm:^29.0.0" - peerDependencies: - jest: ">=28.0.0" - react: ">=16.0.0" - react-native: ">=0.59" - react-test-renderer: ">=16.0.0" + jest: ">=29.0.0" + react: ">=19.0.0" + react-native: ">=0.78" + test-renderer: ^1.0.0 peerDependenciesMeta: jest: optional: true - checksum: 10c0/2b5a1c18fa69d6b38b937b70a60fb300db4c88d0c73205fb84f61075819075937715f029b3e623cdd88e752460aaf8b65d3f716ae1209d8b8cff5ec6501ebc98 + checksum: 10c0/1c19b1d8e1b66757b38904ddddb54c24185b4969faafcbd6b3888841f8597c4a1d87fbff68f3a950bcf051b2ef16adbdb395f44aad1cbc4ba7eaeca900ffc984 languageName: node linkType: hard @@ -5912,6 +6347,13 @@ __metadata: languageName: node linkType: hard +"@types/estree@npm:^1.0.6": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^5.0.0": version: 5.1.0 resolution: "@types/express-serve-static-core@npm:5.1.0" @@ -6056,16 +6498,6 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:^29.2.1": - version: 29.5.14 - resolution: "@types/jest@npm:29.5.14" - dependencies: - expect: "npm:^29.0.0" - pretty-format: "npm:^29.0.0" - checksum: 10c0/18e0712d818890db8a8dab3d91e9ea9f7f19e3f83c2e50b312f557017dc81466207a71f3ed79cf4428e813ba939954fa26ffa0a9a7f153181ba174581b1c2aed - languageName: node - linkType: hard - "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -6137,13 +6569,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^13.1.0": - version: 13.13.52 - resolution: "@types/node@npm:13.13.52" - checksum: 10c0/9224aaea219fe7f8b903039bb146d4c38b391d0166a8226a1efa2d43a35b5b7dddfe021c32941bb454a1543da94921c112687934da88e2a019f4bd44f9d517b5 - languageName: node - linkType: hard - "@types/node@npm:^17.0.5": version: 17.0.45 resolution: "@types/node@npm:17.0.45" @@ -6151,6 +6576,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^24.0.0": + version: 24.13.2 + resolution: "@types/node@npm:24.13.2" + dependencies: + undici-types: "npm:~7.18.0" + checksum: 10c0/d7d48a88a4feb0a6aac3cbfaf9ef3b12752b4b09447f88dd0b4c77c03b281e3d4330fe6982a99aedcd63fc16c7540a0c248b91eb2abb0b3edd884d7fe684e9ea + languageName: node + linkType: hard + "@types/normalize-package-data@npm:^2.4.0": version: 2.4.4 resolution: "@types/normalize-package-data@npm:2.4.4" @@ -6197,31 +6631,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^19.1.1": - version: 19.2.3 - resolution: "@types/react-dom@npm:19.2.3" +"@types/react-reconciler@npm:~0.33.0": + version: 0.33.0 + resolution: "@types/react-reconciler@npm:0.33.0" peerDependencies: - "@types/react": ^19.2.0 - checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 - languageName: node - linkType: hard - -"@types/react-native-vector-icons@npm:^6.4.18": - version: 6.4.18 - resolution: "@types/react-native-vector-icons@npm:6.4.18" - dependencies: - "@types/react": "npm:*" - "@types/react-native": "npm:^0.70" - checksum: 10c0/b27f2295c5baaba9ce2ff9ade6e505e4d1fe4f40a628df13dc26d0e1b3ccee9775846f3613a443d91cfe86844703b81e9e64a10cc4b7fac1edfde96cb82c893b - languageName: node - linkType: hard - -"@types/react-native@npm:^0.70": - version: 0.70.19 - resolution: "@types/react-native@npm:0.70.19" - dependencies: - "@types/react": "npm:*" - checksum: 10c0/09a5191dd0f3b85abfa8939df585860ba11a666d888846c7fd7d8d5d1cd72a39224e154403f90a5b82f1dc6c896fa3447e4f5061b2d3e102d0ce798e559d7f13 + "@types/react": "*" + checksum: 10c0/190c203d93c0df9a42fabd693ce059dbdf6c53e15eb14502d9e5b946c981231c5846b867de15522ff61368e9218a8508a9db5476f3e47b5d664bbb2c84b31ac7 languageName: node linkType: hard @@ -6275,6 +6690,15 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^19.2.7": + version: 19.2.17 + resolution: "@types/react@npm:19.2.17" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/bc2c4af96b3e480604424de70d5ebda90c5f4b485df471858c0bc2d7d70364b606ec3c4d8579f94f01aa0c6c0591f56bcf14cba5689f5eea4b74250ccdc3a232 + languageName: node + linkType: hard + "@types/reactcss@npm:*": version: 1.2.13 resolution: "@types/reactcss@npm:1.2.13" @@ -6309,13 +6733,6 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12": - version: 7.7.1 - resolution: "@types/semver@npm:7.7.1" - checksum: 10c0/c938aef3bf79a73f0f3f6037c16e2e759ff40c54122ddf0b2583703393d8d3127130823facb880e694caa324eb6845628186aac1997ee8b31dc2d18fafe26268 - languageName: node - linkType: hard - "@types/send@npm:*": version: 1.2.1 resolution: "@types/send@npm:1.2.1" @@ -6450,124 +6867,138 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.36.2, @typescript-eslint/eslint-plugin@npm:^5.41.0": - version: 5.62.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" +"@typescript-eslint/eslint-plugin@npm:8.61.1, @typescript-eslint/eslint-plugin@npm:^8.0.1": + version: 8.61.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.61.1" dependencies: - "@eslint-community/regexpp": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/type-utils": "npm:5.62.0" - "@typescript-eslint/utils": "npm:5.62.0" - debug: "npm:^4.3.4" - graphemer: "npm:^1.4.0" - ignore: "npm:^5.2.0" - natural-compare-lite: "npm:^1.4.0" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" + "@eslint-community/regexpp": "npm:^4.12.2" + "@typescript-eslint/scope-manager": "npm:8.61.1" + "@typescript-eslint/type-utils": "npm:8.61.1" + "@typescript-eslint/utils": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" + ignore: "npm:^7.0.5" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^2.5.0" peerDependencies: - "@typescript-eslint/parser": ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/3f40cb6bab5a2833c3544e4621b9fdacd8ea53420cadc1c63fac3b89cdf5c62be1e6b7bcf56976dede5db4c43830de298ced3db60b5494a3b961ca1b4bff9f2a + "@typescript-eslint/parser": ^8.61.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/3cb445622907283fb23e78f8bde4d1b04cca96da181a0c549b2775954c5dfa59f20e34c27f73ae3e189420e36637f59145bd97ce45c55017a6c3ac1871197951 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.36.2, @typescript-eslint/parser@npm:^5.41.0": - version: 5.62.0 - resolution: "@typescript-eslint/parser@npm:5.62.0" +"@typescript-eslint/parser@npm:8.61.1, @typescript-eslint/parser@npm:^8.0.1": + version: 8.61.1 + resolution: "@typescript-eslint/parser@npm:8.61.1" dependencies: - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - debug: "npm:^4.3.4" + "@typescript-eslint/scope-manager": "npm:8.61.1" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" + debug: "npm:^4.4.3" peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/315194b3bf39beb9bd16c190956c46beec64b8371e18d6bb72002108b250983eb1e186a01d34b77eb4045f4941acbb243b16155fbb46881105f65e37dc9e24d4 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/6515afed5df24fd56fd0c96f94b2fc951236fc805e5d5ec925cbcae1319e5a41caea44284cd35b21f5ce7b812e567185c9dfe0882607135b947895509f23b253 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/scope-manager@npm:5.62.0" +"@typescript-eslint/project-service@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/project-service@npm:8.61.1" dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - checksum: 10c0/861253235576c1c5c1772d23cdce1418c2da2618a479a7de4f6114a12a7ca853011a1e530525d0931c355a8fd237b9cd828fac560f85f9623e24054fd024726f + "@typescript-eslint/tsconfig-utils": "npm:^8.61.1" + "@typescript-eslint/types": "npm:^8.61.1" + debug: "npm:^4.4.3" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/4ced4f96cbe6b4b1f1f53d02e0e5d1efcb6ca02316d8ea11f9b328f7b3783a76e59b72ebbe233a00452de7466ac176f9836afe8a99c63acc94e8e2d1d31d370b languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/type-utils@npm:5.62.0" +"@typescript-eslint/scope-manager@npm:8.61.1, @typescript-eslint/scope-manager@npm:^8.56.0": + version: 8.61.1 + resolution: "@typescript-eslint/scope-manager@npm:8.61.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:5.62.0" - "@typescript-eslint/utils": "npm:5.62.0" - debug: "npm:^4.3.4" - tsutils: "npm:^3.21.0" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" + checksum: 10c0/8558b56629acc28da1b90a8254eaf9862a38bf49c0a04680c767ea542f49d683c8c60c43676e0348491d1eac9af25cc67abe0a1bb3cfe90add42523faf48b029 + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.61.1, @typescript-eslint/tsconfig-utils@npm:^8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.61.1" peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/93112e34026069a48f0484b98caca1c89d9707842afe14e08e7390af51cdde87378df29d213d3bbd10a7cfe6f91b228031b56218515ce077bdb62ddea9d9f474 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/ab732f3c329f0ee8ea9e7d6c7c7d91b508cc0cf6c48c17d95e5df348d3e475730bd7ce63ac1f90260c80a9339a8a6f2eec8f2a35e1793e956a447130291bb5e3 languageName: node linkType: hard -"@typescript-eslint/types@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/types@npm:5.62.0" - checksum: 10c0/7febd3a7f0701c0b927e094f02e82d8ee2cada2b186fcb938bc2b94ff6fbad88237afc304cbaf33e82797078bbbb1baf91475f6400912f8b64c89be79bfa4ddf +"@typescript-eslint/type-utils@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/type-utils@npm:8.61.1" + dependencies: + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" + "@typescript-eslint/utils": "npm:8.61.1" + debug: "npm:^4.4.3" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/c6d112e80e82de3ad5a4f8a875c5caf267fa856df2eb97e3ef945de7b62ad07eff02e1dd9e8943cc2e1388180d4f31f1d97e0300d2e5e662245e322205af67dc + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.61.1, @typescript-eslint/types@npm:^8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/types@npm:8.61.1" + checksum: 10c0/165c3f0d3f4e1d04b310fe2a918c1012d037a0f0913895096eed40895fa72638e414a69e36182fc1bcc78efb9a4b7d81654b8768d8f1c3f43f3f2792694dfa49 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" +"@typescript-eslint/typescript-estree@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.61.1" dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/d7984a3e9d56897b2481940ec803cb8e7ead03df8d9cfd9797350be82ff765dfcf3cfec04e7355e1779e948da8f02bc5e11719d07a596eb1cb995c48a95e38cf + "@typescript-eslint/project-service": "npm:8.61.1" + "@typescript-eslint/tsconfig-utils": "npm:8.61.1" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" + debug: "npm:^4.4.3" + minimatch: "npm:^10.2.2" + semver: "npm:^7.7.3" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/cc3f15e8e30dee0522e9f8b5879824cfe3d98aad6a64e863bf0a21c26eedb3b0e5c82c2b73d59f9d5bc1b66f67305132c7c71f11a542e04e152f92b58599b358 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.62.0, @typescript-eslint/utils@npm:^5.10.0": - version: 5.62.0 - resolution: "@typescript-eslint/utils@npm:5.62.0" +"@typescript-eslint/utils@npm:8.61.1, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/utils@npm:^8.56.0": + version: 8.61.1 + resolution: "@typescript-eslint/utils@npm:8.61.1" dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@types/json-schema": "npm:^7.0.9" - "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - eslint-scope: "npm:^5.1.1" - semver: "npm:^7.3.7" + "@eslint-community/eslint-utils": "npm:^4.9.1" + "@typescript-eslint/scope-manager": "npm:8.61.1" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 10c0/f09b7d9952e4a205eb1ced31d7684dd55cee40bf8c2d78e923aa8a255318d97279825733902742c09d8690f37a50243f4c4d383ab16bd7aefaf9c4b438f785e1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/afb5e6f39da3ee34c11cfc73a64045a0e5dadd74ae2a2fcf01d9494e3be00c849a3b9ca8b23570f596726611f00598a198d66cd82e3753f63b8bde69ead0e507 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" +"@typescript-eslint/visitor-keys@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.61.1" dependencies: - "@typescript-eslint/types": "npm:5.62.0" - eslint-visitor-keys: "npm:^3.3.0" - checksum: 10c0/7c3b8e4148e9b94d9b7162a596a1260d7a3efc4e65199693b8025c71c4652b8042501c0bc9f57654c1e2943c26da98c0f77884a746c6ae81389fcb0b513d995d + "@typescript-eslint/types": "npm:8.61.1" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10c0/fa2075b891076fe640cfc36fd68299985f35c9b6782ff35cf231c1418e38dba56b28412bc6fced7fb8fabbd91444a02643f62e1ebe424aa45a885b8a8e60fbfd languageName: node linkType: hard @@ -6832,7 +7263,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.9.0": +"acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.15.0": version: 8.15.0 resolution: "acorn@npm:8.15.0" bin: @@ -6906,7 +7337,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5": +"ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -6918,6 +7349,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^6.14.0": + version: 6.15.0 + resolution: "ajv@npm:6.15.0" + dependencies: + fast-deep-equal: "npm:^3.1.1" + fast-json-stable-stringify: "npm:^2.0.0" + json-schema-traverse: "npm:^0.4.1" + uri-js: "npm:^4.2.2" + checksum: 10c0/67966499dd272ecde1c2e467084411132891523d057487587879d39ac04207f4351b7b2324c83198013967fbfa632c1612adc960114a30770fbe07a0773b32c2 + languageName: node + linkType: hard + "ajv@npm:^8.0.0, ajv@npm:^8.9.0": version: 8.17.1 resolution: "ajv@npm:8.17.1" @@ -7044,13 +7487,6 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^2.0.0": - version: 2.1.1 - resolution: "ansi-regex@npm:2.1.1" - checksum: 10c0/78cebaf50bce2cb96341a7230adf28d804611da3ce6bf338efa7b72f06cc6ff648e29f80cd95e582617ba58d5fdbec38abfeed3500a98bce8381a9daec7c548b - languageName: node - linkType: hard - "ansi-regex@npm:^4.0.0, ansi-regex@npm:^4.1.0": version: 4.1.1 resolution: "ansi-regex@npm:4.1.1" @@ -7072,13 +7508,6 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^2.2.1": - version: 2.2.1 - resolution: "ansi-styles@npm:2.2.1" - checksum: 10c0/7c68aed4f1857389e7a12f85537ea5b40d832656babbf511cc7ecd9efc52889b9c3e5653a71a6aade783c3c5e0aa223ad4ff8e83c27ac8a666514e6c79068cab - languageName: node - linkType: hard - "ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -7097,7 +7526,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^5.0.0": +"ansi-styles@npm:^5.0.0, ansi-styles@npm:^5.2.0": version: 5.2.0 resolution: "ansi-styles@npm:5.2.0" checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df @@ -7111,16 +7540,6 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:^1.3.0": - version: 1.3.2 - resolution: "anymatch@npm:1.3.2" - dependencies: - micromatch: "npm:^2.1.5" - normalize-path: "npm:^2.0.0" - checksum: 10c0/aa1eae8ef5076cfecefef1983811b4666b365513d60dfcb30756556cc7e8547fae2654328509beedb812b211da4785df5d42ca720aa24d52e745509ad3a4b2a8 - languageName: node - linkType: hard - "anymatch@npm:^2.0.0": version: 2.0.0 resolution: "anymatch@npm:2.0.0" @@ -7191,15 +7610,6 @@ __metadata: languageName: node linkType: hard -"arr-diff@npm:^2.0.0": - version: 2.0.0 - resolution: "arr-diff@npm:2.0.0" - dependencies: - arr-flatten: "npm:^1.0.1" - checksum: 10c0/d79592bf2b621b9c038e7a697357174409fceb63658529ea3b2d2d53a2918160e6bebb2e6ae756eb53330f07c11b052752377905d743a8928f9d3858598cafa2 - languageName: node - linkType: hard - "arr-diff@npm:^4.0.0": version: 4.0.0 resolution: "arr-diff@npm:4.0.0" @@ -7207,7 +7617,7 @@ __metadata: languageName: node linkType: hard -"arr-flatten@npm:^1.0.1, arr-flatten@npm:^1.1.0": +"arr-flatten@npm:^1.1.0": version: 1.1.0 resolution: "arr-flatten@npm:1.1.0" checksum: 10c0/bef53be02ed3bc58f202b3861a5b1eb6e1ae4fecf39c3ad4d15b1e0433f941077d16e019a33312d820844b0661777322acbb7d0c447b04d9bdf7d6f9c532548a @@ -7275,13 +7685,6 @@ __metadata: languageName: node linkType: hard -"array-unique@npm:^0.2.1": - version: 0.2.1 - resolution: "array-unique@npm:0.2.1" - checksum: 10c0/e72f4c45a432b44f9785b24bb5742648ed68f074a74f7bcf65b3f47630cd6aea05e532ab921f1a5f57266512a02183440b42f683dab95636bb81c8d6e2758641 - languageName: node - linkType: hard - "array-unique@npm:^0.3.2": version: 0.3.2 resolution: "array-unique@npm:0.3.2" @@ -7407,13 +7810,6 @@ __metadata: languageName: node linkType: hard -"async-each@npm:^1.0.0": - version: 1.0.6 - resolution: "async-each@npm:1.0.6" - checksum: 10c0/d4e45e8f077e20e015952c065ceae75f82b30ee2d4a8e56a5c454ae44331aaa009d8c94fe043ba254c177bffae9f6ebeefebb7daf9f7ce4d27fac0274dc328ae - languageName: node - linkType: hard - "async-function@npm:^1.0.0": version: 1.0.0 resolution: "async-function@npm:1.0.0" @@ -7502,110 +7898,6 @@ __metadata: languageName: node linkType: hard -"babel-cli@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-cli@npm:6.26.0" - dependencies: - babel-core: "npm:^6.26.0" - babel-polyfill: "npm:^6.26.0" - babel-register: "npm:^6.26.0" - babel-runtime: "npm:^6.26.0" - chokidar: "npm:^1.6.1" - commander: "npm:^2.11.0" - convert-source-map: "npm:^1.5.0" - fs-readdir-recursive: "npm:^1.0.0" - glob: "npm:^7.1.2" - lodash: "npm:^4.17.4" - output-file-sync: "npm:^1.1.2" - path-is-absolute: "npm:^1.0.1" - slash: "npm:^1.0.0" - source-map: "npm:^0.5.6" - v8flags: "npm:^2.1.1" - dependenciesMeta: - chokidar: - optional: true - bin: - babel: ./bin/babel.js - babel-doctor: ./bin/babel-doctor.js - babel-external-helpers: ./bin/babel-external-helpers.js - babel-node: ./bin/babel-node.js - checksum: 10c0/8b83a100a5e7ea121141c7305dba06d9acf607f545687b9d331adc881beec2ed6ce2a0b0d8d4e75a4d3138eee82a26922841c75f6ff2f94db42331b513008de7 - languageName: node - linkType: hard - -"babel-code-frame@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-code-frame@npm:6.26.0" - dependencies: - chalk: "npm:^1.1.3" - esutils: "npm:^2.0.2" - js-tokens: "npm:^3.0.2" - checksum: 10c0/7fecc128e87578cf1b96e78d2b25e0b260e202bdbbfcefa2eac23b7f8b7b2f7bc9276a14599cde14403cc798cc2a38e428e2cab50b77658ab49228b09ae92473 - languageName: node - linkType: hard - -"babel-core@npm:^6.26.0": - version: 6.26.3 - resolution: "babel-core@npm:6.26.3" - dependencies: - babel-code-frame: "npm:^6.26.0" - babel-generator: "npm:^6.26.0" - babel-helpers: "npm:^6.24.1" - babel-messages: "npm:^6.23.0" - babel-register: "npm:^6.26.0" - babel-runtime: "npm:^6.26.0" - babel-template: "npm:^6.26.0" - babel-traverse: "npm:^6.26.0" - babel-types: "npm:^6.26.0" - babylon: "npm:^6.18.0" - convert-source-map: "npm:^1.5.1" - debug: "npm:^2.6.9" - json5: "npm:^0.5.1" - lodash: "npm:^4.17.4" - minimatch: "npm:^3.0.4" - path-is-absolute: "npm:^1.0.1" - private: "npm:^0.1.8" - slash: "npm:^1.0.0" - source-map: "npm:^0.5.7" - checksum: 10c0/10292649779f8c33d1908f5671c92ca9df036c9e1b9f35f97e7f62c9da9e3a146ee069f94fc401283ce129ba980f34a30339f137c512f3e62ddd354653b2da0e - languageName: node - linkType: hard - -"babel-core@npm:^7.0.0-bridge.0": - version: 7.0.0-bridge.0 - resolution: "babel-core@npm:7.0.0-bridge.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/f57576e30267be4607d163b7288031d332cf9200ea35efe9fb33c97f834e304376774c28c1f9d6928d6733fcde7041e4010f1248a0519e7730c590d4b07b9608 - languageName: node - linkType: hard - -"babel-generator@npm:^6.26.0": - version: 6.26.1 - resolution: "babel-generator@npm:6.26.1" - dependencies: - babel-messages: "npm:^6.23.0" - babel-runtime: "npm:^6.26.0" - babel-types: "npm:^6.26.0" - detect-indent: "npm:^4.0.0" - jsesc: "npm:^1.3.0" - lodash: "npm:^4.17.4" - source-map: "npm:^0.5.7" - trim-right: "npm:^1.0.1" - checksum: 10c0/d5f9d20c6f7d8644dc41ee57d48c98a78d24d5b74dc305cc518d6e0872d4fa73c5fd8d47ec00e3515858eaf3c3e512a703cdbc184ff0061af5979bc206618555 - languageName: node - linkType: hard - -"babel-helpers@npm:^6.24.1": - version: 6.24.1 - resolution: "babel-helpers@npm:6.24.1" - dependencies: - babel-runtime: "npm:^6.22.0" - babel-template: "npm:^6.24.1" - checksum: 10c0/bbd082e42adaa9c584242515e8c5b1e861108e03ed9517f0b600189e1c1041376ab6a15c71265a2cc095c5af4bd15cfc97158e30ce95a81cbfcea1bfd81ce3e6 - languageName: node - linkType: hard - "babel-jest@npm:^29.6.3, babel-jest@npm:^29.7.0": version: 29.7.0 resolution: "babel-jest@npm:29.7.0" @@ -7623,7 +7915,7 @@ __metadata: languageName: node linkType: hard -"babel-loader@npm:^8.1.0, babel-loader@npm:^8.2.3, babel-loader@npm:^8.2.5": +"babel-loader@npm:^8.1.0, babel-loader@npm:^8.2.5": version: 8.4.1 resolution: "babel-loader@npm:8.4.1" dependencies: @@ -7638,15 +7930,6 @@ __metadata: languageName: node linkType: hard -"babel-messages@npm:^6.23.0": - version: 6.23.0 - resolution: "babel-messages@npm:6.23.0" - dependencies: - babel-runtime: "npm:^6.22.0" - checksum: 10c0/d4fd6414ee5bb1aa0dad6d8d2c4ffaa66331ec5a507959e11f56b19a683566e2c1e7a4d0b16cfef58ea4cc07db8acf5ff3dc8b25c585407cff2e09ac60553401 - languageName: node - linkType: hard - "babel-plugin-apply-mdx-type-prop@npm:1.6.22": version: 1.6.22 resolution: "babel-plugin-apply-mdx-type-prop@npm:1.6.22" @@ -7824,7 +8107,7 @@ __metadata: languageName: node linkType: hard -"babel-polyfill@npm:6.26.0, babel-polyfill@npm:^6.26.0": +"babel-polyfill@npm:6.26.0": version: 6.26.0 resolution: "babel-polyfill@npm:6.26.0" dependencies: @@ -7996,22 +8279,7 @@ __metadata: languageName: node linkType: hard -"babel-register@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-register@npm:6.26.0" - dependencies: - babel-core: "npm:^6.26.0" - babel-runtime: "npm:^6.26.0" - core-js: "npm:^2.5.0" - home-or-tmp: "npm:^2.0.0" - lodash: "npm:^4.17.4" - mkdirp: "npm:^0.5.1" - source-map-support: "npm:^0.4.15" - checksum: 10c0/4ffbc1bfa60a817fb306c98d1a6d10852b0130a614dae3a91e45f391dbebdc95f428d95b489943d85724e046527d2aac3bafb74d3c24f62143492b5f606e2e04 - languageName: node - linkType: hard - -"babel-runtime@npm:^6.22.0, babel-runtime@npm:^6.23.0, babel-runtime@npm:^6.26.0": +"babel-runtime@npm:^6.23.0, babel-runtime@npm:^6.26.0": version: 6.26.0 resolution: "babel-runtime@npm:6.26.0" dependencies: @@ -8021,19 +8289,6 @@ __metadata: languageName: node linkType: hard -"babel-template@npm:^6.24.1, babel-template@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-template@npm:6.26.0" - dependencies: - babel-runtime: "npm:^6.26.0" - babel-traverse: "npm:^6.26.0" - babel-types: "npm:^6.26.0" - babylon: "npm:^6.18.0" - lodash: "npm:^4.17.4" - checksum: 10c0/67bc875f19d289dabb1830a1cde93d7f1e187e4599dac9b1d16392fd47f1d12b53fea902dacf7be360acd09807d440faafe0f7907758c13275b1a14d100b68e4 - languageName: node - linkType: hard - "babel-test@npm:^0.1.1": version: 0.1.6 resolution: "babel-test@npm:0.1.6" @@ -8046,44 +8301,6 @@ __metadata: languageName: node linkType: hard -"babel-traverse@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-traverse@npm:6.26.0" - dependencies: - babel-code-frame: "npm:^6.26.0" - babel-messages: "npm:^6.23.0" - babel-runtime: "npm:^6.26.0" - babel-types: "npm:^6.26.0" - babylon: "npm:^6.18.0" - debug: "npm:^2.6.8" - globals: "npm:^9.18.0" - invariant: "npm:^2.2.2" - lodash: "npm:^4.17.4" - checksum: 10c0/dca71b23d07e3c00833c3222d7998202e687105f461048107afeb2b4a7aa2507efab1bd5a6e3e724724ebb9b1e0b14f0113621e1d8c25b4ffdb829392b54b8de - languageName: node - linkType: hard - -"babel-types@npm:^6.26.0": - version: 6.26.0 - resolution: "babel-types@npm:6.26.0" - dependencies: - babel-runtime: "npm:^6.26.0" - esutils: "npm:^2.0.2" - lodash: "npm:^4.17.4" - to-fast-properties: "npm:^1.0.3" - checksum: 10c0/cabe371de1b32c4bbb1fd4ed0fe8a8726d42e5ad7d5cefb83cdae6de0f0a152dce591e4026719743fdf3aa45f84fea2c8851fb822fbe29b0c78a1f0094b67418 - languageName: node - linkType: hard - -"babylon@npm:^6.18.0": - version: 6.18.0 - resolution: "babylon@npm:6.18.0" - bin: - babylon: ./bin/babylon.js - checksum: 10c0/9b1bf946e16782deadb1f5414c1269efa6044eb1e97a3de2051f09a3f2a54e97be3542d4242b28d23de0ef67816f519d38ce1ec3ddb7be306131c39a60e5a667 - languageName: node - linkType: hard - "bail@npm:^1.0.0": version: 1.0.5 resolution: "bail@npm:1.0.5" @@ -8180,13 +8397,6 @@ __metadata: languageName: node linkType: hard -"binary-extensions@npm:^1.0.0": - version: 1.13.1 - resolution: "binary-extensions@npm:1.13.1" - checksum: 10c0/2d616938ac23d828ec3fbe0dea429b566fd2c137ddc38f166f16561ccd58029deac3fa9fddb489ab13d679c8fb5f1bd0e82824041299e5e39d8dd3cc68fbb9f9 - languageName: node - linkType: hard - "binary-extensions@npm:^2.0.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" @@ -8194,15 +8404,6 @@ __metadata: languageName: node linkType: hard -"bindings@npm:^1.5.0": - version: 1.5.0 - resolution: "bindings@npm:1.5.0" - dependencies: - file-uri-to-path: "npm:1.0.0" - checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba - languageName: node - linkType: hard - "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -8345,17 +8546,6 @@ __metadata: languageName: node linkType: hard -"braces@npm:^1.8.2": - version: 1.8.5 - resolution: "braces@npm:1.8.5" - dependencies: - expand-range: "npm:^1.8.1" - preserve: "npm:^0.2.0" - repeat-element: "npm:^1.1.2" - checksum: 10c0/41092fe0f5dbb522f013963fa4432fbef3323a92ee8c1a6b9b6681fc05525b8541968b525632aa9df217daa6307fe526e9ce994054d4308abd0627a7d26e4745 - languageName: node - linkType: hard - "braces@npm:^2.3.1": version: 2.3.2 resolution: "braces@npm:2.3.2" @@ -8752,19 +8942,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^1.1.3": - version: 1.1.3 - resolution: "chalk@npm:1.1.3" - dependencies: - ansi-styles: "npm:^2.2.1" - escape-string-regexp: "npm:^1.0.2" - has-ansi: "npm:^2.0.0" - strip-ansi: "npm:^3.0.0" - supports-color: "npm:^2.0.0" - checksum: 10c0/28c3e399ec286bb3a7111fd4225ebedb0d7b813aef38a37bca7c498d032459c265ef43404201d5fbb8d888d29090899c95335b4c0cda13e8b126ff15c541cef8 - languageName: node - linkType: hard - "chalk@npm:^3.0.0": version: 3.0.0 resolution: "chalk@npm:3.0.0" @@ -8893,26 +9070,6 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^1.6.1": - version: 1.7.0 - resolution: "chokidar@npm:1.7.0" - dependencies: - anymatch: "npm:^1.3.0" - async-each: "npm:^1.0.0" - fsevents: "npm:^1.0.0" - glob-parent: "npm:^2.0.0" - inherits: "npm:^2.0.1" - is-binary-path: "npm:^1.0.0" - is-glob: "npm:^2.0.0" - path-is-absolute: "npm:^1.0.0" - readdirp: "npm:^2.0.0" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/d3f82bc7fba1d5793a05ae494c30536cf6e4b23364a610e8bee8ae49dbaf963a67f70c627a943ab538cab252f6ac1862c6012885bccd06a10487438de5ae8a15 - languageName: node - linkType: hard - "chokidar@npm:^3.4.2, chokidar@npm:^3.5.3": version: 3.6.0 resolution: "chokidar@npm:3.6.0" @@ -9314,7 +9471,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^2.11.0, commander@npm:^2.19.0, commander@npm:^2.20.0": +"commander@npm:^2.19.0, commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" checksum: 10c0/74c781a5248c2402a0a3e966a0a2bba3c054aad144f5c023364be83265e796b20565aa9feff624132ff629aa64e16999fa40a743c10c12f7c61e96a794b99288 @@ -9597,21 +9754,6 @@ __metadata: languageName: node linkType: hard -"conventional-changelog-cli@npm:^2.0.11": - version: 2.2.2 - resolution: "conventional-changelog-cli@npm:2.2.2" - dependencies: - add-stream: "npm:^1.0.0" - conventional-changelog: "npm:^3.1.24" - lodash: "npm:^4.17.15" - meow: "npm:^8.0.0" - tempfile: "npm:^3.0.0" - bin: - conventional-changelog: cli.js - checksum: 10c0/f78790778b3e35c0f16d8299af98b03fd6376222047996cbfd2503a45ee86e3462f3276403ff6ee0cdaf7dcd90a2e230e16fc01fa7f64f5b5019a0dd34052ff4 - languageName: node - linkType: hard - "conventional-changelog-codemirror@npm:^2.0.8": version: 2.0.8 resolution: "conventional-changelog-codemirror@npm:2.0.8" @@ -9737,7 +9879,7 @@ __metadata: languageName: node linkType: hard -"conventional-changelog@npm:^3.1.24, conventional-changelog@npm:^3.1.8": +"conventional-changelog@npm:^3.1.8": version: 3.1.25 resolution: "conventional-changelog@npm:3.1.25" dependencies: @@ -9800,7 +9942,7 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:^1.5.0, convert-source-map@npm:^1.5.1, convert-source-map@npm:^1.7.0": +"convert-source-map@npm:^1.7.0": version: 1.9.0 resolution: "convert-source-map@npm:1.9.0" checksum: 10c0/281da55454bf8126cbc6625385928c43479f2060984180c42f3a86c8b8c12720a24eac260624a7d1e090004028d2dee78602330578ceec1a08e27cb8bb0a8a5b @@ -9924,7 +10066,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^5.0.7, cosmiconfig@npm:^5.1.0, cosmiconfig@npm:^5.2.0": +"cosmiconfig@npm:^5.1.0, cosmiconfig@npm:^5.2.0": version: 5.2.1 resolution: "cosmiconfig@npm:5.2.1" dependencies: @@ -10028,7 +10170,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -10363,7 +10505,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.0, debug@npm:^2.6.8, debug@npm:^2.6.9": +"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.0, debug@npm:^2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" dependencies: @@ -10687,15 +10829,6 @@ __metadata: languageName: node linkType: hard -"detect-indent@npm:^4.0.0": - version: 4.0.0 - resolution: "detect-indent@npm:4.0.0" - dependencies: - repeating: "npm:^2.0.0" - checksum: 10c0/066a0d13eadebb1e7d2ba395fdf9f3956f31f8383a6db263320108c283e2230250a102f4871f54926cc8a77c6323ac7103f30550a4ac3d6518aa1b934c041295 - languageName: node - linkType: hard - "detect-libc@npm:^2.0.1, detect-libc@npm:^2.0.3": version: 2.1.2 resolution: "detect-libc@npm:2.1.2" @@ -10810,8 +10943,8 @@ __metadata: "@docusaurus/module-type-aliases": "npm:^2.3.6" "@docusaurus/preset-classic": "npm:^2.3.6" "@docusaurus/remark-plugin-npm2yarn": "npm:^2.3.6" + "@docusaurus/theme-common": "npm:^2.3.6" "@easyops-cn/docusaurus-search-local": "npm:^0.33.4" - "@jest/globals": "npm:^29.7.0" "@material/material-color-utilities": "npm:0.2.4" "@mdx-js/react": "npm:^1.6.22" "@react-native-vector-icons/material-design-icons": "npm:^12.0.0" @@ -10833,6 +10966,7 @@ __metadata: react-native-web: "npm:^0.18.12" typescript: "npm:^4.8.4" use-latest-callback: "npm:^0.1.7" + webpack: "npm:^5.73.0" languageName: unknown linkType: soft @@ -11413,14 +11547,14 @@ __metadata: languageName: node linkType: hard -"eslint-config-prettier@npm:^8.3.0": - version: 8.10.2 - resolution: "eslint-config-prettier@npm:8.10.2" +"eslint-config-prettier@npm:^9.0.0": + version: 9.1.2 + resolution: "eslint-config-prettier@npm:9.1.2" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: 10c0/b5953cf7a86f685e1218b16707bf36643b525513d08495226a6820caccd8b7bfc6b9aa64ac7cb2415dbe2c1f7dc4995832148bdc53ad45777f75a8ded1073b29 + checksum: 10c0/d2e9dc913b1677764a4732433d83d258f40820458c65d0274cb9e3eaf6559b39f2136446f310c05abed065a4b3c2e901807ccf583dff76c6227eaebf4132c39a languageName: node linkType: hard @@ -11461,7 +11595,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-import@npm:^2.25.3": +"eslint-plugin-import@npm:^2.28.1": version: 2.32.0 resolution: "eslint-plugin-import@npm:2.32.0" dependencies: @@ -11490,56 +11624,56 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-jest@npm:^27.0.1": - version: 27.9.0 - resolution: "eslint-plugin-jest@npm:27.9.0" +"eslint-plugin-jest@npm:^28.7.0": + version: 28.14.0 + resolution: "eslint-plugin-jest@npm:28.14.0" dependencies: - "@typescript-eslint/utils": "npm:^5.10.0" + "@typescript-eslint/utils": "npm:^6.0.0 || ^7.0.0 || ^8.0.0" peerDependencies: - "@typescript-eslint/eslint-plugin": ^5.0.0 || ^6.0.0 || ^7.0.0 - eslint: ^7.0.0 || ^8.0.0 + "@typescript-eslint/eslint-plugin": ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 jest: "*" peerDependenciesMeta: "@typescript-eslint/eslint-plugin": optional: true jest: optional: true - checksum: 10c0/b8b09f7d8ba3d84a8779a6e95702a6e4dce45ab034e4edf5ddb631e77cd38dcdf791dfd9228e0a0d1d80d1eb2d278deb62ad2ec39f10fb8fd43cec07304e0c38 - languageName: node - linkType: hard - -"eslint-plugin-local-rules@npm:^1.3.2": - version: 1.3.2 - resolution: "eslint-plugin-local-rules@npm:1.3.2" - checksum: 10c0/a6677d0d7ad1e8cf08c175046fe13508c018f0d9294de42905769848aef2fdab9541272177b8b1af59b2ee6650b81a9ea3c5f4be3815be27829c79da2645529d + checksum: 10c0/da9c99dd8a1a80aa0c126ff4558882451dcee61b7e4c88e2407ac27d0c86fad2951384a4b037748e26f8743890b4628c6917b0760b01b7017c53fb29768584bc languageName: node linkType: hard -"eslint-plugin-prettier@npm:^4.0.0": - version: 4.2.5 - resolution: "eslint-plugin-prettier@npm:4.2.5" +"eslint-plugin-prettier@npm:^5.2.1": + version: 5.5.6 + resolution: "eslint-plugin-prettier@npm:5.5.6" dependencies: - prettier-linter-helpers: "npm:^1.0.0" + prettier-linter-helpers: "npm:^1.0.1" + synckit: "npm:^0.11.13" peerDependencies: - eslint: ">=7.28.0" - prettier: ">=2.0.0" + "@types/eslint": ">=8.0.0" + eslint: ">=8.0.0" + eslint-config-prettier: ">= 7.0.0 <10.0.0 || >=10.1.0" + prettier: ">=3.0.0" peerDependenciesMeta: + "@types/eslint": + optional: true eslint-config-prettier: optional: true - checksum: 10c0/75b3cdc90328aacf4cc7fabc522e651bd8208d40634c9b2772274332a696548136dac4608b141863bc462500c5a8012fbc2495623f684f631ddb62c2f5bca0a3 + checksum: 10c0/af37126c947ff3e87ff1ea76408db8cb1c7da966ebdaba68c6dd5924da7eb1b43b1abc4796d753a97b1bc26ea94c5497093e6ab9f8588fffe558d5a554e19bbf languageName: node linkType: hard -"eslint-plugin-promise@npm:^6.0.1": - version: 6.6.0 - resolution: "eslint-plugin-promise@npm:6.6.0" +"eslint-plugin-promise@npm:^7.1.0": + version: 7.3.0 + resolution: "eslint-plugin-promise@npm:7.3.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 - checksum: 10c0/93a667dbc9ff15c4d586b0d40a31c7828314cbbb31b2b9a75802aa4ef536e9457bb3e1a89b384b07aa336dd61b315ae8b0aadc0870210378023dd018819b59b3 + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + checksum: 10c0/417d3ab57895143bd687aa840804d0d7bbe70227242530eea7d4af7ae680a67d6872ce3a2adcc36dec29009d1861c63717c5887c30da1887153c8e5a181f3eb5 languageName: node linkType: hard -"eslint-plugin-react-hooks@npm:^4.3.0": +"eslint-plugin-react-hooks@npm:^4.6.0": version: 4.6.2 resolution: "eslint-plugin-react-hooks@npm:4.6.2" peerDependencies: @@ -11548,7 +11682,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react-native-a11y@npm:^3.2.1": +"eslint-plugin-react-native-a11y@npm:^3.4.1": version: 3.5.1 resolution: "eslint-plugin-react-native-a11y@npm:3.5.1" dependencies: @@ -11568,7 +11702,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react-native@npm:^4.0.0": +"eslint-plugin-react-native@npm:^4.1.0": version: 4.1.0 resolution: "eslint-plugin-react-native@npm:4.1.0" dependencies: @@ -11579,7 +11713,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:^7.27.1": +"eslint-plugin-react@npm:^7.35.0": version: 7.37.5 resolution: "eslint-plugin-react@npm:7.37.5" dependencies: @@ -11607,6 +11741,18 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-testing-library@npm:^7.16.2": + version: 7.16.2 + resolution: "eslint-plugin-testing-library@npm:7.16.2" + dependencies: + "@typescript-eslint/scope-manager": "npm:^8.56.0" + "@typescript-eslint/utils": "npm:^8.56.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + checksum: 10c0/ea99f41eee929e0b139d927f8a89bef7ffe3bd33c51fc3c2c93d8e9d146e146395e1f7bb8a34d4b5bd9a1ffe7a1879959fc171e952c731e1e8310b079d9d5c97 + languageName: node + linkType: hard + "eslint-restricted-globals@npm:^0.2.0": version: 0.2.0 resolution: "eslint-restricted-globals@npm:0.2.0" @@ -11614,7 +11760,7 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": +"eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" dependencies: @@ -11624,98 +11770,101 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^7.1.1": - version: 7.2.2 - resolution: "eslint-scope@npm:7.2.2" +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10c0/613c267aea34b5a6d6c00514e8545ef1f1433108097e857225fed40d397dd6b1809dffd11c2fde23b37ca53d7bf935fe04d2a18e6fc932b31837b6ad67e1c116 - languageName: node - linkType: hard - -"eslint-utils@npm:^3.0.0": - version: 3.0.0 - resolution: "eslint-utils@npm:3.0.0" - dependencies: - eslint-visitor-keys: "npm:^2.0.0" - peerDependencies: - eslint: ">=5" - checksum: 10c0/45aa2b63667a8d9b474c98c28af908d0a592bed1a4568f3145cd49fb5d9510f545327ec95561625290313fe126e6d7bdfe3fdbdb6f432689fab6b9497d3bfb52 + checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 languageName: node linkType: hard -"eslint-visitor-keys@npm:^2.0.0, eslint-visitor-keys@npm:^2.1.0": +"eslint-visitor-keys@npm:^2.1.0": version: 2.1.0 resolution: "eslint-visitor-keys@npm:2.1.0" checksum: 10c0/9f0e3a2db751d84067d15977ac4b4472efd6b303e369e6ff241a99feac04da758f46d5add022c33d06b53596038dbae4b4aceb27c7e68b8dfc1055b35e495787 languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": +"eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 languageName: node linkType: hard -"eslint@npm:8.31.0": - version: 8.31.0 - resolution: "eslint@npm:8.31.0" - dependencies: - "@eslint/eslintrc": "npm:^1.4.1" - "@humanwhocodes/config-array": "npm:^0.11.8" +"eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^5.0.0": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678 + languageName: node + linkType: hard + +"eslint@npm:9.39.4": + version: 9.39.4 + resolution: "eslint@npm:9.39.4" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.8.0" + "@eslint-community/regexpp": "npm:^4.12.1" + "@eslint/config-array": "npm:^0.21.2" + "@eslint/config-helpers": "npm:^0.4.2" + "@eslint/core": "npm:^0.17.0" + "@eslint/eslintrc": "npm:^3.3.5" + "@eslint/js": "npm:9.39.4" + "@eslint/plugin-kit": "npm:^0.4.1" + "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" - "@nodelib/fs.walk": "npm:^1.2.8" - ajv: "npm:^6.10.0" + "@humanwhocodes/retry": "npm:^0.4.2" + "@types/estree": "npm:^1.0.6" + ajv: "npm:^6.14.0" chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.2" + cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" - doctrine: "npm:^3.0.0" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^7.1.1" - eslint-utils: "npm:^3.0.0" - eslint-visitor-keys: "npm:^3.3.0" - espree: "npm:^9.4.0" - esquery: "npm:^1.4.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" + esquery: "npm:^1.5.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" - file-entry-cache: "npm:^6.0.1" + file-entry-cache: "npm:^8.0.0" find-up: "npm:^5.0.0" glob-parent: "npm:^6.0.2" - globals: "npm:^13.19.0" - grapheme-splitter: "npm:^1.0.4" ignore: "npm:^5.2.0" - import-fresh: "npm:^3.0.0" imurmurhash: "npm:^0.1.4" is-glob: "npm:^4.0.0" - is-path-inside: "npm:^3.0.3" - js-sdsl: "npm:^4.1.4" - js-yaml: "npm:^4.1.0" json-stable-stringify-without-jsonify: "npm:^1.0.1" - levn: "npm:^0.4.1" lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" + minimatch: "npm:^3.1.5" natural-compare: "npm:^1.4.0" - optionator: "npm:^0.9.1" - regexpp: "npm:^3.2.0" - strip-ansi: "npm:^6.0.1" - strip-json-comments: "npm:^3.1.0" - text-table: "npm:^0.2.0" + optionator: "npm:^0.9.3" + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true bin: eslint: bin/eslint.js - checksum: 10c0/55c32aeeb9b5d6d7417a9c5e5789aa5486ffd31cc0cf498ebaa945f2492964ea2372c38eff084eb99948d81d69cc387ca62ddf58e5824fac3c1d0162a53a76f7 + checksum: 10c0/1955067c2d991f0c84f4c4abfafe31bb47fa3b717a7fd3e43fe1e511c6f859d7700cbca969f85661dc4c130f7aeced5e5444884314198a54428f5e5141db9337 languageName: node linkType: hard -"espree@npm:^9.4.0": - version: 9.6.1 - resolution: "espree@npm:9.6.1" +"espree@npm:^10.0.1, espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" dependencies: - acorn: "npm:^8.9.0" + acorn: "npm:^8.15.0" acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^3.4.1" - checksum: 10c0/1a2e9b4699b715347f62330bcc76aee224390c28bb02b31a3752e9d07549c473f5f986720483c6469cf3cfb3c9d05df612ffc69eb1ee94b54b739e67de9bb460 + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b languageName: node linkType: hard @@ -11729,7 +11878,7 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.4.0": +"esquery@npm:^1.5.0": version: 1.7.0 resolution: "esquery@npm:1.7.0" dependencies: @@ -11911,15 +12060,6 @@ __metadata: languageName: node linkType: hard -"expand-brackets@npm:^0.1.4": - version: 0.1.5 - resolution: "expand-brackets@npm:0.1.5" - dependencies: - is-posix-bracket: "npm:^0.1.0" - checksum: 10c0/49b7fc1250f5f60ffe640be03777471ce63420eaa9850ce897b32bcf874e7be16b00917c7e2266a310e674ddb4ffe499ca964115bbc3f8c881288a280740aa6f - languageName: node - linkType: hard - "expand-brackets@npm:^2.1.4": version: 2.1.4 resolution: "expand-brackets@npm:2.1.4" @@ -11935,16 +12075,7 @@ __metadata: languageName: node linkType: hard -"expand-range@npm:^1.8.1": - version: 1.8.2 - resolution: "expand-range@npm:1.8.2" - dependencies: - fill-range: "npm:^2.1.0" - checksum: 10c0/ad7911af12f026953c57e3d7b7fe9f750ce2a1d45f7f7d717de890ed6429baf5e8a7224540cd648eeb603d409be0b7a7df09f951693cc83e98dcdc1e0043c23e - languageName: node - linkType: hard - -"expect@npm:^29.0.0, expect@npm:^29.7.0": +"expect@npm:^29.7.0": version: 29.7.0 resolution: "expect@npm:29.7.0" dependencies: @@ -11983,6 +12114,18 @@ __metadata: languageName: node linkType: hard +"expo-constants@npm:~56.0.18": + version: 56.0.18 + resolution: "expo-constants@npm:56.0.18" + dependencies: + "@expo/env": "npm:~2.3.0" + peerDependencies: + expo: "*" + react-native: "*" + checksum: 10c0/4a8c35f4eeeb078c0feb3d5b55da403ef1d151a35b9b3a4e17505cedfb7ddd78cc02d670ebc5b7c9fb11d42052fd1b0c143cd0a652d22fa6a3730685b19ca0b2 + languageName: node + linkType: hard + "expo-crypto@npm:~56.0.3": version: 56.0.3 resolution: "expo-crypto@npm:56.0.3" @@ -12358,15 +12501,6 @@ __metadata: languageName: node linkType: hard -"extglob@npm:^0.3.1": - version: 0.3.2 - resolution: "extglob@npm:0.3.2" - dependencies: - is-extglob: "npm:^1.0.0" - checksum: 10c0/9fcca7651e5c50fc970ec402476fb7a150e27cc2d8b415de8a6719fc111b2e03a9fabbff4fbed51221853f720ad734e842dfaef087ef57bdeb2456dcf0369029 - languageName: node - linkType: hard - "extglob@npm:^2.0.4": version: 2.0.4 resolution: "extglob@npm:2.0.4" @@ -12542,12 +12676,12 @@ __metadata: languageName: node linkType: hard -"file-entry-cache@npm:^6.0.1": - version: 6.0.1 - resolution: "file-entry-cache@npm:6.0.1" +"file-entry-cache@npm:^8.0.0": + version: 8.0.0 + resolution: "file-entry-cache@npm:8.0.0" dependencies: - flat-cache: "npm:^3.0.4" - checksum: 10c0/58473e8a82794d01b38e5e435f6feaf648e3f36fdb3a56e98f417f4efae71ad1c0d4ebd8a9a7c50c3ad085820a93fc7494ad721e0e4ebc1da3573f4e1c3c7cdd + flat-cache: "npm:^4.0.0" + checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638 languageName: node linkType: hard @@ -12575,20 +12709,6 @@ __metadata: languageName: node linkType: hard -"file-uri-to-path@npm:1.0.0": - version: 1.0.0 - resolution: "file-uri-to-path@npm:1.0.0" - checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519 - languageName: node - linkType: hard - -"filename-regex@npm:^2.0.0": - version: 2.0.1 - resolution: "filename-regex@npm:2.0.1" - checksum: 10c0/c669fe758641e4830641a9df1d387f14080d96ddde0ef9525439c6d16f4492ea167109362ea69eedd0eef39ae2739586b71daf5f4dab0847d1d07a01a1190ab3 - languageName: node - linkType: hard - "filename-reserved-regex@npm:^2.0.0": version: 2.0.0 resolution: "filename-reserved-regex@npm:2.0.0" @@ -12614,19 +12734,6 @@ __metadata: languageName: node linkType: hard -"fill-range@npm:^2.1.0": - version: 2.2.4 - resolution: "fill-range@npm:2.2.4" - dependencies: - is-number: "npm:^2.1.0" - isobject: "npm:^2.0.0" - randomatic: "npm:^3.0.0" - repeat-element: "npm:^1.1.2" - repeat-string: "npm:^1.5.2" - checksum: 10c0/1cfd1329311d778a844d5806bd06a5d297047e5ff352c45b4f9fadcda68eb272c8ef2196f1c44224f3fe66c672234453ce89aca94fb00122874bdb3978de5f71 - languageName: node - linkType: hard - "fill-range@npm:^4.0.0": version: 4.0.0 resolution: "fill-range@npm:4.0.0" @@ -12782,14 +12889,13 @@ __metadata: languageName: node linkType: hard -"flat-cache@npm:^3.0.4": - version: 3.2.0 - resolution: "flat-cache@npm:3.2.0" +"flat-cache@npm:^4.0.0": + version: 4.0.1 + resolution: "flat-cache@npm:4.0.1" dependencies: flatted: "npm:^3.2.9" - keyv: "npm:^4.5.3" - rimraf: "npm:^3.0.2" - checksum: 10c0/b76f611bd5f5d68f7ae632e3ae503e678d205cf97a17c6ab5b12f6ca61188b5f1f7464503efae6dc18683ed8f0b41460beb48ac4b9ac63fe6201296a91ba2f75 + keyv: "npm:^4.5.4" + checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc languageName: node linkType: hard @@ -12854,22 +12960,13 @@ __metadata: languageName: node linkType: hard -"for-in@npm:^1.0.1, for-in@npm:^1.0.2": +"for-in@npm:^1.0.2": version: 1.0.2 resolution: "for-in@npm:1.0.2" checksum: 10c0/42bb609d564b1dc340e1996868b67961257fd03a48d7fdafd4f5119530b87f962be6b4d5b7e3a3fc84c9854d149494b1d358e0b0ce9837e64c4c6603a49451d6 languageName: node linkType: hard -"for-own@npm:^0.1.4": - version: 0.1.5 - resolution: "for-own@npm:0.1.5" - dependencies: - for-in: "npm:^1.0.1" - checksum: 10c0/3f82c2ea489ce2eb74c0eb8634d89b30a620801c2cb5f2a83d2d797fe6990d40c1aeac8968783e157b1404cf35bac9acb0a6c46065ec37b38a21b5d896e500bd - languageName: node - linkType: hard - "foreground-child@npm:^2.0.0": version: 2.0.0 resolution: "foreground-child@npm:2.0.0" @@ -13022,13 +13119,6 @@ __metadata: languageName: node linkType: hard -"fs-readdir-recursive@npm:^1.0.0": - version: 1.1.0 - resolution: "fs-readdir-recursive@npm:1.1.0" - checksum: 10c0/7e190393952143e674b6d1ad4abcafa1b5d3e337fcc21b0cb051079a7140a54617a7df193d562ef9faf21bd7b2148a38601b3d5c16261fa76f278d88dc69989c - languageName: node - linkType: hard - "fs.realpath@npm:^1.0.0": version: 1.0.0 resolution: "fs.realpath@npm:1.0.0" @@ -13036,17 +13126,6 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^1.0.0": - version: 1.2.13 - resolution: "fsevents@npm:1.2.13" - dependencies: - bindings: "npm:^1.5.0" - nan: "npm:^2.12.1" - checksum: 10c0/4427ff08db9ee7327f2c3ad58ec56f9096a917eed861bfffaa2e2be419479cdf37d00750869ab9ecbf5f59f32ad999bd59577d73fc639193e6c0ce52bb253e02 - conditions: os=darwin - languageName: node - linkType: hard - "fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": version: 2.3.3 resolution: "fsevents@npm:2.3.3" @@ -13057,16 +13136,6 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^1.0.0#optional!builtin": - version: 1.2.13 - resolution: "fsevents@patch:fsevents@npm%3A1.2.13#optional!builtin::version=1.2.13&hash=d11327" - dependencies: - bindings: "npm:^1.5.0" - nan: "npm:^2.12.1" - conditions: os=darwin - languageName: node - linkType: hard - "fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" @@ -13198,13 +13267,6 @@ __metadata: languageName: node linkType: hard -"get-stdin@npm:^6.0.0": - version: 6.0.0 - resolution: "get-stdin@npm:6.0.0" - checksum: 10c0/c8971d27ffb72e4aae0f18ba792d2bfec872f662e98e13b182d8611a36f38396b79f43563884f597e667c7bb9ab98f337ee958ae278af5fa7c310ca62845e56b - languageName: node - linkType: hard - "get-stream@npm:^4.0.0, get-stream@npm:^4.1.0": version: 4.1.0 resolution: "get-stream@npm:4.1.0" @@ -13327,25 +13389,6 @@ __metadata: languageName: node linkType: hard -"glob-base@npm:^0.3.0": - version: 0.3.0 - resolution: "glob-base@npm:0.3.0" - dependencies: - glob-parent: "npm:^2.0.0" - is-glob: "npm:^2.0.0" - checksum: 10c0/4ce785c1dac2ff1e4660c010fa43ed2f1b38993dfd004023a3e7080b20bc61f29fbfe5d265b7e64cc84096ecf44e8ca876c7c1aad8f1f995d4c0f33034f3ae8c - languageName: node - linkType: hard - -"glob-parent@npm:^2.0.0": - version: 2.0.0 - resolution: "glob-parent@npm:2.0.0" - dependencies: - is-glob: "npm:^2.0.0" - checksum: 10c0/b9d59dc532d47aaaa4841046ff631b325a707f738445300b83b7a1ee603dd060c041a378e8a195c887d479bb703685cee4725c8f54b8dacef65355375f57d32a - languageName: node - linkType: hard - "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -13393,7 +13436,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": +"glob@npm:^7.0.0, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -13454,19 +13497,17 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.19.0": - version: 13.24.0 - resolution: "globals@npm:13.24.0" - dependencies: - type-fest: "npm:^0.20.2" - checksum: 10c0/d3c11aeea898eb83d5ec7a99508600fbe8f83d2cf00cbb77f873dbf2bcb39428eff1b538e4915c993d8a3b3473fa71eeebfe22c9bb3a3003d1e26b1f2c8a42cd +"globals@npm:^14.0.0": + version: 14.0.0 + resolution: "globals@npm:14.0.0" + checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d languageName: node linkType: hard -"globals@npm:^9.18.0": - version: 9.18.0 - resolution: "globals@npm:9.18.0" - checksum: 10c0/5ab74cb67cf060a9fceede4a0f2babc4c2c0b90dbb13847d2659defdf2121c60035ef23823c8417ce8c11bdaa7b412396077f2b3d2a7dedab490a881a0a96754 +"globals@npm:^15.9.0": + version: 15.15.0 + resolution: "globals@npm:15.15.0" + checksum: 10c0/f9ae80996392ca71316495a39bec88ac43ae3525a438b5626cd9d5ce9d5500d0a98a266409605f8cd7241c7acf57c354a48111ea02a767ba4f374b806d6861fe languageName: node linkType: hard @@ -13580,27 +13621,13 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.4, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 languageName: node linkType: hard -"grapheme-splitter@npm:^1.0.4": - version: 1.0.4 - resolution: "grapheme-splitter@npm:1.0.4" - checksum: 10c0/108415fb07ac913f17040dc336607772fcea68c7f495ef91887edddb0b0f5ff7bc1d1ab181b125ecb2f0505669ef12c9a178a3bbd2dd8e042d8c5f1d7c90331a - languageName: node - linkType: hard - -"graphemer@npm:^1.4.0": - version: 1.4.0 - resolution: "graphemer@npm:1.4.0" - checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 - languageName: node - linkType: hard - "gray-matter@npm:^4.0.3": version: 4.0.3 resolution: "gray-matter@npm:4.0.3" @@ -13654,15 +13681,6 @@ __metadata: languageName: node linkType: hard -"has-ansi@npm:^2.0.0": - version: 2.0.0 - resolution: "has-ansi@npm:2.0.0" - dependencies: - ansi-regex: "npm:^2.0.0" - checksum: 10c0/f54e4887b9f8f3c4bfefd649c48825b3c093987c92c27880ee9898539e6f01aed261e82e73153c3f920fde0db5bf6ebd58deb498ed1debabcb4bc40113ccdf05 - languageName: node - linkType: hard - "has-bigints@npm:^1.0.2": version: 1.1.0 resolution: "has-bigints@npm:1.1.0" @@ -14066,16 +14084,6 @@ __metadata: languageName: node linkType: hard -"home-or-tmp@npm:^2.0.0": - version: 2.0.0 - resolution: "home-or-tmp@npm:2.0.0" - dependencies: - os-homedir: "npm:^1.0.0" - os-tmpdir: "npm:^1.0.1" - checksum: 10c0/a0e0d26db09dc0b3245f52a9159d3e970e628ddc22d69842e8413ea42f81d5a29c3808f9b08ea4d48db084e4e693193cc238c114775aa92d753bf95a9daa10fb - languageName: node - linkType: hard - "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -14334,26 +14342,6 @@ __metadata: languageName: node linkType: hard -"husky@npm:^1.3.1": - version: 1.3.1 - resolution: "husky@npm:1.3.1" - dependencies: - cosmiconfig: "npm:^5.0.7" - execa: "npm:^1.0.0" - find-up: "npm:^3.0.0" - get-stdin: "npm:^6.0.0" - is-ci: "npm:^2.0.0" - pkg-dir: "npm:^3.0.0" - please-upgrade-node: "npm:^3.1.1" - read-pkg: "npm:^4.0.1" - run-node: "npm:^1.0.0" - slash: "npm:^2.0.0" - bin: - husky-upgrade: ./lib/upgrader/bin.js - checksum: 10c0/5151906792ef9421536db0a23f922c6f8cb01872a75f0e9a82b22647202350f3791eaf9036cbf68ba911a4c2f3bc601b7507141bd6d9e28ba3222bef8b2679ad - languageName: node - linkType: hard - "hyphenate-style-name@npm:^1.0.3": version: 1.1.0 resolution: "hyphenate-style-name@npm:1.1.0" @@ -14409,7 +14397,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^7.0.3": +"ignore@npm:^7.0.3, ignore@npm:^7.0.5": version: 7.0.5 resolution: "ignore@npm:7.0.5" checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d @@ -14636,7 +14624,7 @@ __metadata: languageName: node linkType: hard -"invariant@npm:^2.2.2, invariant@npm:^2.2.4": +"invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" dependencies: @@ -14739,15 +14727,6 @@ __metadata: languageName: node linkType: hard -"is-binary-path@npm:^1.0.0": - version: 1.0.1 - resolution: "is-binary-path@npm:1.0.1" - dependencies: - binary-extensions: "npm:^1.0.0" - checksum: 10c0/16e456fa3782eaf3d8e28d382b750507e3d54ff6694df8a1b2c6498da321e2ead311de9c42e653d8fb3213de72bac204b5f97e4a110cda8a72f17b1c1b4eb643 - languageName: node - linkType: hard - "is-binary-path@npm:~2.1.0": version: 2.1.0 resolution: "is-binary-path@npm:2.1.0" @@ -14881,22 +14860,6 @@ __metadata: languageName: node linkType: hard -"is-dotfile@npm:^1.0.0": - version: 1.0.3 - resolution: "is-dotfile@npm:1.0.3" - checksum: 10c0/aa6bb345aa06555f46eedd491bdd039b95d3fa80b899ee7d6b30628e309d705d403e445fd8a126ff70962adc1252171dbe0d72884afa323fb3c817387faf10ed - languageName: node - linkType: hard - -"is-equal-shallow@npm:^0.1.3": - version: 0.1.3 - resolution: "is-equal-shallow@npm:0.1.3" - dependencies: - is-primitive: "npm:^2.0.0" - checksum: 10c0/ae623698cdfeeec0688b2e6128d76cabe1cc5957d533bf7f7596caf3f2993d4c50a20c97420e60a0d58745fc4b2709dfb62e653e054cf948c5834615b715f05f - languageName: node - linkType: hard - "is-extendable@npm:^0.1.0, is-extendable@npm:^0.1.1": version: 0.1.1 resolution: "is-extendable@npm:0.1.1" @@ -14913,13 +14876,6 @@ __metadata: languageName: node linkType: hard -"is-extglob@npm:^1.0.0": - version: 1.0.0 - resolution: "is-extglob@npm:1.0.0" - checksum: 10c0/1ce5366d19958f36069a45ca996c1e51ab607f42a01eb0505f0ccffe8f9c91f5bcba6e971605efd8b4d4dfd0111afa3c8df3e1746db5b85b9a8f933f5e7286b7 - languageName: node - linkType: hard - "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -14936,13 +14892,6 @@ __metadata: languageName: node linkType: hard -"is-finite@npm:^1.0.0": - version: 1.1.0 - resolution: "is-finite@npm:1.1.0" - checksum: 10c0/ca6bc7a0321b339f098e657bd4cbf4bb2410f5a11f1b9adb1a1a9ab72288b64368e8251326cb1f74e985f2779299cec3e1f1e558b68ce7e1e2c9be17b7cfd626 - languageName: node - linkType: hard - "is-fullwidth-code-point@npm:^2.0.0": version: 2.0.0 resolution: "is-fullwidth-code-point@npm:2.0.0" @@ -14977,15 +14926,6 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^2.0.0, is-glob@npm:^2.0.1": - version: 2.0.1 - resolution: "is-glob@npm:2.0.1" - dependencies: - is-extglob: "npm:^1.0.0" - checksum: 10c0/ef156806af0924983325c9218a8b8a838fa50e1a104ed2a11fe94829a5b27c1b05a4c8cf98d96cb3a7fea539c21f14ae2081e1a248f3d5a9eea62f2d4e9f8b0c - languageName: node - linkType: hard - "is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -15076,15 +15016,6 @@ __metadata: languageName: node linkType: hard -"is-number@npm:^2.1.0": - version: 2.1.0 - resolution: "is-number@npm:2.1.0" - dependencies: - kind-of: "npm:^3.0.2" - checksum: 10c0/f9d2079a0dbfbce6f9f3b6644f6eb60d0211ee56bb26db3963ef4d514e2444f87e3f56c8169896c90544c501ed5e510c5b83abae6748a57d15f6ac8d85efd602 - languageName: node - linkType: hard - "is-number@npm:^3.0.0": version: 3.0.0 resolution: "is-number@npm:3.0.0" @@ -15094,13 +15025,6 @@ __metadata: languageName: node linkType: hard -"is-number@npm:^4.0.0": - version: 4.0.0 - resolution: "is-number@npm:4.0.0" - checksum: 10c0/bb17a331f357eb59a7f8db848086c41886715b2ea1db03f284a99d14001cda094083a5b6a7b343b5bcf410ccef668a70bc626d07bc2032cc4ab46dd264cea244 - languageName: node - linkType: hard - "is-number@npm:^7.0.0": version: 7.0.0 resolution: "is-number@npm:7.0.0" @@ -15136,7 +15060,7 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.1, is-path-inside@npm:^3.0.2, is-path-inside@npm:^3.0.3": +"is-path-inside@npm:^3.0.1, is-path-inside@npm:^3.0.2": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 @@ -15187,20 +15111,6 @@ __metadata: languageName: node linkType: hard -"is-posix-bracket@npm:^0.1.0": - version: 0.1.1 - resolution: "is-posix-bracket@npm:0.1.1" - checksum: 10c0/13ef3f466700fd63c1c348e647edfa22b73bb89cf8d993fb7820824ea2ddc7119975e64861fe1d52c3c4e881a7dcf2538faa05e3f700e9d2ea56eeeb4ba26a25 - languageName: node - linkType: hard - -"is-primitive@npm:^2.0.0": - version: 2.0.0 - resolution: "is-primitive@npm:2.0.0" - checksum: 10c0/bb84a2f05eca29f560aafc3bca9173e4c06d74dc24a6fc7faee6e61c70a00bae95e08f0d3d217d61e646b521378d4326103d124bb469d1de0240c8722b56a3fd - languageName: node - linkType: hard - "is-regex@npm:^1.2.1": version: 1.2.1 resolution: "is-regex@npm:1.2.1" @@ -15645,6 +15555,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:30.4.1": + version: 30.4.1 + resolution: "jest-diff@npm:30.4.1" + dependencies: + "@jest/diff-sequences": "npm:30.4.0" + "@jest/get-type": "npm:30.1.0" + chalk: "npm:^4.1.2" + pretty-format: "npm:30.4.1" + checksum: 10c0/787e11f0ea27e94815479d6c5415e4173da1e74bede34c1515b8515fc9d1fe053e2ad25a3c31f9998a7292c186a0e4d395ed82e0e149d57d7708ee6759b442e9 + languageName: node + linkType: hard + "jest-diff@npm:^24.8.0": version: 24.9.0 resolution: "jest-diff@npm:24.9.0" @@ -15657,7 +15579,7 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.0.1, jest-diff@npm:^29.7.0": +"jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" dependencies: @@ -15764,7 +15686,7 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^29.0.1, jest-matcher-utils@npm:^29.7.0": +"jest-matcher-utils@npm:^29.7.0": version: 29.7.0 resolution: "jest-matcher-utils@npm:29.7.0" dependencies: @@ -15776,6 +15698,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:^30.4.1": + version: 30.4.1 + resolution: "jest-matcher-utils@npm:30.4.1" + dependencies: + "@jest/get-type": "npm:30.1.0" + chalk: "npm:^4.1.2" + jest-diff: "npm:30.4.1" + pretty-format: "npm:30.4.1" + checksum: 10c0/ddbb0c7075def27ba30160883c327cb3fd13f561f5789d00a1edca1b48b0651f8ea23a1c51bcfcb6413a68c47d658bcf47a34701b8a39ce135dd28d87a3117af + languageName: node + linkType: hard + "jest-message-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-message-util@npm:29.7.0" @@ -16052,13 +15986,6 @@ __metadata: languageName: node linkType: hard -"js-sdsl@npm:^4.1.4": - version: 4.4.2 - resolution: "js-sdsl@npm:4.4.2" - checksum: 10c0/50707728fc31642164f4d83c8087f3750aaa99c450b008b19e236a1f190c9e48f9fc799615c341f9ca2c0803b15ab6f48d92a9cc3e6ffd20065cba7d7e742b92 - languageName: node - linkType: hard - "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -16066,13 +15993,6 @@ __metadata: languageName: node linkType: hard -"js-tokens@npm:^3.0.2": - version: 3.0.2 - resolution: "js-tokens@npm:3.0.2" - checksum: 10c0/e3c3ee4d12643d90197628eb022a2884a15f08ea7dcac1ce97fdeee43031fbfc7ede674f2cdbbb582dcd4c94388b22e52d56c6cbeb2ac7d1b57c2f33c405e2ba - languageName: node - linkType: hard - "js-yaml@npm:^3.13.1": version: 3.14.2 resolution: "js-yaml@npm:3.14.2" @@ -16096,6 +16016,17 @@ __metadata: languageName: node linkType: hard +"js-yaml@npm:^4.1.1": + version: 4.2.0 + resolution: "js-yaml@npm:4.2.0" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/1916456c118746603b067d74bbcbb0445d9a1d5e474ad4ae775e7b20525bed902e01d9d97dd0c81fcd8d4f596162309d0eb057f4aa38f3e9647f14075e9dea45 + languageName: node + linkType: hard + "jsc-safe-url@npm:^0.2.2, jsc-safe-url@npm:^0.2.4": version: 0.2.4 resolution: "jsc-safe-url@npm:0.2.4" @@ -16103,15 +16034,6 @@ __metadata: languageName: node linkType: hard -"jsesc@npm:^1.3.0": - version: 1.3.0 - resolution: "jsesc@npm:1.3.0" - bin: - jsesc: bin/jsesc - checksum: 10c0/62420889dd46b4cdba4df20fe6ffdefa6eeab7532fb4079170ea1b53c45d5a6abcb485144905833e5a69cc1735db12319b1e0b0f9a556811ec926b57a22318a7 - languageName: node - linkType: hard - "jsesc@npm:^3.0.2, jsesc@npm:~3.1.0": version: 3.1.0 resolution: "jsesc@npm:3.1.0" @@ -16188,15 +16110,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^0.5.1": - version: 0.5.1 - resolution: "json5@npm:0.5.1" - bin: - json5: lib/cli.js - checksum: 10c0/aca0ab7ccf1883d3fc2ecc16219bc389716a773f774552817deaadb549acc0bb502e317a81946fc0a48f9eb6e0822cf1dc5a097009203f2c94de84c8db02a1f3 - languageName: node - linkType: hard - "json5@npm:^1.0.1, json5@npm:^1.0.2": version: 1.0.2 resolution: "json5@npm:1.0.2" @@ -16270,7 +16183,7 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^4.0.0, keyv@npm:^4.5.3": +"keyv@npm:^4.0.0, keyv@npm:^4.5.4": version: 4.5.4 resolution: "keyv@npm:4.5.4" dependencies: @@ -16336,22 +16249,133 @@ __metadata: languageName: node linkType: hard -"latest-version@npm:^5.0.0, latest-version@npm:^5.1.0": - version: 5.1.0 - resolution: "latest-version@npm:5.1.0" - dependencies: - package-json: "npm:^6.3.0" - checksum: 10c0/6219631d8651467c54c58ef1b5d5c5c53e146f5ae2b0ecbb78b202da3eaad55b05b043db2d2d6f1d4230ee071b2ae8c2f85089e01377e4338bad97fa76a963b7 +"latest-version@npm:^5.0.0, latest-version@npm:^5.1.0": + version: 5.1.0 + resolution: "latest-version@npm:5.1.0" + dependencies: + package-json: "npm:^6.3.0" + checksum: 10c0/6219631d8651467c54c58ef1b5d5c5c53e146f5ae2b0ecbb78b202da3eaad55b05b043db2d2d6f1d4230ee071b2ae8c2f85089e01377e4338bad97fa76a963b7 + languageName: node + linkType: hard + +"launch-editor@npm:^2.6.0": + version: 2.12.0 + resolution: "launch-editor@npm:2.12.0" + dependencies: + picocolors: "npm:^1.1.1" + shell-quote: "npm:^1.8.3" + checksum: 10c0/fac5e7ad90bf185594cad4c831a52419eef50e667c4eddb5b0a58eb5f944e16d947636ee767b9896ffd46a51db34925edd3b854c48efb47f6d767ffd7d904e71 + languageName: node + linkType: hard + +"lefthook-darwin-arm64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-darwin-arm64@npm:2.1.9" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-darwin-x64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-darwin-x64@npm:2.1.9" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lefthook-freebsd-arm64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-freebsd-arm64@npm:2.1.9" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-freebsd-x64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-freebsd-x64@npm:2.1.9" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lefthook-linux-arm64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-linux-arm64@npm:2.1.9" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-linux-x64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-linux-x64@npm:2.1.9" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"lefthook-openbsd-arm64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-openbsd-arm64@npm:2.1.9" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-openbsd-x64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-openbsd-x64@npm:2.1.9" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"lefthook-windows-arm64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-windows-arm64@npm:2.1.9" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lefthook-windows-x64@npm:2.1.9": + version: 2.1.9 + resolution: "lefthook-windows-x64@npm:2.1.9" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"launch-editor@npm:^2.6.0": - version: 2.12.0 - resolution: "launch-editor@npm:2.12.0" +"lefthook@npm:^2.1.9": + version: 2.1.9 + resolution: "lefthook@npm:2.1.9" dependencies: - picocolors: "npm:^1.1.1" - shell-quote: "npm:^1.8.3" - checksum: 10c0/fac5e7ad90bf185594cad4c831a52419eef50e667c4eddb5b0a58eb5f944e16d947636ee767b9896ffd46a51db34925edd3b854c48efb47f6d767ffd7d904e71 + lefthook-darwin-arm64: "npm:2.1.9" + lefthook-darwin-x64: "npm:2.1.9" + lefthook-freebsd-arm64: "npm:2.1.9" + lefthook-freebsd-x64: "npm:2.1.9" + lefthook-linux-arm64: "npm:2.1.9" + lefthook-linux-x64: "npm:2.1.9" + lefthook-openbsd-arm64: "npm:2.1.9" + lefthook-openbsd-x64: "npm:2.1.9" + lefthook-windows-arm64: "npm:2.1.9" + lefthook-windows-x64: "npm:2.1.9" + dependenciesMeta: + lefthook-darwin-arm64: + optional: true + lefthook-darwin-x64: + optional: true + lefthook-freebsd-arm64: + optional: true + lefthook-freebsd-x64: + optional: true + lefthook-linux-arm64: + optional: true + lefthook-linux-x64: + optional: true + lefthook-openbsd-arm64: + optional: true + lefthook-openbsd-x64: + optional: true + lefthook-windows-arm64: + optional: true + lefthook-windows-x64: + optional: true + bin: + lefthook: bin/index.js + checksum: 10c0/676d9798942439d1cd5373bb0b2c9907f6a8097b29c05cc028d87d897d2a2cfe15a8f7e4f40b02fa93cd1ae0f02528f24053a10fb5c5967210a9c23f3c7b5059 languageName: node linkType: hard @@ -16718,7 +16742,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.17.21, lodash@npm:^4.0.1, lodash@npm:^4.11.2, lodash@npm:^4.17.11, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.2.1": +"lodash@npm:4.17.21, lodash@npm:^4.0.1, lodash@npm:^4.11.2, lodash@npm:^4.17.11, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.5, lodash@npm:^4.2.1": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c @@ -17012,13 +17036,6 @@ __metadata: languageName: node linkType: hard -"math-random@npm:^1.0.1": - version: 1.0.4 - resolution: "math-random@npm:1.0.4" - checksum: 10c0/7b0ddc17f5dfe3b426c1e92505122e6a32f884dd50f5e0bb3898e5ce2da60b4ffb47c9b607809cf0beb5b8bf253b9dcc3b6f7331b20ce59b8bd7e8dbbbb1e347 - languageName: node - linkType: hard - "mdast-squeeze-paragraphs@npm:^3.0.0": version: 3.0.5 resolution: "mdast-squeeze-paragraphs@npm:3.0.5" @@ -17458,28 +17475,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^2.1.5": - version: 2.3.11 - resolution: "micromatch@npm:2.3.11" - dependencies: - arr-diff: "npm:^2.0.0" - array-unique: "npm:^0.2.1" - braces: "npm:^1.8.2" - expand-brackets: "npm:^0.1.4" - extglob: "npm:^0.3.1" - filename-regex: "npm:^2.0.0" - is-extglob: "npm:^1.0.0" - is-glob: "npm:^2.0.1" - kind-of: "npm:^3.0.2" - normalize-path: "npm:^2.0.1" - object.omit: "npm:^2.0.0" - parse-glob: "npm:^3.0.4" - regex-cache: "npm:^0.4.2" - checksum: 10c0/56864f45f5a76523a3b3fe7c07c1a19cb9e6a2078b1e5dd036bacdd6e65f5d8adc00679ebb785ab88d577fce80197f2d8fd6f5565188643f87d8a47f64f6127a - languageName: node - linkType: hard - -"micromatch@npm:^3.1.10, micromatch@npm:^3.1.4": +"micromatch@npm:^3.1.4": version: 3.1.10 resolution: "micromatch@npm:3.1.10" dependencies: @@ -17684,6 +17680,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^3.1.5": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 + languageName: node + linkType: hard + "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -17929,15 +17934,6 @@ __metadata: languageName: node linkType: hard -"nan@npm:^2.12.1": - version: 2.24.0 - resolution: "nan@npm:2.24.0" - dependencies: - node-gyp: "npm:latest" - checksum: 10c0/6f9828a15464999ccefcae61b0f94f1f37067048a56363966e892cc6a194e3500966ae6964dd5a6a8acc5e1a849d60d620b120a84bc66c60445379a930c5b0f8 - languageName: node - linkType: hard - "nanoid@npm:^3.3.11": version: 3.3.11 resolution: "nanoid@npm:3.3.11" @@ -17975,13 +17971,6 @@ __metadata: languageName: node linkType: hard -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: 10c0/f6cef26f5044515754802c0fc475d81426f3b90fe88c20fabe08771ce1f736ce46e0397c10acb569a4dd0acb84c7f1ee70676122f95d5bfdd747af3a6c6bbaa8 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -18178,7 +18167,7 @@ __metadata: languageName: node linkType: hard -"normalize-path@npm:^2.0.0, normalize-path@npm:^2.0.1, normalize-path@npm:^2.1.1": +"normalize-path@npm:^2.1.1": version: 2.1.1 resolution: "normalize-path@npm:2.1.1" dependencies: @@ -18379,16 +18368,6 @@ __metadata: languageName: node linkType: hard -"object.omit@npm:^2.0.0": - version: 2.0.1 - resolution: "object.omit@npm:2.0.1" - dependencies: - for-own: "npm:^0.1.4" - is-extendable: "npm:^0.1.1" - checksum: 10c0/219549087650a1dce1990bbb9c207aa9e0c5302372cbcb363b4a7a36a7b1655a80287d290bebcaff5ae4b5ab7e5859a57f49e3f766cade65bc149fe15c0ba38d - languageName: node - linkType: hard - "object.pick@npm:^1.3.0": version: 1.3.0 resolution: "object.pick@npm:1.3.0" @@ -18508,7 +18487,7 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.9.1": +"optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" dependencies: @@ -18553,13 +18532,6 @@ __metadata: languageName: node linkType: hard -"os-homedir@npm:^1.0.0": - version: 1.0.2 - resolution: "os-homedir@npm:1.0.2" - checksum: 10c0/6be4aa67317ee247b8d46142e243fb4ef1d2d65d3067f54bfc5079257a2f4d4d76b2da78cba7af3cb3f56dbb2e4202e0c47f26171d11ca1ed4008d842c90363f - languageName: node - linkType: hard - "os-name@npm:4.0.0": version: 4.0.0 resolution: "os-name@npm:4.0.0" @@ -18570,24 +18542,13 @@ __metadata: languageName: node linkType: hard -"os-tmpdir@npm:^1.0.1, os-tmpdir@npm:~1.0.1, os-tmpdir@npm:~1.0.2": +"os-tmpdir@npm:~1.0.1, os-tmpdir@npm:~1.0.2": version: 1.0.2 resolution: "os-tmpdir@npm:1.0.2" checksum: 10c0/f438450224f8e2687605a8dd318f0db694b6293c5d835ae509a69e97c8de38b6994645337e5577f5001115470414638978cc49da1cdcc25106dad8738dc69990 languageName: node linkType: hard -"output-file-sync@npm:^1.1.2": - version: 1.1.2 - resolution: "output-file-sync@npm:1.1.2" - dependencies: - graceful-fs: "npm:^4.1.4" - mkdirp: "npm:^0.5.1" - object-assign: "npm:^4.1.0" - checksum: 10c0/b77185cb61fe209061898180eefab0d2c619e87cf5f6de14fa2c043a38795c8f00109624b017966a4528a2eab8b5f34e37a81cb48f31e1ebad5193f00e8fe8a4 - languageName: node - linkType: hard - "own-keys@npm:^1.0.1": version: 1.0.1 resolution: "own-keys@npm:1.0.1" @@ -18816,18 +18777,6 @@ __metadata: languageName: node linkType: hard -"parse-glob@npm:^3.0.4": - version: 3.0.4 - resolution: "parse-glob@npm:3.0.4" - dependencies: - glob-base: "npm:^0.3.0" - is-dotfile: "npm:^1.0.0" - is-extglob: "npm:^1.0.0" - is-glob: "npm:^2.0.0" - checksum: 10c0/4faf2e81ca85bc545777a1210ab770e0305c9e095680c219e5635e1a439d763feaf761e055b136425c3d6dcd3ec9431b77fd20f7411525b21031620125dc1dbc - languageName: node - linkType: hard - "parse-json@npm:5.2.0, parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" @@ -19027,7 +18976,7 @@ __metadata: languageName: node linkType: hard -"path-is-absolute@npm:^1.0.0, path-is-absolute@npm:^1.0.1": +"path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 @@ -19234,15 +19183,6 @@ __metadata: languageName: node linkType: hard -"please-upgrade-node@npm:^3.1.1": - version: 3.2.0 - resolution: "please-upgrade-node@npm:3.2.0" - dependencies: - semver-compare: "npm:^1.0.0" - checksum: 10c0/222514d2841022be4b843f38d415beadcc6409c0545d6d153778d71c601bba7bbf1cd5827d650c7fae6a9a2ba7cf00f4b6729b40d015a3a5ba2937e57bc1c435 - languageName: node - linkType: hard - "plist@npm:^3.0.5, plist@npm:^3.1.0": version: 3.1.0 resolution: "plist@npm:3.1.0" @@ -19788,14 +19728,7 @@ __metadata: languageName: node linkType: hard -"preserve@npm:^0.2.0": - version: 0.2.0 - resolution: "preserve@npm:0.2.0" - checksum: 10c0/21154ae0e53e3a338bcdf61dd6859a62f12f198961509fe07ac4f7f59b6f97de0b60c0dda2cce18e57894c77fa22544c8941c4e6f41fc30ed36753763fba6f19 - languageName: node - linkType: hard - -"prettier-linter-helpers@npm:^1.0.0": +"prettier-linter-helpers@npm:^1.0.1": version: 1.0.1 resolution: "prettier-linter-helpers@npm:1.0.1" dependencies: @@ -19804,7 +19737,7 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2, prettier@npm:^2.4.1": +"prettier@npm:^2": version: 2.8.8 resolution: "prettier@npm:2.8.8" bin: @@ -19813,6 +19746,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^3.0.3, prettier@npm:^3.8.4": + version: 3.8.4 + resolution: "prettier@npm:3.8.4" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/b90a0cbe75b88ac0af9c13fe0f359bd19926fabccd88483227b21f71f0c1cc42da056fc1ac3a361e665577c568371d5ccfb2c62c31c8a1186f8d1bd531a063e9 + languageName: node + linkType: hard + "pretty-error@npm:^4.0.0": version: 4.0.0 resolution: "pretty-error@npm:4.0.0" @@ -19823,6 +19765,18 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:30.4.1, pretty-format@npm:^30.4.1": + version: 30.4.1 + resolution: "pretty-format@npm:30.4.1" + dependencies: + "@jest/schemas": "npm:30.4.1" + ansi-styles: "npm:^5.2.0" + react-is-18: "npm:react-is@^18.3.1" + react-is-19: "npm:react-is@^19.2.5" + checksum: 10c0/c7e6633740cd2f6d382f188c00c8b4b3f2bee3cda16db6753471c6bb4b94f76531358d3a7793062a0fb00d72ebfb934e8ae1d4f5ced6bb34c8e7f60996f90076 + languageName: node + linkType: hard + "pretty-format@npm:^24.9.0": version: 24.9.0 resolution: "pretty-format@npm:24.9.0" @@ -19835,7 +19789,7 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.0.3, pretty-format@npm:^29.7.0": +"pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" dependencies: @@ -19876,13 +19830,6 @@ __metadata: languageName: node linkType: hard -"private@npm:^0.1.8": - version: 0.1.8 - resolution: "private@npm:0.1.8" - checksum: 10c0/829a23723e5fd3105c72b2dadeeb65743a430f7e6967a8a6f3e49392a1b3ea52975a255376d8c513b0c988bdf162f1a5edf9d9bac27d1ab11f8dba8cdb58880e - languageName: node - linkType: hard - "proc-log@npm:^4.0.0": version: 4.2.0 resolution: "proc-log@npm:4.2.0" @@ -20140,17 +20087,6 @@ __metadata: languageName: node linkType: hard -"randomatic@npm:^3.0.0": - version: 3.1.1 - resolution: "randomatic@npm:3.1.1" - dependencies: - is-number: "npm:^4.0.0" - kind-of: "npm:^6.0.0" - math-random: "npm:^1.0.1" - checksum: 10c0/4b1da4b8e234d3d0bd2294a42541dfa03edbde85ee06fa0722e2b004e845da197d72fa7995723d32ea7d7402823ea62550034118cf22e94638560a509cec5bfc - languageName: node - linkType: hard - "randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -20367,6 +20303,20 @@ __metadata: languageName: node linkType: hard +"react-is-18@npm:react-is@^18.3.1, react-is@npm:^18.0.0": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 + languageName: node + linkType: hard + +"react-is-19@npm:react-is@^19.2.5": + version: 19.2.7 + resolution: "react-is@npm:19.2.7" + checksum: 10c0/419fe54d5bd7fdf5414a5bb7bd9a1e0e36f9fae28ffb4cb73290fbe342bde15d8584a90d1db62547f6aa03018dce517b178a041abb522136cd4b4b51b4e94c83 + languageName: node + linkType: hard + "react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0, react-is@npm:^16.8.3, react-is@npm:^16.8.4": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -20374,13 +20324,6 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0": - version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 - languageName: node - linkType: hard - "react-is@npm:^19.1.0": version: 19.2.3 resolution: "react-is@npm:19.2.3" @@ -20388,13 +20331,6 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^19.2.3": - version: 19.2.6 - resolution: "react-is@npm:19.2.6" - checksum: 10c0/263177f370fc156b279d22570dd6e922a0ad641a4a426a4cb70284b8003b00ef532d59f2beca1d22a1ca0b37f85f9077d7733ca5d344ebecd2942e9bc2a2a3c0 - languageName: node - linkType: hard - "react-json-view@npm:^1.21.3": version: 1.21.3 resolution: "react-json-view@npm:1.21.3" @@ -20527,14 +20463,17 @@ __metadata: resolution: "react-native-paper-example@workspace:example" dependencies: "@babel/core": "npm:^7.29.0" + "@expo/metro-config": "npm:~56.0.0" "@react-native-async-storage/async-storage": "npm:2.2.0" "@react-native-masked-view/masked-view": "npm:0.3.2" "@react-navigation/bottom-tabs": "npm:^8.0.0-alpha.33" "@react-navigation/drawer": "npm:^8.0.0-alpha.34" + "@react-navigation/elements": "npm:3.0.0-alpha.31" "@react-navigation/native": "npm:^8.0.0-alpha.28" "@react-navigation/native-stack": "npm:^8.0.0-alpha.34" babel-preset-expo: "npm:~56.0.0" expo: "npm:~56.0.0" + expo-constants: "npm:~56.0.18" expo-crypto: "npm:~56.0.3" expo-dev-client: "npm:~56.0.13" expo-font: "npm:~56.0.5" @@ -20561,56 +20500,45 @@ __metadata: resolution: "react-native-paper@workspace:." dependencies: "@babel/core": "npm:^7.29.0" + "@babel/parser": "npm:^7.29.0" "@babel/plugin-proposal-export-namespace-from": "npm:^7.18.9" - "@babel/runtime": "npm:^7.29.0" - "@callstack/eslint-config": "npm:^13.0.2" + "@babel/types": "npm:^7.29.0" + "@callstack/eslint-config": "npm:^15.0.0" "@callstack/react-theme-provider": "npm:^3.0.9" "@commitlint/config-conventional": "npm:^8.3.4" + "@eslint/js": "npm:9.39.4" "@jest/globals": "npm:^29.7.0" "@react-native-vector-icons/material-design-icons": "npm:^12.0.0" "@react-native/babel-preset": "npm:^0.85.3" "@react-native/jest-preset": "npm:^0.85.3" "@react-navigation/native": "npm:^8.0.0-alpha.28" "@release-it/conventional-changelog": "npm:^1.1.0" - "@testing-library/jest-native": "npm:^5.4.1" - "@testing-library/react-native": "npm:11.5.0" + "@testing-library/react-native": "npm:^14.0.0" "@types/color": "npm:^3.0.0" - "@types/jest": "npm:^29.2.1" - "@types/node": "npm:^13.1.0" - "@types/react-dom": "npm:^19.1.1" - "@types/react-native-vector-icons": "npm:^6.4.18" - "@types/react-test-renderer": "npm:^19.1.0" - "@typescript-eslint/eslint-plugin": "npm:^5.41.0" - "@typescript-eslint/parser": "npm:^5.41.0" + "@types/node": "npm:^24.0.0" + "@types/react": "npm:^19.2.7" all-contributors-cli: "npm:^6.24.0" - babel-cli: "npm:^6.26.0" - babel-core: "npm:^7.0.0-bridge.0" babel-jest: "npm:^29.6.3" - babel-loader: "npm:^8.2.3" babel-test: "npm:^0.1.1" - chalk: "npm:^4.0.0" color: "npm:^3.1.2" commitlint: "npm:^8.3.4" - conventional-changelog-cli: "npm:^2.0.11" - dedent: "npm:^0.7.0" - eslint: "npm:8.31.0" + eslint: "npm:9.39.4" eslint-plugin-flowtype: "npm:^8.0.3" - eslint-plugin-local-rules: "npm:^1.3.2" - glob: "npm:^7.1.3" - husky: "npm:^1.3.1" + eslint-plugin-testing-library: "npm:^7.16.2" jest: "npm:^29.6.3" jest-file-snapshot: "npm:^0.3.2" + lefthook: "npm:^2.1.9" + prettier: "npm:^3.8.4" react: "npm:19.2.3" - react-dom: "npm:19.2.3" react-native: "npm:0.85.3" react-native-builder-bob: "npm:^0.42.1" react-native-reanimated: "npm:4.3.1" react-native-safe-area-context: "npm:5.7.0" react-native-worklets: "npm:0.8.3" - react-test-renderer: "npm:19.2.3" release-it: "npm:^13.4.0" - rimraf: "npm:^3.0.2" + test-renderer: "npm:1.2.0" typescript: "npm:5.8.3" + typescript-eslint: "npm:^8.61.1" use-latest-callback: "npm:^0.2.3" peerDependencies: react: "*" @@ -20780,6 +20708,17 @@ __metadata: languageName: node linkType: hard +"react-reconciler@npm:~0.33.0": + version: 0.33.0 + resolution: "react-reconciler@npm:0.33.0" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.0 + checksum: 10c0/3f7b27ea8d0ff4c8bf0e402a285e1af9b7d0e6f4c1a70a28f4384938bc1130bc82a90a31df0b79ef5e380e2e55e2598bd90b4dbf802b1203d735ba0355817d3a + languageName: node + linkType: hard + "react-refresh@npm:^0.14.0, react-refresh@npm:^0.14.2": version: 0.14.2 resolution: "react-refresh@npm:0.14.2" @@ -20835,18 +20774,6 @@ __metadata: languageName: node linkType: hard -"react-test-renderer@npm:19.2.3": - version: 19.2.3 - resolution: "react-test-renderer@npm:19.2.3" - dependencies: - react-is: "npm:^19.2.3" - scheduler: "npm:^0.27.0" - peerDependencies: - react: ^19.2.3 - checksum: 10c0/842b82239dbddbc536083a6260c3e1b0507c02a3400bd05879fc19160468fd0f8ab79fec5dceffa6113b131835cc7621212f8415b46ea5156ab66bbfd7e24297 - languageName: node - linkType: hard - "react-textarea-autosize@npm:^8.3.2": version: 8.5.9 resolution: "react-textarea-autosize@npm:8.5.9" @@ -20930,17 +20857,6 @@ __metadata: languageName: node linkType: hard -"read-pkg@npm:^4.0.1": - version: 4.0.1 - resolution: "read-pkg@npm:4.0.1" - dependencies: - normalize-package-data: "npm:^2.3.2" - parse-json: "npm:^4.0.0" - pify: "npm:^3.0.0" - checksum: 10c0/70c934969aba7267a229871345fe1714e1416085c3db9cba8f7fcd78fba3bd38cef7f08a9d661880e552a5722baf8feaf792875b3108d251a5b6dd94369c752d - languageName: node - linkType: hard - "readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.0.6, readable-stream@npm:^3.4.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" @@ -20952,7 +20868,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:~2.3.6": +"readable-stream@npm:^2.0.1, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -20967,17 +20883,6 @@ __metadata: languageName: node linkType: hard -"readdirp@npm:^2.0.0": - version: 2.2.1 - resolution: "readdirp@npm:2.2.1" - dependencies: - graceful-fs: "npm:^4.1.11" - micromatch: "npm:^3.1.10" - readable-stream: "npm:^2.0.2" - checksum: 10c0/770d177372ff2212d382d425d55ca48301fcbf3231ab3827257bbcca7ff44fb51fe4af6acc2dda8512dc7f29da390e9fbea5b2b3fc724b86e85cc828395b7797 - languageName: node - linkType: hard - "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -21123,15 +21028,6 @@ __metadata: languageName: node linkType: hard -"regex-cache@npm:^0.4.2": - version: 0.4.4 - resolution: "regex-cache@npm:0.4.4" - dependencies: - is-equal-shallow: "npm:^0.1.3" - checksum: 10c0/d3e374638b577ae560a445c7f36b801cab4815f7d25e1a9afc2328c01d5c0d203ea0d24e95635843e25ebc54e061f1790f7d47aa3839c49f67bbc53358ad9066 - languageName: node - linkType: hard - "regex-not@npm:^1.0.0, regex-not@npm:^1.0.2": version: 1.0.2 resolution: "regex-not@npm:1.0.2" @@ -21156,13 +21052,6 @@ __metadata: languageName: node linkType: hard -"regexpp@npm:^3.2.0": - version: 3.2.0 - resolution: "regexpp@npm:3.2.0" - checksum: 10c0/d1da82385c8754a1681416b90b9cca0e21b4a2babef159099b88f640637d789c69011d0bc94705dacab85b81133e929d027d85210e8b8b03f8035164dbc14710 - languageName: node - linkType: hard - "regexpu-core@npm:^6.3.1": version: 6.4.0 resolution: "regexpu-core@npm:6.4.0" @@ -21417,22 +21306,13 @@ __metadata: languageName: node linkType: hard -"repeat-string@npm:^1.5.2, repeat-string@npm:^1.5.4, repeat-string@npm:^1.6.1": +"repeat-string@npm:^1.5.4, repeat-string@npm:^1.6.1": version: 1.6.1 resolution: "repeat-string@npm:1.6.1" checksum: 10c0/87fa21bfdb2fbdedc44b9a5b118b7c1239bdd2c2c1e42742ef9119b7d412a5137a1d23f1a83dc6bb686f4f27429ac6f542e3d923090b44181bafa41e8ac0174d languageName: node linkType: hard -"repeating@npm:^2.0.0": - version: 2.0.1 - resolution: "repeating@npm:2.0.1" - dependencies: - is-finite: "npm:^1.0.0" - checksum: 10c0/7f5cd293ec47d9c074ef0852800d5ff5c49028ce65242a7528d84f32bd2fe200b142930562af58c96d869c5a3046e87253030058e45231acaa129c1a7087d2e7 - languageName: node - linkType: hard - "replace-ext@npm:1.0.0": version: 1.0.0 resolution: "replace-ext@npm:1.0.0" @@ -21777,15 +21657,6 @@ __metadata: languageName: node linkType: hard -"run-node@npm:^1.0.0": - version: 1.0.0 - resolution: "run-node@npm:1.0.0" - bin: - run-node: run-node - checksum: 10c0/ab32a065194903e5cd2e4c01e4a5568d22720b4eaa5c853d5386e8d116c59c313e84591f0375913dbe6cc973c2181aed4f54d6b32dba9f7d0d5d01a762de8b6b - languageName: node - linkType: hard - "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -22004,13 +21875,6 @@ __metadata: languageName: node linkType: hard -"semver-compare@npm:^1.0.0": - version: 1.0.0 - resolution: "semver-compare@npm:1.0.0" - checksum: 10c0/9ef4d8b81847556f0865f46ddc4d276bace118c7cb46811867af82e837b7fc473911981d5a0abc561fa2db487065572217e5b06e18701c4281bcdd2a1affaff1 - languageName: node - linkType: hard - "semver-diff@npm:^3.1.1": version: 3.1.1 resolution: "semver-diff@npm:3.1.1" @@ -22437,13 +22301,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^1.0.0": - version: 1.0.0 - resolution: "slash@npm:1.0.0" - checksum: 10c0/3944659885d905480f98810542fd314f3e1006eaad25ec78227a7835a469d9ed66fc3dd90abc7377dd2e71f4b5473e8f766bd08198fdd25152a80792e9ed464c - languageName: node - linkType: hard - "slash@npm:^2.0.0": version: 2.0.0 resolution: "slash@npm:2.0.0" @@ -22616,15 +22473,6 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:^0.4.15": - version: 0.4.18 - resolution: "source-map-support@npm:0.4.18" - dependencies: - source-map: "npm:^0.5.6" - checksum: 10c0/cd9f0309c1632b1e01a7715a009e0b036d565f3af8930fa8cda2a06aeec05ad1d86180e743b7e1f02cc3c97abe8b6d8de7c3878c2d8e01e86e17f876f7ecf98e - languageName: node - linkType: hard - "source-map-support@npm:^0.5.16, source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -22649,7 +22497,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.5.0, source-map@npm:^0.5.6, source-map@npm:^0.5.7": +"source-map@npm:^0.5.0, source-map@npm:^0.5.6": version: 0.5.7 resolution: "source-map@npm:0.5.7" checksum: 10c0/904e767bb9c494929be013017380cbba013637da1b28e5943b566031e29df04fba57edf3f093e0914be094648b577372bd8ad247fa98cfba9c600794cd16b599 @@ -23075,15 +22923,6 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^3.0.0": - version: 3.0.1 - resolution: "strip-ansi@npm:3.0.1" - dependencies: - ansi-regex: "npm:^2.0.0" - checksum: 10c0/f6e7fbe8e700105dccf7102eae20e4f03477537c74b286fd22cfc970f139002ed6f0d9c10d0e21aa9ed9245e0fa3c9275930e8795c5b947da136e4ecb644a70f - languageName: node - linkType: hard - "strip-ansi@npm:^5.0.0, strip-ansi@npm:^5.1.0, strip-ansi@npm:^5.2.0": version: 5.2.0 resolution: "strip-ansi@npm:5.2.0" @@ -23171,7 +23010,7 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": +"strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd @@ -23254,13 +23093,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^2.0.0": - version: 2.0.0 - resolution: "supports-color@npm:2.0.0" - checksum: 10c0/570e0b63be36cccdd25186350a6cb2eaad332a95ff162fa06d9499982315f2fe4217e69dd98e862fbcd9c81eaff300a825a1fe7bf5cc752e5b84dfed042b0dda - languageName: node - linkType: hard - "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -23330,6 +23162,15 @@ __metadata: languageName: node linkType: hard +"synckit@npm:^0.11.13": + version: 0.11.13 + resolution: "synckit@npm:0.11.13" + dependencies: + "@pkgr/core": "npm:^0.3.6" + checksum: 10c0/5a6c19f4f79045aaa7994106401bff6dbe7cca23a6d0a0723ff14eb8b1bebeb4a71729118f6914905598e304ea2fa13509885e11ba07d92e7cb68a06740cb328 + languageName: node + linkType: hard + "tapable@npm:^1.0.0": version: 1.1.3 resolution: "tapable@npm:1.1.3" @@ -23357,23 +23198,6 @@ __metadata: languageName: node linkType: hard -"temp-dir@npm:^2.0.0": - version: 2.0.0 - resolution: "temp-dir@npm:2.0.0" - checksum: 10c0/b1df969e3f3f7903f3426861887ed76ba3b495f63f6d0c8e1ce22588679d9384d336df6064210fda14e640ed422e2a17d5c40d901f60e161c99482d723f4d309 - languageName: node - linkType: hard - -"tempfile@npm:^3.0.0": - version: 3.0.0 - resolution: "tempfile@npm:3.0.0" - dependencies: - temp-dir: "npm:^2.0.0" - uuid: "npm:^3.3.2" - checksum: 10c0/1547e7b5c199868f9b8bf0e321f4cbdc3c57c05c03ff0b1a93deae94edc529ac1499ec7b6d7ebf4e79f469194262c4606785b47ec77d979eaece42c46db72932 - languageName: node - linkType: hard - "term-size@npm:^2.1.0": version: 2.2.1 resolution: "term-size@npm:2.2.1" @@ -23438,6 +23262,18 @@ __metadata: languageName: node linkType: hard +"test-renderer@npm:1.2.0": + version: 1.2.0 + resolution: "test-renderer@npm:1.2.0" + dependencies: + "@types/react-reconciler": "npm:~0.33.0" + react-reconciler: "npm:~0.33.0" + peerDependencies: + react: ^19.0.0 + checksum: 10c0/f6ac10a7906d46fd67e3a2ece1964a71f31ca15d70e8e18e2d06170fa355f8e042599f8956ae55b2b5ce15644d5574c580e1fe751954341ac9327a631215d77e + languageName: node + linkType: hard + "text-extensions@npm:^1.0.0": version: 1.9.0 resolution: "text-extensions@npm:1.9.0" @@ -23568,13 +23404,6 @@ __metadata: languageName: node linkType: hard -"to-fast-properties@npm:^1.0.3": - version: 1.0.3 - resolution: "to-fast-properties@npm:1.0.3" - checksum: 10c0/78974a4f4528700d18e4c2bbf0b1fb1b19862dcc20a18dc5ed659843dea2dff4f933d167a11d3819865c1191042003aea65f7f035791af9e65d070f2e05af787 - languageName: node - linkType: hard - "to-object-path@npm:^0.3.0": version: 0.3.0 resolution: "to-object-path@npm:0.3.0" @@ -23687,13 +23516,6 @@ __metadata: languageName: node linkType: hard -"trim-right@npm:^1.0.1": - version: 1.0.1 - resolution: "trim-right@npm:1.0.1" - checksum: 10c0/71989ec179c6b42a56e03db68e60190baabf39d32d4e1252fa1501c4e478398ae29d7191beffe015b9d9dc76f04f4b3a946bdb9949ad6b0c0b0c5db65f3eb672 - languageName: node - linkType: hard - "trim-trailing-lines@npm:^1.0.0": version: 1.1.4 resolution: "trim-trailing-lines@npm:1.1.4" @@ -23715,6 +23537,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.5.0": + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10c0/767849383c114e7f1971fa976b20e73ac28fd0c70d8d65c0004790bf4d8f89888c7e4cf6d5949f9c1beae9bc3c64835bef77bbe27fddf45a3c7b60cebcf85c8c + languageName: node + linkType: hard + "tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" @@ -23727,7 +23558,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1, tslib@npm:^1.9.0": +"tslib@npm:^1.9.0": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 @@ -23741,17 +23572,6 @@ __metadata: languageName: node linkType: hard -"tsutils@npm:^3.21.0": - version: 3.21.0 - resolution: "tsutils@npm:3.21.0" - dependencies: - tslib: "npm:^1.8.1" - peerDependencies: - typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - checksum: 10c0/02f19e458ec78ead8fffbf711f834ad8ecd2cc6ade4ec0320790713dccc0a412b99e7fd907c4cda2a1dc602c75db6f12e0108e87a5afad4b2f9e90a24cabd5a2 - languageName: node - linkType: hard - "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -23896,6 +23716,21 @@ __metadata: languageName: node linkType: hard +"typescript-eslint@npm:^8.61.1": + version: 8.61.1 + resolution: "typescript-eslint@npm:8.61.1" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.61.1" + "@typescript-eslint/parser": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" + "@typescript-eslint/utils": "npm:8.61.1" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/cf27a5c25a6d492a77ee72d98ff0e708b25553c4450483c627018556bee5b7b17066c0696947aa968ed24fd07ebc095b449dea79f8a7a4c65bccdb6aaf8dc09d + languageName: node + linkType: hard + "typescript@npm:5.8.3": version: 5.8.3 resolution: "typescript@npm:5.8.3" @@ -23993,6 +23828,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~7.18.0": + version: 7.18.2 + resolution: "undici-types@npm:7.18.2" + checksum: 10c0/85a79189113a238959d7a647368e4f7c5559c3a404ebdb8fc4488145ce9426fcd82252a844a302798dfc0e37e6fb178ff481ed03bc4caf634c5757d9ef43521d + languageName: node + linkType: hard + "undici@npm:^7.12.0": version: 7.18.2 resolution: "undici@npm:7.18.2" @@ -24524,15 +24366,6 @@ __metadata: languageName: node linkType: hard -"user-home@npm:^1.1.1": - version: 1.1.1 - resolution: "user-home@npm:1.1.1" - bin: - user-home: cli.js - checksum: 10c0/9d80a5df3bfea008e4d17b1465e8eb4ac7472ba02766feb242e84349b877f74e302838c85a622d4ba78665c2378b654fe1b0d27cf912c917b5536eb4778f8804 - languageName: node - linkType: hard - "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -24570,15 +24403,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^3.3.2": - version: 3.4.0 - resolution: "uuid@npm:3.4.0" - bin: - uuid: ./bin/uuid - checksum: 10c0/1c13950df865c4f506ebfe0a24023571fa80edf2e62364297a537c80af09c618299797bbf2dbac6b1f8ae5ad182ba474b89db61e0e85839683991f7e08795347 - languageName: node - linkType: hard - "uuid@npm:^7.0.3": version: 7.0.3 resolution: "uuid@npm:7.0.3" @@ -24599,15 +24423,6 @@ __metadata: languageName: node linkType: hard -"v8flags@npm:^2.1.1": - version: 2.1.1 - resolution: "v8flags@npm:2.1.1" - dependencies: - user-home: "npm:^1.1.1" - checksum: 10c0/ab5e478e661826a16c261515367091befa2edcdf0819d3a2f25013f80328e5998aed05429d2b28c1bc58e7df0eedc8377dc5829ba44811626fbadd5f6877c31f - languageName: node - linkType: hard - "validate-npm-package-license@npm:^3.0.1": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" From 466d9c608d2cb3dead7831b64c925f4523419b0e Mon Sep 17 00:00:00 2001 From: MrMuzyk Date: Tue, 23 Jun 2026 18:11:33 +0200 Subject: [PATCH 49/94] refactor: migrate accessibility props to aria props (#5005) --- src/components/ActivityIndicator.tsx | 4 +- src/components/Appbar/AppbarAction.tsx | 6 +- src/components/Appbar/AppbarBackAction.tsx | 6 +- src/components/Appbar/AppbarContent.tsx | 23 +--- src/components/Banner.tsx | 4 +- .../BottomNavigation/BottomNavigation.tsx | 8 +- .../BottomNavigation/BottomNavigationBar.tsx | 20 +-- src/components/Button/Button.tsx | 16 +-- src/components/Checkbox/Checkbox.tsx | 14 +- src/components/Checkbox/CheckboxItem.tsx | 14 +- src/components/Chip/Chip.tsx | 23 ++-- .../DataTable/DataTablePagination.tsx | 22 +-- src/components/Dialog/DialogTitle.tsx | 2 +- src/components/Drawer/DrawerCollapsedItem.tsx | 14 +- src/components/Drawer/DrawerItem.tsx | 10 +- src/components/FAB/Extended.tsx | 36 +++-- src/components/FAB/FAB.tsx | 34 +++-- src/components/FAB/Menu.tsx | 33 +++-- src/components/FAB/Shell.tsx | 36 +++-- src/components/IconButton/IconButton.tsx | 13 +- src/components/List/ListAccordion.tsx | 10 +- src/components/Menu/Menu.tsx | 6 +- src/components/Menu/MenuItem.tsx | 39 ++++-- src/components/Modal.tsx | 8 +- src/components/ProgressBar.tsx | 12 +- .../RadioButton/RadioButtonAndroid.tsx | 7 +- .../RadioButton/RadioButtonGroup.tsx | 2 +- src/components/RadioButton/RadioButtonIOS.tsx | 7 +- .../RadioButton/RadioButtonItem.tsx | 14 +- src/components/Searchbar.tsx | 14 +- .../SegmentedButtons/SegmentedButtonItem.tsx | 11 +- .../SegmentedButtons/SegmentedButtons.tsx | 4 +- src/components/Snackbar.tsx | 6 +- src/components/Switch/Switch.tsx | 14 +- src/components/TextInput/TextInput.tsx | 2 +- src/components/TextInput/utils.ts | 9 +- src/components/ToggleButton/ToggleButton.tsx | 9 +- src/components/Tooltip/Tooltip.tsx | 2 +- .../__tests__/Appbar/Appbar.test.tsx | 8 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 21 +-- .../__tests__/Checkbox/CheckboxItem.test.tsx | 2 +- .../__snapshots__/Checkbox.test.tsx.snap | 14 +- .../__snapshots__/CheckboxItem.test.tsx.snap | 4 +- src/components/__tests__/FAB.test.tsx | 25 ++-- src/components/__tests__/FABExtended.test.tsx | 19 ++- src/components/__tests__/MenuItem.test.tsx | 20 +-- src/components/__tests__/ProgressBar.test.tsx | 6 + .../__snapshots__/RadioButton.test.tsx.snap | 8 +- .../RadioButtonGroup.test.tsx.snap | 4 +- .../RadioButtonItem.test.tsx.snap | 16 +-- src/components/__tests__/Switch.test.tsx | 17 ++- src/components/__tests__/TextInput.test.tsx | 101 ++++++-------- .../ActivityIndicator.test.tsx.snap | 32 ++--- .../__snapshots__/Banner.test.tsx.snap | 32 ++--- .../BottomNavigation.test.tsx.snap | 130 +++++++++--------- .../__snapshots__/Button.test.tsx.snap | 36 +++-- .../__snapshots__/Chip.test.tsx.snap | 16 +-- .../__snapshots__/DataTable.test.tsx.snap | 72 ++++------ .../__snapshots__/DrawerItem.test.tsx.snap | 6 +- .../__tests__/__snapshots__/FAB.test.tsx.snap | 28 ++-- .../__snapshots__/FABExtended.test.tsx.snap | 24 ++-- .../__snapshots__/FABMenu.test.tsx.snap | 86 ++++++------ .../__snapshots__/IconButton.test.tsx.snap | 25 +--- .../__snapshots__/ListAccordion.test.tsx.snap | 10 +- .../__snapshots__/ListItem.test.tsx.snap | 2 +- .../__snapshots__/Menu.test.tsx.snap | 22 +-- .../__snapshots__/MenuItem.test.tsx.snap | 10 +- .../__snapshots__/ProgressBar.test.tsx.snap | 63 +++------ .../__snapshots__/Searchbar.test.tsx.snap | 34 ++--- .../SegmentedButton.test.tsx.snap | 4 +- .../__snapshots__/Snackbar.test.tsx.snap | 10 +- .../__snapshots__/Switch.test.tsx.snap | 12 +- .../__snapshots__/TextInput.test.tsx.snap | 80 +++-------- .../__snapshots__/ToggleButton.test.tsx.snap | 22 +-- 74 files changed, 713 insertions(+), 822 deletions(-) diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 8faa54de58..58ef633c9b 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -146,8 +146,8 @@ const ActivityIndicator = ({ style={[styles.container, style]} {...rest} accessible - accessibilityRole="progressbar" - accessibilityState={{ busy: animating }} + role="progressbar" + aria-busy={animating} > & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -79,7 +79,7 @@ const AppbarAction = ({ icon, disabled, onPress, - accessibilityLabel, + 'aria-label': ariaLabel, isLeading, theme: themeOverrides, ref, @@ -101,7 +101,7 @@ const AppbarAction = ({ iconColor={actionIconColor} icon={icon} disabled={disabled} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} animated ref={ref} {...rest} diff --git a/src/components/Appbar/AppbarBackAction.tsx b/src/components/Appbar/AppbarBackAction.tsx index 80c7df2f95..2835c27c91 100644 --- a/src/components/Appbar/AppbarBackAction.tsx +++ b/src/components/Appbar/AppbarBackAction.tsx @@ -31,7 +31,7 @@ export type Props = $Omit< /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -58,12 +58,12 @@ export type Props = $Omit< * ``` */ const AppbarBackAction = ({ - accessibilityLabel = 'Back', + 'aria-label': ariaLabel = 'Back', ref, ...rest }: Props) => ( @@ -162,13 +153,9 @@ const AppbarContent = ({ if (onPress) { return ( - // eslint-disable-next-line react-native-a11y/has-accessibility-props {children} diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index cd8dbd8663..df7e28cc76 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -23,7 +23,7 @@ export type BaseRoute = { focusedIcon?: IconSource; unfocusedIcon?: IconSource; badge?: string | number | boolean; - accessibilityLabel?: string; + 'aria-label'?: string; testID?: string; lazy?: boolean; }; @@ -77,7 +77,7 @@ export type Props = { * - `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x * - `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3 * - `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text. - * - `accessibilityLabel`: accessibility label for the tab button + * - `aria-label`: accessibility label for the tab button * - `testID`: test id for the tab button * * Example: @@ -164,7 +164,7 @@ export type Props = { renderTouchable?: (props: TouchableProps) => React.ReactNode; /** * Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab. - * Uses `route.accessibilityLabel` by default. + * Uses `route['aria-label']` by default. */ getAccessibilityLabel?: (props: { route: Route }) => string | undefined; /** @@ -523,7 +523,7 @@ const BottomNavigation = ({ = { * - `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x * - `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3 * - `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text. - * - `accessibilityLabel`: accessibility label for the tab button + * - `aria-label`: accessibility label for the tab button * - `testID`: test id for the tab button * * Example: @@ -131,7 +134,7 @@ export type Props = { renderTouchable?: (props: TouchableProps) => React.ReactNode; /** * Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab. - * Uses `route.accessibilityLabel` by default. + * Uses `route['aria-label']` by default. */ getAccessibilityLabel?: (props: { route: Route }) => string | undefined; /** @@ -305,8 +308,7 @@ const BottomNavigationBar = ({ ), getLabelText = ({ route }: { route: Route }) => route.title, getBadge = ({ route }: { route: Route }) => route.badge, - getAccessibilityLabel = ({ route }: { route: Route }) => - route.accessibilityLabel, + getAccessibilityLabel = ({ route }: { route: Route }) => route['aria-label'], getTestID = ({ route }: { route: Route }) => route.testID, activeColor, inactiveColor, @@ -507,7 +509,7 @@ const BottomNavigationBar = ({ maxWidth: maxTabBarWidth, }, ]} - accessibilityRole={'tablist'} + role={'tablist'} testID={`${testID}-content-wrapper`} > {routes.map((route, index) => { @@ -585,9 +587,9 @@ const BottomNavigationBar = ({ onPress: () => onTabPress(eventForIndex(index)), onLongPress: () => onTabLongPress?.(eventForIndex(index)), testID: getTestID({ route }), - accessibilityLabel: getAccessibilityLabel({ route }), - accessibilityRole: Platform.OS === 'ios' ? 'button' : 'tab', - accessibilityState: { selected: focused }, + 'aria-label': getAccessibilityLabel({ route }), + role: Platform.OS === 'ios' ? 'button' : 'tab', + 'aria-selected': focused, style: [styles.item, styles.v3Item], children: ( , 'mode'> & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Accessibility hint for the button. This is read by the screen reader when the user taps the button. */ @@ -88,7 +88,7 @@ export type Props = $Omit, 'mode'> & { /** * Accessibility role for the button. The "button" role is set by default. */ - accessibilityRole?: AccessibilityRole; + role?: Role; /** * Function to execute on press. */ @@ -169,9 +169,9 @@ const Button = ({ buttonColor: customButtonColor, textColor: customTextColor, children, - accessibilityLabel, + 'aria-label': ariaLabel, accessibilityHint, - accessibilityRole = 'button', + role = 'button', hitSlop, onPress, onPressIn, @@ -352,10 +352,10 @@ const Button = ({ onPressIn={hasPassedTouchHandler ? handlePressIn : undefined} onPressOut={hasPassedTouchHandler ? handlePressOut : undefined} delayLongPress={delayLongPress} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} accessibilityHint={accessibilityHint} - accessibilityRole={accessibilityRole} - accessibilityState={{ disabled }} + role={role} + aria-disabled={disabled} accessible={accessible} hitSlop={hitSlop} disabled={disabled} diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index b893a6530c..f93b48becc 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -247,14 +247,12 @@ const Checkbox = ({ rest.accessible === false ? {} : { - accessibilityRole: 'checkbox' as const, - accessibilityState: { - disabled: !!disabled, - checked: (status === 'indeterminate' - ? 'mixed' - : status === 'checked') as boolean | 'mixed', - }, - accessibilityLiveRegion: 'polite' as const, + role: 'checkbox' as const, + 'aria-disabled': !!disabled, + 'aria-checked': (status === 'indeterminate' + ? 'mixed' + : status === 'checked') as boolean | 'mixed', + 'aria-live': 'polite' as const, }; return ( diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 50e758a412..e8bd23a59b 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -44,7 +44,7 @@ export type Props = { /** * Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Custom color for unchecked checkbox. */ @@ -129,7 +129,7 @@ const CheckboxItem = ({ theme: themeOverrides, testID, position = 'trailing', - accessibilityLabel = label, + 'aria-label': ariaLabel = label, disabled, labelVariant = 'bodyLarge', labelMaxFontSizeMultiplier = 1.5, @@ -154,12 +154,10 @@ const CheckboxItem = ({ return ( , 'mode'> & { /** * Accessibility label for the chip. This is read by the screen reader when the user taps the chip. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon. */ @@ -178,8 +177,8 @@ const Chip = ({ selected = false, disabled = false, background, - accessibilityLabel, - accessibilityRole = 'button', + 'aria-label': ariaLabel, + role = 'button', closeIconAccessibilityLabel = 'Close', onPress, onLongPress, @@ -263,11 +262,6 @@ const Chip = ({ disabled, }); - const accessibilityState: AccessibilityState = { - selected, - disabled, - }; - const elevationStyle = elevation; const multiplier = compact ? 1.5 : 2; const labelSpacings = { @@ -312,9 +306,10 @@ const Chip = ({ onPressOut={hasPassedTouchHandler ? handlePressOut : undefined} delayLongPress={delayLongPress} disabled={disabled} - accessibilityLabel={accessibilityLabel} - accessibilityRole={accessibilityRole} - accessibilityState={accessibilityState} + aria-label={ariaLabel} + role={role} + aria-selected={selected} + aria-disabled={disabled} testID={testID} theme={theme} hitSlop={hitSlop} @@ -401,8 +396,8 @@ const Chip = ({ {closeIcon ? ( diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 862185e9c5..32f4b8b4fb 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -29,7 +29,7 @@ export type Props = ViewProps & /** * AccessibilityLabel for `label`. */ - accessibilityLabel?: string; + 'aria-label'?: string; style?: StyleProp; /** * @optional @@ -106,7 +106,7 @@ const PaginationControls = ({ iconColor={textColor} disabled={page === 0} onPress={() => onPageChange(0)} - accessibilityLabel="page-first" + aria-label="page-first" theme={theme} /> ) : null} @@ -122,7 +122,7 @@ const PaginationControls = ({ iconColor={textColor} disabled={page === 0} onPress={() => onPageChange(page - 1)} - accessibilityLabel="chevron-left" + aria-label="chevron-left" theme={theme} /> onPageChange(page + 1)} - accessibilityLabel="chevron-right" + aria-label="chevron-right" theme={theme} /> {showFastPaginationControls ? ( @@ -153,7 +153,7 @@ const PaginationControls = ({ iconColor={textColor} disabled={numberOfPages === 0 || page === numberOfPages - 1} onPress={() => onPageChange(numberOfPages - 1)} - accessibilityLabel="page-last" + aria-label="page-last" theme={theme} /> ) : null} @@ -266,7 +266,7 @@ const PaginationDropdown = ({ */ const DataTablePagination = ({ label, - accessibilityLabel, + 'aria-label': accessibilityLabel, page, numberOfPages, onPageChange, @@ -287,19 +287,21 @@ const DataTablePagination = ({ {numberOfItemsPerPageList && numberOfItemsPerPage && onItemsPerPageChange && ( {label} diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index f869887205..bdf5021379 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -63,7 +63,7 @@ const DialogTitle = ({ return ( diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index 3f79fce421..1542eafe36 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -54,7 +54,7 @@ export type Props = ViewProps & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; style?: StyleProp; /** * @optional @@ -102,7 +102,7 @@ const DrawerCollapsedItem = ({ style, onPress, disabled, - accessibilityLabel, + 'aria-label': ariaLabel, badge = false, testID = 'drawer-collapsed-item', labelMaxFontSizeMultiplier, @@ -164,17 +164,13 @@ const DrawerCollapsedItem = ({ return ( - {/* eslint-disable-next-line react-native-a11y/has-accessibility-props */} diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 95ef989248..88535ba162 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -46,7 +46,7 @@ export type Props = ViewProps & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Callback which returns a React element to display on the right side. For instance a Badge. */ @@ -94,7 +94,7 @@ const DrawerItem = ({ style, onPress, background, - accessibilityLabel, + 'aria-label': ariaLabel, right, labelMaxFontSizeMultiplier, hitSlop, @@ -124,9 +124,9 @@ const DrawerItem = ({ { backgroundColor, borderRadius }, style, ]} - accessibilityRole="button" - accessibilityState={{ selected: active }} - accessibilityLabel={accessibilityLabel} + role="button" + aria-selected={active} + aria-label={ariaLabel} theme={theme} hitSlop={hitSlop} > diff --git a/src/components/FAB/Extended.tsx b/src/components/FAB/Extended.tsx index d15fe6a6fa..06a67cab1f 100644 --- a/src/components/FAB/Extended.tsx +++ b/src/components/FAB/Extended.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import type { - AccessibilityState, ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, @@ -71,11 +70,24 @@ export type Props = { /** * Accessibility label. Falls back to `label` if unset. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** - * Accessibility state forwarded to the underlying button. + * Indicates whether the element is checked. Accepts `true`, `false`, + * or `'mixed'` for an indeterminate state. */ - accessibilityState?: AccessibilityState; + 'aria-checked'?: boolean | 'mixed'; + /** + * Indicates whether the element is selected. + */ + 'aria-selected'?: boolean; + /** + * Indicates whether the element is currently busy (e.g. loading). + */ + 'aria-busy'?: boolean; + /** + * Indicates whether the element's controlled content is expanded. + */ + 'aria-expanded'?: boolean; /** * Specifies the largest possible scale a label font can reach. */ @@ -148,8 +160,11 @@ const Extended = ({ expanded, visible = true, onPress, - accessibilityLabel = label, - accessibilityState, + 'aria-label': ariaLabel = label, + 'aria-checked': ariaChecked, + 'aria-selected': ariaSelected, + 'aria-busy': ariaBusy, + 'aria-expanded': ariaExpanded, labelMaxFontSizeMultiplier, background, style, @@ -239,8 +254,11 @@ const Extended = ({ size={size} visible={visible} onPress={onPress} - accessibilityLabel={accessibilityLabel} - accessibilityState={accessibilityState} + aria-label={ariaLabel} + aria-checked={ariaChecked} + aria-selected={ariaSelected} + aria-busy={ariaBusy} + aria-expanded={ariaExpanded} background={background} widthShared={widthValue} labelMaxFontSizeMultiplier={labelMaxFontSizeMultiplier} @@ -253,7 +271,7 @@ const Extended = ({ ref={offscreenLabelRef} style={styles.offscreenMeasure} importantForAccessibility="no-hide-descendants" - accessibilityElementsHidden + aria-hidden > void; - accessibilityLabel?: string; + /** + * Accessibility label for the trigger FAB. + */ + 'aria-label'?: string; testID?: string; }; @@ -206,7 +209,7 @@ const AnimatedItem = ({ expanded ? styles.pointerEventsAuto : styles.pointerEventsNone, ]} importantForAccessibility={expanded ? 'yes' : 'no-hide-descendants'} - accessibilityElementsHidden={!expanded} + aria-hidden={!expanded} > {children} @@ -219,7 +222,10 @@ type ItemProps = { variant: Variant; theme: InternalTheme; onPress: (e: GestureResponderEvent) => void; - accessibilityLabel?: string; + /** + * Accessibility label. Falls back to `label`. + */ + 'aria-label'?: string; testID?: string; }; @@ -235,7 +241,7 @@ const MenuItem = ({ variant, theme, onPress, - accessibilityLabel, + 'aria-label': ariaLabel, testID, }: ItemProps) => { const colors = resolveColors({ theme, variant }); @@ -261,8 +267,8 @@ const MenuItem = ({ onPress={onPress} onFocus={onFocus} onBlur={onBlur} - accessibilityRole="button" - accessibilityLabel={accessibilityLabel ?? label} + role="button" + aria-label={ariaLabel ?? label} style={[ { borderRadius }, Platform.OS === 'web' ? webNoOutline : null, @@ -309,7 +315,10 @@ type MorphingTriggerProps = { visible: boolean; alignment: 'start' | 'center' | 'end'; onPress?: (e: GestureResponderEvent) => void; - accessibilityLabel?: string; + /** + * Accessibility label for the trigger button. + */ + 'aria-label'?: string; theme: InternalTheme; testID?: string; }; @@ -326,7 +335,7 @@ const MorphingTrigger = ({ visible, alignment, onPress, - accessibilityLabel, + 'aria-label': ariaLabel, theme, testID, }: MorphingTriggerProps) => { @@ -446,7 +455,7 @@ const MorphingTrigger = ({ contentColor={triggerContentColor} visible={visible} onPress={onPress} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} widthShared={widthShared} heightShared={heightShared} borderRadiusShared={borderRadiusShared} @@ -616,7 +625,7 @@ const Menu = ({ label={item.label} variant={itemsVariant} theme={theme} - accessibilityLabel={item.accessibilityLabel ?? item.label} + aria-label={item['aria-label'] ?? item.label} onPress={handleItemPress(item)} testID={item.testID} /> @@ -636,7 +645,7 @@ const Menu = ({ visible={triggerVisible} alignment={alignment} onPress={effectiveExpanded ? onDismiss : openOnPress} - accessibilityLabel={trigger.accessibilityLabel} + aria-label={trigger['aria-label']} theme={theme} testID={trigger.testID} /> diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index 4f9b9b5da9..2d2d60e4cd 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Platform, StyleSheet, View } from 'react-native'; import type { - AccessibilityState, ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, @@ -92,11 +91,24 @@ export type ShellProps = { /** * Accessibility label. Falls back to `label` if unset. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** - * Accessibility state forwarded to the underlying button. + * Indicates whether the element is checked. Accepts `true`, `false`, + * or `'mixed'` for an indeterminate state. */ - accessibilityState?: AccessibilityState; + 'aria-checked'?: boolean | 'mixed'; + /** + * Indicates whether the element is selected. + */ + 'aria-selected'?: boolean; + /** + * Indicates whether the element is currently busy (e.g. loading). + */ + 'aria-busy'?: boolean; + /** + * Indicates whether the element's controlled content is expanded. + */ + 'aria-expanded'?: boolean; /** * Largest scale the label font can reach (auto-built content only). */ @@ -182,8 +194,11 @@ const Shell = ({ elevation = Tokens.stateElevation.enabled, visible = true, onPress, - accessibilityLabel = label, - accessibilityState, + 'aria-label': ariaLabel = label, + 'aria-checked': ariaChecked, + 'aria-selected': ariaSelected, + 'aria-busy': ariaBusy, + 'aria-expanded': ariaExpanded, labelMaxFontSizeMultiplier, labelAnimatedStyle, background, @@ -290,9 +305,12 @@ const Shell = ({ onPress={onPress} onFocus={onFocus} onBlur={onBlur} - accessibilityLabel={accessibilityLabel} - accessibilityRole="button" - accessibilityState={accessibilityState} + aria-label={ariaLabel} + role="button" + aria-checked={ariaChecked} + aria-selected={ariaSelected} + aria-busy={ariaBusy} + aria-expanded={ariaExpanded} testID={testID} style={[ children ? styles.fill : null, diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 899d1e5a46..52a64e98d0 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -59,7 +59,7 @@ export type Props = Omit<$RemoveChildren, 'style'> & { /** * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Style of button's inner content. * Use this prop to apply custom height and width or to set a custom padding`. @@ -112,7 +112,7 @@ const IconButton = ({ iconColor: customIconColor, containerColor: customContainerColor, size = 24, - accessibilityLabel, + 'aria-label': ariaLabel, disabled, onPress, selected = false, @@ -188,13 +188,10 @@ const IconButton = ({ borderless centered onPress={onPress} - accessibilityLabel={accessibilityLabel} + aria-label={ariaLabel} style={[styles.touchable, contentStyle]} - // @ts-expect-error We keep old a11y props for backwards compat with old RN versions - accessibilityTraits={disabled ? ['button', 'disabled'] : 'button'} - accessibilityComponentType="button" - accessibilityRole="button" - accessibilityState={{ disabled }} + role="button" + aria-disabled={disabled} disabled={disabled} hitSlop={ TouchableRipple.supported diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 5dbca2ee60..97730d58e6 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -120,7 +120,7 @@ export type Props = { /** * Accessibility label for the TouchableRipple. This is read by the screen reader when the user taps the touchable. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * `pointerEvents` passed to the `View` container */ @@ -190,7 +190,7 @@ const ListAccordion = ({ onLongPress, delayLongPress, expanded: expandedProp, - accessibilityLabel, + 'aria-label': ariaLabel, pointerEvents = 'none', titleMaxFontSizeMultiplier, descriptionMaxFontSizeMultiplier, @@ -249,9 +249,9 @@ const ListAccordion = ({ onPress={handlePress} onLongPress={onLongPress} delayLongPress={delayLongPress} - accessibilityRole="button" - accessibilityState={{ expanded: isExpanded }} - accessibilityLabel={accessibilityLabel} + role="button" + aria-expanded={isExpanded} + aria-label={ariaLabel} testID={testID} theme={theme} background={background} diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index a23fe82942..ca6e849e3d 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -640,8 +640,8 @@ const Menu = ({ {rendered ? ( diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index a8412da806..3c66c885b3 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -182,15 +182,15 @@ function Modal({ return ( diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index a907b46b09..5fcaf9f6e5 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -134,9 +134,10 @@ const RadioButtonAndroid = ({ }); } } - accessibilityRole="radio" - accessibilityState={{ disabled, checked }} - accessibilityLiveRegion="polite" + role="radio" + aria-disabled={disabled} + aria-checked={checked} + aria-live="polite" style={styles.container} testID={testID} theme={theme} diff --git a/src/components/RadioButton/RadioButtonGroup.tsx b/src/components/RadioButton/RadioButtonGroup.tsx index 2bdd596561..8522bf3fc4 100644 --- a/src/components/RadioButton/RadioButtonGroup.tsx +++ b/src/components/RadioButton/RadioButtonGroup.tsx @@ -56,7 +56,7 @@ export const RadioButtonContext = React.createContext( */ const RadioButtonGroup = ({ value, onValueChange, children }: Props) => ( - {children} + {children} ); diff --git a/src/components/RadioButton/RadioButtonIOS.tsx b/src/components/RadioButton/RadioButtonIOS.tsx index e2e5f06c7c..015356eea3 100644 --- a/src/components/RadioButton/RadioButtonIOS.tsx +++ b/src/components/RadioButton/RadioButtonIOS.tsx @@ -92,9 +92,10 @@ const RadioButtonIOS = ({ }); } } - accessibilityRole="radio" - accessibilityState={{ disabled, checked }} - accessibilityLiveRegion="polite" + role="radio" + aria-disabled={disabled} + aria-checked={checked} + aria-live="polite" style={styles.container} testID={testID} theme={theme} diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index 1591760980..e37ed435e2 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -49,7 +49,7 @@ export type Props = { /** * Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Custom color for unchecked radio. */ @@ -149,7 +149,7 @@ const RadioButtonItem = ({ status, theme: themeOverrides, background, - accessibilityLabel = label, + 'aria-label': ariaLabel = label, testID, mode, position = 'trailing', @@ -204,12 +204,10 @@ const RadioButtonItem = ({ }) } onLongPress={onLongPress} - accessibilityLabel={accessibilityLabel} - accessibilityRole="radio" - accessibilityState={{ - checked, - disabled, - }} + aria-label={ariaLabel} + role="radio" + aria-checked={checked} + aria-disabled={disabled} testID={testID} disabled={disabled} background={background} diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index a081b62fc4..3cc733e126 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -245,7 +245,7 @@ const Searchbar = ({ theme={theme} > )} {shouldRenderTraileringIcon ? ( ) : null} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 0d30d8aee4..4f701f0b48 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -52,7 +52,7 @@ export type Props = { /** * Accessibility label for the `SegmentedButtonItem`. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -102,7 +102,7 @@ export type Props = { const SegmentedButtonItem = ({ checked, - accessibilityLabel, + 'aria-label': ariaLabel, disabled, style, labelStyle, @@ -195,9 +195,10 @@ const SegmentedButtonItem = ({ = { * - `value`: value of button (required) * - `icon`: icon to display for the item * - `disabled`: whether the button is disabled - * - `accessibilityLabel`: acccessibility label for the button. This is read by the screen reader when the user taps the button. + * - `aria-label`: accessibility label for the button. This is read by the screen reader when the user taps the button. * - `checkedColor`: custom color for checked Text and Icon * - `uncheckedColor`: custom color for unchecked Text and Icon * - `onPress`: callback that is called when button is pressed @@ -62,7 +62,7 @@ export type Props = { value: T; icon?: IconSource; disabled?: boolean; - accessibilityLabel?: string; + 'aria-label'?: string; checkedColor?: string; uncheckedColor?: string; onPress?: (event: GestureResponderEvent) => void; diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index 2d6ee23211..04502eb7de 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -284,7 +284,7 @@ const Snackbar = ({ > diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index fd145bfdee..6f12dafdd8 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -57,7 +57,10 @@ export type Props = { style?: StyleProp; testID?: string; theme?: ThemeProp; - accessibilityLabel?: string; + /** + * Accessibility label for the switch. This is read by the screen reader when the user focuses the switch. + */ + 'aria-label'?: string; }; const { @@ -132,7 +135,7 @@ const Switch = ({ style, testID, theme: themeOverrides, - accessibilityLabel, + 'aria-label': ariaLabel, }: Props) => { const theme = useInternalTheme(themeOverrides); const reduceMotion = useReduceMotion(); @@ -367,9 +370,10 @@ const Switch = ({ focusedSV.value = 0; }} android_ripple={{ color: 'transparent' }} - accessibilityRole="switch" - accessibilityState={{ disabled: isDisabled, checked }} - accessibilityLabel={accessibilityLabel} + role="switch" + aria-disabled={isDisabled} + aria-checked={checked} + aria-label={ariaLabel} testID={testID} style={[ styles.touchable, diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx index 806dd4585a..5a7a65d974 100644 --- a/src/components/TextInput/TextInput.tsx +++ b/src/components/TextInput/TextInput.tsx @@ -57,7 +57,7 @@ export type TextInputColors = { }; export type GetAccessibilityDataReturn = { - input: AccessibilityProps; + input: AccessibilityProps & { 'aria-invalid'?: boolean }; supportingText: AccessibilityProps; counter: AccessibilityProps; }; diff --git a/src/components/TextInput/utils.ts b/src/components/TextInput/utils.ts index e8da1a4de3..ad81f55301 100644 --- a/src/components/TextInput/utils.ts +++ b/src/components/TextInput/utils.ts @@ -611,19 +611,14 @@ export const getAccessibilityData = ({ : `Characters entered ${inputLength} of ${maxLength}` : undefined; - const accessibilityState = { - disabled: isDisabled, - invalid: isInvalid, - ...props.accessibilityState, - } as const; - return { input: { 'aria-label': ariaLabel, 'aria-valuemax': isCounterReached ? maxLength : undefined, 'aria-valuenow': isCounterReached ? inputLength : undefined, + 'aria-disabled': isDisabled, + 'aria-invalid': isInvalid, accessibilityHint: hint, - accessibilityState, }, supportingText: { 'aria-hidden': isSupportingTextHidden, diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index 8d4cdd556f..22598c16b2 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -29,7 +29,7 @@ export type Props = { /** * Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button. */ - accessibilityLabel?: string; + 'aria-label'?: string; /** * Function to execute on press. */ @@ -88,7 +88,7 @@ const ToggleButton = ({ icon, size, theme: themeOverrides, - accessibilityLabel, + 'aria-label': ariaLabel, disabled, style, value, @@ -123,8 +123,9 @@ const ToggleButton = ({ } }} size={size} - accessibilityLabel={accessibilityLabel} - accessibilityState={{ disabled, selected: checked }} + aria-label={ariaLabel} + aria-disabled={disabled} + aria-selected={checked} disabled={disabled} style={[ styles.content, diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 36dc08971b..8d237d6991 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -215,7 +215,7 @@ const Tooltip = ({ testID="tooltip-container" > { ); }); - it('Is recognized as a header when no onPress callback has been pressed', async () => { + it('Is recognized as a heading when no onPress callback has been passed', async () => { await render( @@ -162,7 +162,7 @@ describe('renderAppbarContent', () => { ); - expect(screen.getByRole('header')).toBeOnTheScreen(); + expect(screen.getByRole('heading')).toBeOnTheScreen(); }); it('is recognized as a button when onPress callback has been passed', async () => { await render( @@ -174,7 +174,7 @@ describe('renderAppbarContent', () => { ); expect(screen.getByRole('button')).toBeEnabled(); - expect(screen.queryByRole('header')).not.toBeOnTheScreen(); + expect(screen.queryByRole('heading')).not.toBeOnTheScreen(); }); it('is recognized as a disabled button when onPress and disabled is passed', async () => { await render( @@ -186,7 +186,7 @@ describe('renderAppbarContent', () => { ); expect(screen.getByRole('button')).toBeDisabled(); - expect(screen.queryByRole('header')).not.toBeOnTheScreen(); + expect(screen.queryByRole('heading')).not.toBeOnTheScreen(); }); }); diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 7dbcf9f0a5..a5d9d95766 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -122,9 +122,7 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` testID="search-bar-icon-container" > { ); diff --git a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap index 1b21c11b68..54f2e4f7a4 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap @@ -3,7 +3,6 @@ exports[`renders Checkbox with custom testID 1`] = ` { expect(tree).toMatchSnapshot(); }); +it('renders FAB with aria-label', async () => { + const tree = ( + await render() + ).toJSON(); + expect(tree).toMatchSnapshot(); +}); + it('renders FAB medium size', async () => { const tree = (await render()).toJSON(); expect(tree).toMatchSnapshot(); @@ -64,13 +71,6 @@ it('renders FAB with containerColor and contentColor overrides', async () => { expect(tree).toMatchSnapshot(); }); -it('renders FAB with accessibilityLabel', async () => { - const tree = ( - await render() - ).toJSON(); - expect(tree).toMatchSnapshot(); -}); - it('renders FAB transitioning to not visible', async () => { const { rerender, toJSON } = await render(); await rerender(); @@ -86,15 +86,18 @@ it('renders FAB transitioning to visible', async () => { }); it('calls onPress when FAB is pressed', async () => { + const user = userEvent.setup(); const onPress = jest.fn(); - await render(); - await userEvent.press(screen.getByTestId('fab')); + await render(); + await user.press(screen.getByRole('button', { name: 'Add item' })); expect(onPress).toHaveBeenCalledTimes(1); }); it('forwards event object to onPress', async () => { const onPress = jest.fn(); - await render(); - await fireEvent(screen.getByTestId('fab'), 'onPress', { key: 'value' }); + await render(); + await fireEvent(screen.getByRole('button', { name: 'Add item' }), 'onPress', { + key: 'value', + }); expect(onPress).toHaveBeenCalledWith({ key: 'value' }); }); diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx index 2d24e0a038..fd9a21cec9 100644 --- a/src/components/__tests__/FABExtended.test.tsx +++ b/src/components/__tests__/FABExtended.test.tsx @@ -57,7 +57,7 @@ it('renders extended FAB transitioning to collapsed', async () => { expect(toJSON()).toMatchSnapshot(); }); -it('uses label as default accessibilityLabel', async () => { +it('uses label as default aria-label', async () => { await render( { expect(screen.getByLabelText('New message')).toBeOnTheScreen(); }); -it('respects explicit accessibilityLabel', async () => { +it('respects explicit aria-label', async () => { await render( ); @@ -85,6 +85,7 @@ it('respects explicit accessibilityLabel', async () => { }); it('calls onPress when pressed', async () => { + const user = userEvent.setup(); const onPress = jest.fn(); await render( { testID="extended-fab" /> ); - await userEvent.press(screen.getByTestId('extended-fab')); + await user.press(screen.getByRole('button', { name: 'New message' })); expect(onPress).toHaveBeenCalledTimes(1); }); @@ -110,8 +111,12 @@ it('forwards event object to onPress', async () => { testID="extended-fab" /> ); - await fireEvent(screen.getByTestId('extended-fab'), 'onPress', { - key: 'value', - }); + await fireEvent( + screen.getByRole('button', { name: 'New message' }), + 'onPress', + { + key: 'value', + } + ); expect(onPress).toHaveBeenCalledWith({ key: 'value' }); }); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index e4f683fa52..ba66e705a7 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -53,21 +53,13 @@ describe('Menu Item', () => { ).toBe(labelMaxFontSizeMultiplier); }); - it('accepts different values for accessibilityState', async () => { - await render( - - ); + it('accepts aria-checked prop', async () => { + await render(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('touchable').props.accessibilityState - ).toMatchObject({ - checked: true, - }); + expect(screen.getByRole('menuitem')).toHaveProp( + 'accessibilityState', + expect.objectContaining({ checked: true }) + ); }); }); diff --git a/src/components/__tests__/ProgressBar.test.tsx b/src/components/__tests__/ProgressBar.test.tsx index db2a2bf600..4939f25c4c 100644 --- a/src/components/__tests__/ProgressBar.test.tsx +++ b/src/components/__tests__/ProgressBar.test.tsx @@ -86,6 +86,12 @@ it('renders progress bar with full height on web', async () => { }); }); +it('has progressbar role', async () => { + await render(); + + expect(screen.getByRole(a11yRole)).toBeOnTheScreen(); +}); + it('renders progress bar with custom style of filled part', async () => { await render( diff --git a/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap b/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap index 5560a80f36..c20910f20e 100644 --- a/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap +++ b/src/components/__tests__/RadioButton/__snapshots__/RadioButton.test.tsx.snap @@ -3,7 +3,6 @@ exports[`RadioButton RadioButton with custom testID renders properly 1`] = ` { }); }); +describe('Switch accessibility', () => { + it('has switch role', async () => { + await render(); + + expect(screen.getByRole('switch')).toBeOnTheScreen(); + }); +}); + describe('Switch interaction', () => { it('toggles to true when off and pressed', async () => { + const user = userEvent.setup(); const onValueChange = jest.fn(); await render(); - await userEvent.press(screen.getByRole('switch')); + await user.press(screen.getByRole('switch')); expect(onValueChange).toHaveBeenCalledWith(true); }); it('toggles to false when on and pressed', async () => { + const user = userEvent.setup(); const onValueChange = jest.fn(); await render(); - await userEvent.press(screen.getByRole('switch')); + await user.press(screen.getByRole('switch')); expect(onValueChange).toHaveBeenCalledWith(false); }); it('does not fire onValueChange when disabled', async () => { + const user = userEvent.setup(); const onValueChange = jest.fn(); await render( ); - await userEvent.press(screen.getByRole('switch')); + await user.press(screen.getByRole('switch')); expect(onValueChange).not.toHaveBeenCalled(); }); }); diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index 6a0fe19768..14b1c72bdb 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -1,5 +1,10 @@ import * as React from 'react'; -import { I18nManager, TextInput as NativeTextInput, View } from 'react-native'; +import { + I18nManager, + StyleSheet, + TextInput as NativeTextInput, + View, +} from 'react-native'; import type { GestureResponderEvent } from 'react-native'; import { @@ -23,6 +28,13 @@ import type { TextInputAccessoryProps } from '../TextInput/TextInputIcon'; const stateOpacity = tokens.md.sys.state.opacity; +const styles = StyleSheet.create({ + textInputStyle: { + fontSize: 40, + letterSpacing: 9, + }, +}); + const defaultI18nIsRTL = I18nManager.isRTL; const includeHiddenElements = { includeHiddenElements: true }; @@ -181,6 +193,7 @@ it('renders outlined TextInput with TextInput.Icon accessories when error is tru }); it('fires onPress on TextInput.Icon end accessory', async () => { + const user = userEvent.setup(); const onClear = jest.fn<(event: GestureResponderEvent) => void>(); await render( { {...props} icon="close" onPress={onClear} - accessibilityLabel="Clear" + aria-label="Clear" /> )} /> ); - await userEvent.press(screen.getAllByTestId('icon-button')[1]); + await user.press(screen.getByRole('button', { name: 'Clear' })); expect(onClear).toHaveBeenCalledTimes(1); }); @@ -222,11 +235,9 @@ it('disables TextInput.Icon when the field is disabled', async () => { /> ); - const buttons = screen.getAllByTestId('icon-button'); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[0].props.accessibilityState?.disabled).toBe(true); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[1].props.accessibilityState?.disabled).toBe(true); + const buttons = screen.getAllByRole('button'); + expect(buttons[0]).toBeDisabled(); + expect(buttons[1]).toBeDisabled(); }); it('does not disable TextInput.Icon when the field is read-only (editable false)', async () => { @@ -245,11 +256,9 @@ it('does not disable TextInput.Icon when the field is read-only (editable false) /> ); - const buttons = screen.getAllByTestId('icon-button'); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[0].props.accessibilityState?.disabled).not.toBe(true); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(buttons[1].props.accessibilityState?.disabled).not.toBe(true); + const buttons = screen.getAllByRole('button'); + expect(buttons[0]).toBeEnabled(); + expect(buttons[1]).toBeEnabled(); }); it('renders supporting text below the field', async () => { @@ -279,12 +288,8 @@ it('uses polite aria-live on error supporting text', async () => { /> ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByText('Invalid').props['aria-live']).toBe('polite'); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props.accessibilityState?.invalid).toBe( - true - ); + expect(screen.getByText('Invalid')).toHaveProp('aria-live', 'polite'); + expect(screen.getByLabelText('Email')).toHaveProp('aria-invalid', true); }); it('marks the input invalid when error is true without supporting text', async () => { @@ -298,14 +303,9 @@ it('marks the input invalid when error is true without supporting text', async ( /> ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props.accessibilityState?.invalid).toBe( - true - ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('tf-input').props.accessibilityHint - ).toBeUndefined(); + const input = screen.getByLabelText('Email'); + expect(input).toHaveProp('aria-invalid', true); + expect(input).not.toHaveProp('accessibilityHint'); }); it('hides helper supporting text from the accessibility tree and omits aria-live', async () => { @@ -319,18 +319,10 @@ it('hides helper supporting text from the accessibility tree and omits aria-live /> ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByText('Optional', includeHiddenElements).props['aria-hidden'] - ).toBe(true); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByText('Optional', includeHiddenElements).props['aria-live'] - ).toBeUndefined(); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props['aria-label']).toBe( - 'Email, Optional' - ); + const supportingText = screen.getByText('Optional', includeHiddenElements); + expect(supportingText).toHaveProp('aria-hidden', true); + expect(supportingText).not.toHaveProp('aria-live'); + expect(screen.getByLabelText('Email, Optional')).toBeOnTheScreen(); }); it('includes supporting text in aria-label when label is omitted', async () => { @@ -343,10 +335,7 @@ it('includes supporting text in aria-label when label is omitted', async () => { /> ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('tf-input').props['aria-label']).toBe( - 'Helper only' - ); + expect(screen.getByLabelText('Helper only')).toBeOnTheScreen(); }); it('does not mark the input as aria-disabled when editable is false (read-only)', async () => { @@ -360,13 +349,10 @@ it('does not mark the input as aria-disabled when editable is false (read-only)' /> ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('tf-input').props.accessibilityState?.disabled - ).not.toBe(true); + expect(screen.getByLabelText('Email')).toHaveProp('aria-disabled', false); }); -it('marks the input as disabled in accessibilityState when disabled is true', async () => { +it('marks the input as aria-disabled when disabled is true', async () => { await render( ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('tf-input').props.accessibilityState?.disabled - ).toBe(true); + expect(screen.getByLabelText('Email')).toBeDisabled(); }); it('renders the input via render with merged props', async () => { @@ -624,6 +607,7 @@ it('invokes onFocus and onBlur on the TextInput', async () => { }); it('focuses the TextInput when the outer Pressable is pressed', async () => { + const user = userEvent.setup(); const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); const { root } = await render( @@ -637,26 +621,28 @@ it('focuses the TextInput when the outer Pressable is pressed', async () => { expect(screen.getByTestId('tf-input')).toBeOnTheScreen(); - await userEvent.press(getOuterTextInputPressable(root)); + await user.press(getOuterTextInputPressable(root)); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); }); it('does not focus the TextInput when disabled and the Pressable is pressed', async () => { + const user = userEvent.setup(); const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); const { root } = await render( {}} disabled /> ); - await userEvent.press(getOuterTextInputPressable(root)); + await user.press(getOuterTextInputPressable(root)); expect(focusSpy).not.toHaveBeenCalled(); focusSpy.mockRestore(); }); it('focuses the TextInput when read-only and the Pressable is pressed', async () => { + const user = userEvent.setup(); const focusSpy = jest.spyOn(NativeTextInput.prototype, 'focus'); const { root } = await render( @@ -668,7 +654,7 @@ it('focuses the TextInput when read-only and the Pressable is pressed', async () /> ); - await userEvent.press(getOuterTextInputPressable(root)); + await user.press(getOuterTextInputPressable(root)); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); @@ -1182,7 +1168,7 @@ it('does not apply the TextInput style prop to prefix or suffix Text', async () onChangeText={() => {}} prefix="$" suffix="]" - style={{ fontSize: 40, letterSpacing: 9 }} + style={styles.textInputStyle} testID="tf-input-style" /> ); @@ -1217,6 +1203,7 @@ it('passes defaultValue to the native input when uncontrolled without counter', it('updates the character counter for an uncontrolled field with counter enabled', async () => { const onChangeText = jest.fn(); + const user = userEvent.setup(); await render( +; ``` + ::: We also want the menu to appear only on `HomeScreen`, which means we will render it conditionally based on the `back` prop. @@ -240,12 +249,8 @@ export default function CustomNavigationBar({ - }> + anchor={} + > { console.log('Option 1 was pressed'); @@ -276,6 +281,6 @@ Final result: -That's all we need! We have app bar that contains everything we need to navigate through screens and access an additional menu on the main screen. As you can see, with Material design `Appbar` provided by `react-native-paper` used together with `react-navigation` we can easily create an app that looks and works great. +That's all we need! We have app bar that contains everything we need to navigate through screens and access an additional menu on the main screen. As you can see, with Material design `Appbar` provided by `react-native-paper` used together with `react-navigation` we can easily create an app that looks and works great. Grab the Expo [snack](https://snack.expo.dev/@react-native-paper/integrate-appbar-with-react-navigation) if you want to check the whole code! diff --git a/docs/versioned_docs/version-5.x/guides/06-recommended-libraries.md b/docs/5.x/docs/guides/recommended-libraries.md similarity index 96% rename from docs/versioned_docs/version-5.x/guides/06-recommended-libraries.md rename to docs/5.x/docs/guides/recommended-libraries.md index e70157ca1d..076dfb6871 100644 --- a/docs/versioned_docs/version-5.x/guides/06-recommended-libraries.md +++ b/docs/5.x/docs/guides/recommended-libraries.md @@ -24,17 +24,19 @@ An implementation of the [bottom sheet behaviour](https://material.io/design/com [gorhom/react-native-bottom-sheet](https://github.com/gorhom/react-native-bottom-sheet) An implementation of the [bottom sheet behaviour](https://material.io/design/components/sheets-bottom.html), maintained by [@Gorhom](https://twitter.com/Gorhom). - ## Date Picker + [web-ridge/react-native-paper-dates](https://github.com/web-ridge/react-native-paper-dates) Material Design themed [date picker](https://material.io/components/date-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) - + [react-native-community/react-native-datetimepicker](https://github.com/react-native-community/react-native-datetimepicker) ## Time Picker + [web-ridge/react-native-paper-dates](https://github.com/web-ridge/react-native-paper-dates) -Material Design themed [time picker](https://material.io/components/time-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) +Material Design themed [time picker](https://material.io/components/time-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) ## System Colors + [pchmn/expo-material3-theme](https://github.com/pchmn/expo-material3-theme) -Retrieve Material 3 system colors from Android 12+ devices \ No newline at end of file +Retrieve Material 3 system colors from Android 12+ devices diff --git a/docs/versioned_docs/version-6.x/guides/11-ripple-effect.md b/docs/5.x/docs/guides/ripple-effect.md similarity index 99% rename from docs/versioned_docs/version-6.x/guides/11-ripple-effect.md rename to docs/5.x/docs/guides/ripple-effect.md index 31845060d7..e6f6e503ef 100644 --- a/docs/versioned_docs/version-6.x/guides/11-ripple-effect.md +++ b/docs/5.x/docs/guides/ripple-effect.md @@ -40,6 +40,3 @@ import { Provider as PaperProvider } from 'react-native-paper'; // ... ``` - - - diff --git a/docs/versioned_docs/version-5.x/guides/08-theming-with-react-navigation.md b/docs/5.x/docs/guides/theming-with-react-navigation.md similarity index 95% rename from docs/versioned_docs/version-5.x/guides/08-theming-with-react-navigation.md rename to docs/5.x/docs/guides/theming-with-react-navigation.md index 197d2bfa31..d240f5b43d 100644 --- a/docs/versioned_docs/version-5.x/guides/08-theming-with-react-navigation.md +++ b/docs/5.x/docs/guides/theming-with-react-navigation.md @@ -21,15 +21,12 @@ import { DefaultTheme as NavigationDefaultTheme, } from '@react-navigation/native'; -import { - MD2LightTheme, - MD2DarkTheme, -} from 'react-native-paper'; +import { MD2LightTheme, MD2DarkTheme } from 'react-native-paper'; ``` ### Material Design 3 -From v5, React Native Paper theme colors structure follows the Material Design 3 (known as Material You) colors system, which differs significantly from both the previous Paper's theme and React Navigation theme. +From v5, React Native Paper theme colors structure follows the Material Design 3 (known as Material You) colors system, which differs significantly from both the previous Paper's theme and React Navigation theme. However, to simplify adapting React Navigation theme colors, to use the ones from React Native Paper, it's worth using a utility called `adaptNavigationTheme` – it accepts navigation-compliant themes in both modes and returns their equivalents adjusted to Material Design 3. @@ -49,10 +46,7 @@ const { LightTheme, DarkTheme } = adaptNavigationTheme({ Library exports also Material Design 3 themes in both modes: ```js -import { - MD3LightTheme, - MD3DarkTheme, -} from 'react-native-paper'; +import { MD3LightTheme, MD3DarkTheme } from 'react-native-paper'; ``` ## Combining theme objects @@ -117,8 +111,6 @@ export default function App() { } ``` - - Our goal here is to combine those two themes, so that we could control the theme for the entire application from a single place. To make things easier we can use [deepmerge](https://www.npmjs.com/package/deepmerge) package. We can install it with: @@ -135,10 +127,7 @@ import { DarkTheme as NavigationDarkTheme, DefaultTheme as NavigationDefaultTheme, } from '@react-navigation/native'; -import { - MD2DarkTheme, - MD2LightTheme, -} from 'react-native-paper'; +import { MD2DarkTheme, MD2LightTheme } from 'react-native-paper'; import merge from 'deepmerge'; const CombinedDefaultTheme = merge(MD2LightTheme, NavigationDefaultTheme); @@ -331,11 +320,7 @@ const Header = ({ scene }) => { }} > - + ); }; @@ -343,7 +328,7 @@ const Header = ({ scene }) => { And now you can switch between light and dark theme! -![paperGuide1](../../../static/screenshots/themingWithReactNavigationDarkLightSwitch.gif) +![paperGuide1](/react-native-paper/screenshots/themingWithReactNavigationDarkLightSwitch.gif) Thanks to the linking of themes that we did earlier, switching themes can be controlled with only one piece of state. diff --git a/docs/versioned_docs/version-5.x/guides/02-theming.mdx b/docs/5.x/docs/guides/theming.mdx similarity index 98% rename from docs/versioned_docs/version-5.x/guides/02-theming.mdx rename to docs/5.x/docs/guides/theming.mdx index 53bb236889..89bd6e75af 100644 --- a/docs/versioned_docs/version-5.x/guides/02-theming.mdx +++ b/docs/5.x/docs/guides/theming.mdx @@ -2,7 +2,7 @@ title: Theming --- -import DynamicColorTheme from '@site/src/components/DynamicColorTheme.tsx'; +import DynamicColorTheme from '@docs/components/DynamicColorTheme.tsx'; # Theming @@ -73,14 +73,12 @@ A theme usually contains the following properties: - `colors` (`object`): various colors used throughout different elements. > The primary key color is used to derive roles for key components across the UI, such as the FAB, prominent buttons, active states, as well as the tint of elevated surfaces. - - `primary` - `onPrimary` - `primaryContainer` - `onPrimaryContainer` > The secondary key color is used for less prominent components in the UI such as filter chips, while expanding the opportunity for color expression. - - `secondary` - `onSecondary` - `secondaryContainer` @@ -89,34 +87,29 @@ A theme usually contains the following properties: > The tertiary key color is used to derive the roles of contrasting accents that can be used to balance primary and secondary colors or bring heightened attention to an element. > The tertiary color role is left for teams to use at their discretion and is intended to support broader color expression in products. - - `tertiary` - `onTertiary` - `tertiaryContainer` - `onTertiaryContainer` > The neutral key color is used to derive the roles of surface and background, as well as high emphasis text and icons. - - `background` - `onBackground` - `surface` - `onSurface` > The neutral variant key color is used to derive medium emphasis text and icons, surface variants, and component outlines. - - `surfaceVariant` - `onSurfaceVariant` - `outline` > In addition to the accent and neutral key color, the color system includes a semantic color role for error - - `error` - `onError` - `errorContainer` - `onErrorContainer` > Surfaces at elevation levels 0-5 are tinted via color overlays based on the primary color, such as app bars or menus. The addition of a grade from 0-5 introduces tonal variation to the surface baseline. - - `elevation` (`object`) - `level0` - transparent - `level1` - 5% opacity @@ -126,12 +119,10 @@ A theme usually contains the following properties: - `level5` - 14% opacity > Colors for disabled state - - `surfaceDisabled` - `onSurfaceDisabled` > These additional role mappings exist in a scheme and are mapped to components where needed. - - `shadow` - `inverseOnSurface` - `inverseSurface` @@ -197,7 +188,7 @@ Created schemes are following the Material Design 3 color system and covering co Passed source color into the util is translated into tones to automatically provide the range of tones that map to color roles. -![customColors](../../../static/screenshots/custom-colors.png) +![customColors](/react-native-paper/screenshots/custom-colors.png) _Source: [Material You Color System](https://m3.material.io/styles/color/the-color-system/custom-colors)_ @@ -344,11 +335,10 @@ By default, TypeScript works well whenever you change the value of the built-in There are two supported ways of overriding the theme: -1. Simple built-in theme overrides - when you only customize the values and - the whole theme schema remains the same -2. Advanced theme overrides - when you add new properties or - change the built-in schema shape - +1. Simple built-in theme overrides - when you only customize the values + and the whole theme schema remains the same +2. Advanced theme overrides - when you add new properties or + change the built-in schema shape :::caution TypeScript support for `withTheme` is currently limited to Material You (MD3) theme only. diff --git a/docs/versioned_docs/version-5.x/showcase.mdx b/docs/5.x/docs/showcase.mdx similarity index 78% rename from docs/versioned_docs/version-5.x/showcase.mdx rename to docs/5.x/docs/showcase.mdx index 7024ab018d..19bce467e3 100644 --- a/docs/versioned_docs/version-5.x/showcase.mdx +++ b/docs/5.x/docs/showcase.mdx @@ -2,7 +2,7 @@ title: Showcase --- -import Showcase from '@site/src/components/Showcase.tsx'; +import Showcase from '@docs/components/Showcase.tsx'; # Who's using Paper? diff --git a/docs/5.x/index.mdx b/docs/5.x/index.mdx new file mode 100644 index 0000000000..91f12670f9 --- /dev/null +++ b/docs/5.x/index.mdx @@ -0,0 +1,38 @@ +--- +pageType: home +hero: + name: Cross-platform Material Design for React Native + tagline: Paper is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. + actions: + - text: Get started + link: /docs/guides/getting-started + theme: brand + - text: Try on Snack + link: https://snack.expo.dev/@react-native-paper/react-native-paper-example_v5 + theme: alt +features: + - title: Production-ready components + details: Buttons, inputs, lists, cards, menus, dialogs, navigation, and feedback patterns built for day-to-day product work. + icon: /react-native-paper/images/home/components.svg + span: 4 + - title: Material You theming + details: Adopt Material 3 color, typography, elevation, and state layers with a theme system designed for real apps. + icon: /react-native-paper/images/home/palette.svg + span: 4 + - title: Accessible by default + details: Common interactions, contrast, and state handling are already baked into the component surface instead of left to each screen. + icon: /react-native-paper/images/home/accessibility.svg + span: 4 + - title: Cross-platform parity + details: Keep design language and component behavior aligned across iOS, Android, and React Native Web from one codebase. + icon: /react-native-paper/images/home/devices.svg + span: 4 + - title: Dark mode support + details: Ship light and dark experiences with the same primitives, including adaptive surfaces and semantic color roles. + icon: /react-native-paper/images/home/moon.svg + span: 4 + - title: Migration-friendly docs + details: Browse stable 5.x, preview 6.x, and legacy static docs from one version selector without splitting the docs experience. + icon: /react-native-paper/images/home/versions.svg + span: 4 +--- diff --git a/docs/6.x/_nav.json b/docs/6.x/_nav.json new file mode 100644 index 0000000000..f7de8846cb --- /dev/null +++ b/docs/6.x/_nav.json @@ -0,0 +1,17 @@ +[ + { + "text": "Guides", + "link": "/docs/guides/getting-started", + "activeMatch": "^/(?:6\\.x/)?docs/guides/" + }, + { + "text": "Components", + "link": "/docs/components/ActivityIndicator", + "activeMatch": "^/(?:6\\.x/)?docs/components/" + }, + { + "text": "Showcase", + "link": "/docs/showcase", + "activeMatch": "^/(?:6\\.x/)?docs/showcase$" + } +] diff --git a/docs/6.x/docs/_meta.json b/docs/6.x/docs/_meta.json new file mode 100644 index 0000000000..9e4ac38659 --- /dev/null +++ b/docs/6.x/docs/_meta.json @@ -0,0 +1,17 @@ +[ + { + "type": "dir-section-header", + "name": "guides", + "label": "Guides" + }, + { + "type": "dir-section-header", + "name": "components", + "label": "Components" + }, + { + "type": "file", + "name": "showcase", + "label": "Showcase" + } +] diff --git a/docs/versioned_docs/version-6.x/components/ActivityIndicator.mdx b/docs/6.x/docs/components/ActivityIndicator.mdx similarity index 70% rename from docs/versioned_docs/version-6.x/components/ActivityIndicator.mdx rename to docs/6.x/docs/components/ActivityIndicator.mdx index 51fcd3899d..eac03089ea 100644 --- a/docs/versioned_docs/version-6.x/components/ActivityIndicator.mdx +++ b/docs/6.x/docs/components/ActivityIndicator.mdx @@ -2,11 +2,11 @@ title: ActivityIndicator --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Activity indicator is used to present progress of some activity in the app. It can be used as a drop-in replacement for the ActivityIndicator shipped with React Native. @@ -14,8 +14,8 @@ It can be used as a drop-in replacement for the ActivityIndicator shipped with R - - + + ## Usage ```js @@ -32,58 +32,58 @@ export default MyComponent; ## Props - - + +
-### animating +### animating
- + - +
-### color +### color
- + - +
-### size +### size
- + - +
-### hidesWhenStopped +### hidesWhenStopped
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + @@ -91,6 +91,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/Appbar.mdx b/docs/6.x/docs/components/Appbar/Appbar.mdx similarity index 85% rename from docs/versioned_docs/version-6.x/components/Appbar/Appbar.mdx rename to docs/6.x/docs/components/Appbar/Appbar.mdx index b9f52f72ae..798b6dd136 100644 --- a/docs/versioned_docs/version-6.x/components/Appbar/Appbar.mdx +++ b/docs/6.x/docs/components/Appbar/Appbar.mdx @@ -2,11 +2,11 @@ title: Appbar --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to display action items in a bar. It can be placed at the top or bottom. The top bar usually contains the screen title, controls such as navigation buttons, menu button etc. @@ -18,8 +18,8 @@ See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/them - - + + ## Usage ### Top bar @@ -102,66 +102,66 @@ export default MyComponent; ## Props - - + +
-### dark +### dark
- + - +
-### children (required) +### children (required)
- + - +
### mode Available in v5.x with theme version 3
- + - +
### elevated Available in v5.x with theme version 3
- + - +
-### safeAreaInsets +### safeAreaInsets
- + - +
-### theme +### theme
- + - +
-### style +### style
- + - - + + @@ -169,6 +169,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarAction.mdx b/docs/6.x/docs/components/Appbar/AppbarAction.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/Appbar/AppbarAction.mdx rename to docs/6.x/docs/components/Appbar/AppbarAction.mdx index d4b3fe6ca1..cd55f9cdb7 100644 --- a/docs/versioned_docs/version-6.x/components/Appbar/AppbarAction.mdx +++ b/docs/6.x/docs/components/Appbar/AppbarAction.mdx @@ -2,19 +2,19 @@ title: Appbar.Action --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component used to display an action item in the appbar. - - + + ## Usage ```js @@ -38,90 +38,90 @@ export default MyComponent; ## Props - - + +
-### color +### color
- + - +
-### icon (required) +### icon (required)
- + - +
-### size +### size
- + - +
-### disabled +### disabled
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### onPress +### onPress
- + - +
### isLeading Available in v5.x with theme version 3
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - +
-### theme +### theme
- + - - + + @@ -129,6 +129,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarBackAction.mdx b/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx similarity index 67% rename from docs/versioned_docs/version-6.x/components/Appbar/AppbarBackAction.mdx rename to docs/6.x/docs/components/Appbar/AppbarBackAction.mdx index 1bddbc1a47..592afcdf6f 100644 --- a/docs/versioned_docs/version-6.x/components/Appbar/AppbarBackAction.mdx +++ b/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx @@ -2,19 +2,19 @@ title: Appbar.BackAction --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component used to display a back button in the appbar. - - + + ## Usage ```js @@ -33,66 +33,66 @@ export default MyComponent; ## Props - - + +
-### color +### color
- + - +
-### size +### size
- + - +
-### disabled +### disabled
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### onPress +### onPress
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarContent.mdx b/docs/6.x/docs/components/Appbar/AppbarContent.mdx similarity index 70% rename from docs/versioned_docs/version-6.x/components/Appbar/AppbarContent.mdx rename to docs/6.x/docs/components/Appbar/AppbarContent.mdx index f0cd732f9e..a3c4953c66 100644 --- a/docs/versioned_docs/version-6.x/components/Appbar/AppbarContent.mdx +++ b/docs/6.x/docs/components/Appbar/AppbarContent.mdx @@ -2,19 +2,19 @@ title: Appbar.Content --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component used to display a title in an appbar. - - + + ## Usage ```js @@ -33,90 +33,90 @@ export default MyComponent; ## Props - - + +
-### title (required) +### title (required)
- + - +
-### titleStyle +### titleStyle
- + - +
-### titleRef +### titleRef
- + - +
-### onPress +### onPress
- + - +
-### disabled +### disabled
- + - +
-### color +### color
- + - +
-### titleMaxFontSizeMultiplier +### titleMaxFontSizeMultiplier
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + @@ -124,6 +124,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Appbar/AppbarHeader.mdx b/docs/6.x/docs/components/Appbar/AppbarHeader.mdx similarity index 80% rename from docs/versioned_docs/version-6.x/components/Appbar/AppbarHeader.mdx rename to docs/6.x/docs/components/Appbar/AppbarHeader.mdx index 131dbdec53..f541042a00 100644 --- a/docs/versioned_docs/version-6.x/components/Appbar/AppbarHeader.mdx +++ b/docs/6.x/docs/components/Appbar/AppbarHeader.mdx @@ -2,11 +2,11 @@ title: Appbar.Header --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to use as a header at the top of the screen. It can contain the screen title, controls such as navigation buttons, menu button etc. @@ -14,8 +14,8 @@ It can contain the screen title, controls such as navigation buttons, menu butto - - + + ## Usage ```js @@ -45,74 +45,74 @@ export default MyComponent; ## Props - - + +
-### dark +### dark
- + - +
-### statusBarHeight +### statusBarHeight
- + - +
-### children (required) +### children (required)
- + - +
### mode Available in v5.x with theme version 3
- + - +
### elevated Available in v5.x with theme version 3
- + - +
-### theme +### theme
- + - +
-### style +### style
- + - +
-### testID +### testID
- + - - + + @@ -120,6 +120,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/Appbar/_meta.json b/docs/6.x/docs/components/Appbar/_meta.json new file mode 100644 index 0000000000..d4243fea02 --- /dev/null +++ b/docs/6.x/docs/components/Appbar/_meta.json @@ -0,0 +1,7 @@ +[ + "Appbar", + "AppbarAction", + "AppbarBackAction", + "AppbarContent", + "AppbarHeader" +] diff --git a/docs/versioned_docs/version-6.x/components/Avatar/AvatarIcon.mdx b/docs/6.x/docs/components/Avatar/AvatarIcon.mdx similarity index 62% rename from docs/versioned_docs/version-6.x/components/Avatar/AvatarIcon.mdx rename to docs/6.x/docs/components/Avatar/AvatarIcon.mdx index 8d2cb9f401..045a00c260 100644 --- a/docs/versioned_docs/version-6.x/components/Avatar/AvatarIcon.mdx +++ b/docs/6.x/docs/components/Avatar/AvatarIcon.mdx @@ -2,19 +2,19 @@ title: Avatar.Icon --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Avatars can be used to represent people in a graphical way. - - + + ## Usage ```js @@ -29,50 +29,50 @@ const MyComponent = () => ( ## Props - - + +
-### icon (required) +### icon (required)
- + - +
-### size +### size
- + - +
-### color +### color
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Avatar/AvatarImage.mdx b/docs/6.x/docs/components/Avatar/AvatarImage.mdx similarity index 68% rename from docs/versioned_docs/version-6.x/components/Avatar/AvatarImage.mdx rename to docs/6.x/docs/components/Avatar/AvatarImage.mdx index 79c7c9bb0a..ecd4bcc751 100644 --- a/docs/versioned_docs/version-6.x/components/Avatar/AvatarImage.mdx +++ b/docs/6.x/docs/components/Avatar/AvatarImage.mdx @@ -2,19 +2,19 @@ title: Avatar.Image --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Avatars can be used to represent people in a graphical way. - - + + ## Usage ```js @@ -30,90 +30,90 @@ export default MyComponent ## Props - - + +
-### source (required) +### source (required)
- + - +
-### size +### size
- + - +
-### style +### style
- + - +
-### onError +### onError
- + - +
-### onLayout +### onLayout
- + - +
-### onLoad +### onLoad
- + - +
-### onLoadEnd +### onLoadEnd
- + - +
-### onLoadStart +### onLoadStart
- + - +
-### onProgress +### onProgress
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Avatar/AvatarText.mdx b/docs/6.x/docs/components/Avatar/AvatarText.mdx similarity index 64% rename from docs/versioned_docs/version-6.x/components/Avatar/AvatarText.mdx rename to docs/6.x/docs/components/Avatar/AvatarText.mdx index ec1379adf4..600c026e9f 100644 --- a/docs/versioned_docs/version-6.x/components/Avatar/AvatarText.mdx +++ b/docs/6.x/docs/components/Avatar/AvatarText.mdx @@ -2,19 +2,19 @@ title: Avatar.Text --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Avatars can be used to represent people in a graphical way. - - + + ## Usage ```js @@ -29,66 +29,66 @@ const MyComponent = () => ( ## Props - - + +
-### label (required) +### label (required)
- + - +
-### size +### size
- + - +
-### color +### color
- + - +
-### style +### style
- + - +
-### labelStyle +### labelStyle
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/6.x/docs/components/Avatar/_meta.json b/docs/6.x/docs/components/Avatar/_meta.json new file mode 100644 index 0000000000..b3967b8318 --- /dev/null +++ b/docs/6.x/docs/components/Avatar/_meta.json @@ -0,0 +1,5 @@ +[ + "AvatarIcon", + "AvatarImage", + "AvatarText" +] diff --git a/docs/versioned_docs/version-6.x/components/Badge.mdx b/docs/6.x/docs/components/Badge.mdx similarity index 69% rename from docs/versioned_docs/version-6.x/components/Badge.mdx rename to docs/6.x/docs/components/Badge.mdx index 77326a9bf0..dba6c041ca 100644 --- a/docs/versioned_docs/version-6.x/components/Badge.mdx +++ b/docs/6.x/docs/components/Badge.mdx @@ -2,11 +2,11 @@ title: Badge --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Badges are small status descriptors for UI elements. A badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object. @@ -14,8 +14,8 @@ A badge consists of a small circle, typically containing a number or other short - - + + ## Usage ```js @@ -32,58 +32,58 @@ export default MyComponent; ## Props - - + +
-### visible +### visible
- + - +
-### children +### children
- + - +
-### size +### size
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - +
-### theme +### theme
- + - - + + @@ -91,6 +91,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Banner.mdx b/docs/6.x/docs/components/Banner.mdx similarity index 75% rename from docs/versioned_docs/version-6.x/components/Banner.mdx rename to docs/6.x/docs/components/Banner.mdx index 14c1107d0b..344d9103e9 100644 --- a/docs/versioned_docs/version-6.x/components/Banner.mdx +++ b/docs/6.x/docs/components/Banner.mdx @@ -2,19 +2,19 @@ title: Banner --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Banner displays a prominent message and related actions. - - + + ## Usage ```js @@ -60,106 +60,106 @@ export default MyComponent; ## Props - - + +
-### visible (required) +### visible (required)
- + - +
-### children (required) +### children (required)
- + - +
-### icon +### icon
- + - +
-### actions +### actions
- + - +
-### contentStyle +### contentStyle
- + - +
### elevation Available in v5.x with theme version 3
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - +
-### theme +### theme
- + - +
-### onShowAnimationFinished +### onShowAnimationFinished
- + - +
-### onHideAnimationFinished +### onHideAnimationFinished
- + - - + + @@ -167,6 +167,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigation.mdx b/docs/6.x/docs/components/BottomNavigation/BottomNavigation.mdx similarity index 80% rename from docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigation.mdx rename to docs/6.x/docs/components/BottomNavigation/BottomNavigation.mdx index fb90edf463..c4fe55af84 100644 --- a/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigation.mdx +++ b/docs/6.x/docs/components/BottomNavigation/BottomNavigation.mdx @@ -2,11 +2,11 @@ title: BottomNavigation --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar. It is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar). @@ -17,8 +17,8 @@ See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/them - - + + ## Usage ```js @@ -64,242 +64,242 @@ export default MyComponent; ## Props - - + +
-### shifting +### shifting
- + - +
-### labeled +### labeled
- + - +
-### compact +### compact
- + - +
-### navigationState (required) +### navigationState (required)
- + - +
-### onIndexChange (required) +### onIndexChange (required)
- + - +
-### renderScene (required) +### renderScene (required)
- + - +
-### renderIcon +### renderIcon
- + - +
-### renderLabel +### renderLabel
- + - +
-### renderTouchable +### renderTouchable
- + - +
-### getAccessibilityLabel +### getAccessibilityLabel
- + - +
-### getBadge +### getBadge
- + - +
-### getLabelText +### getLabelText
- + - +
-### getLazy +### getLazy
- + - +
-### getTestID +### getTestID
- + - +
-### onTabPress +### onTabPress
- + - +
-### onTabLongPress +### onTabLongPress
- + - +
-### activeColor +### activeColor
- + - +
-### inactiveColor +### inactiveColor
- + - +
-### sceneAnimationEnabled +### sceneAnimationEnabled
- + - +
-### sceneAnimationType +### sceneAnimationType
- + - +
-### sceneAnimationEasing +### sceneAnimationEasing
- + - +
-### keyboardHidesNavigationBar +### keyboardHidesNavigationBar
- + - +
-### safeAreaInsets +### safeAreaInsets
- + - +
-### barStyle +### barStyle
- + - +
-### labelMaxFontSizeMultiplier +### labelMaxFontSizeMultiplier
- + - +
-### style +### style
- + - +
-### activeIndicatorStyle +### activeIndicatorStyle
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigationBar.mdx b/docs/6.x/docs/components/BottomNavigation/BottomNavigationBar.mdx similarity index 91% rename from docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigationBar.mdx rename to docs/6.x/docs/components/BottomNavigation/BottomNavigationBar.mdx index f5d064f3b1..a93d41ee74 100644 --- a/docs/versioned_docs/version-6.x/components/BottomNavigation/BottomNavigationBar.mdx +++ b/docs/6.x/docs/components/BottomNavigation/BottomNavigationBar.mdx @@ -2,19 +2,19 @@ title: BottomNavigation.Bar --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/). - - + + ## Usage @@ -84,199 +84,199 @@ export default function MyComponent() { ### with React Navigation \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst MyTabs = createBottomTabNavigator({\n screenOptions: {\n animation: 'shift',\n },\n tabBar: ({ navigation, state, descriptors, insets }) => (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n ),\n screens: {\n Home: {\n screen: HomeScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n Settings: {\n screen: SettingsScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n },\n});\n\nconst Navigation = createStaticNavigation(MyTabs);\n\nexport default function App() {\n return (\n \n \n \n \n \n );\n}","dynamic":"import { Text, View } from 'react-native';\nimport { NavigationContainer, CommonActions } from '@react-navigation/native';\nimport { createBottomTabNavigator } from '@react-navigation/bottom-tabs';\nimport { Provider, BottomNavigation } from 'react-native-paper';\nimport MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst Tab = createBottomTabNavigator();\n\nexport default function App() {\n return (\n \n \n (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n )}>\n (\n \n ),\n }}\n />\n (\n \n ),\n }}\n />\n \n \n \n );\n}\n"}}} /> - + ## Props - - + +
-### shifting +### shifting
- + - +
-### labeled +### labeled
- + - +
-### compact +### compact
- + - +
-### navigationState (required) +### navigationState (required)
- + - +
-### renderIcon +### renderIcon
- + - +
-### renderLabel +### renderLabel
- + - +
-### renderTouchable +### renderTouchable
- + - +
-### getAccessibilityLabel +### getAccessibilityLabel
- + - +
-### getBadge +### getBadge
- + - +
-### getLabelText +### getLabelText
- + - +
-### getTestID +### getTestID
- + - +
-### onTabPress (required) +### onTabPress (required)
- + - +
-### onTabLongPress +### onTabLongPress
- + - +
-### activeColor +### activeColor
- + - +
-### inactiveColor +### inactiveColor
- + - +
-### animationEasing +### animationEasing
- + - +
-### keyboardHidesNavigationBar +### keyboardHidesNavigationBar
- + - +
-### safeAreaInsets +### safeAreaInsets
- + - +
-### labelMaxFontSizeMultiplier +### labelMaxFontSizeMultiplier
- + - +
-### style +### style
- + - +
-### activeIndicatorStyle +### activeIndicatorStyle
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/6.x/docs/components/BottomNavigation/_meta.json b/docs/6.x/docs/components/BottomNavigation/_meta.json new file mode 100644 index 0000000000..94a0b010f3 --- /dev/null +++ b/docs/6.x/docs/components/BottomNavigation/_meta.json @@ -0,0 +1,4 @@ +[ + "BottomNavigation", + "BottomNavigationBar" +] diff --git a/docs/versioned_docs/version-6.x/components/Button/Button.mdx b/docs/6.x/docs/components/Button/Button.mdx similarity index 78% rename from docs/versioned_docs/version-6.x/components/Button/Button.mdx rename to docs/6.x/docs/components/Button/Button.mdx index f85bbe1d76..8daa42b8c3 100644 --- a/docs/versioned_docs/version-6.x/components/Button/Button.mdx +++ b/docs/6.x/docs/components/Button/Button.mdx @@ -2,19 +2,19 @@ title: Button --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A button is component that the user can press to trigger an action. - - + + ## Usage ```js @@ -33,234 +33,234 @@ export default MyComponent; ## Props - - + +
-### mode +### mode
- + - +
-### dark +### dark
- + - +
-### compact +### compact
- + - +
-### buttonColor +### buttonColor
- + - +
-### textColor +### textColor
- + - +
-### loading +### loading
- + - +
-### icon +### icon
- + - +
-### disabled +### disabled
- + - +
-### children (required) +### children (required)
- + - +
-### uppercase +### uppercase
- + - +
-### background +### background
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### accessibilityHint +### accessibilityHint
- + - +
-### accessibilityRole +### accessibilityRole
- + - +
-### onPress +### onPress
- + - +
-### onPressIn +### onPressIn
- + - +
-### onPressOut +### onPressOut
- + - +
-### onLongPress +### onLongPress
- + - +
-### delayLongPress +### delayLongPress
- + - +
-### contentStyle +### contentStyle
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - +
-### hitSlop +### hitSlop
- + - +
-### style +### style
- + - +
-### labelStyle +### labelStyle
- + - +
-### theme +### theme
- + - +
-### touchableRef +### touchableRef
- + - +
-### ref +### ref
- + - +
-### testID +### testID
- + - - + + @@ -268,6 +268,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/Button/_meta.json b/docs/6.x/docs/components/Button/_meta.json new file mode 100644 index 0000000000..a6d17d5990 --- /dev/null +++ b/docs/6.x/docs/components/Button/_meta.json @@ -0,0 +1,3 @@ +[ + "Button" +] diff --git a/docs/versioned_docs/version-6.x/components/Card/Card.mdx b/docs/6.x/docs/components/Card/Card.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/Card/Card.mdx rename to docs/6.x/docs/components/Card/Card.mdx index 98fbe67108..0248a53318 100644 --- a/docs/versioned_docs/version-6.x/components/Card/Card.mdx +++ b/docs/6.x/docs/components/Card/Card.mdx @@ -2,19 +2,19 @@ title: Card --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A card is a sheet of material that serves as an entry point to more detailed information. - - + + ## Usage ```js @@ -44,122 +44,122 @@ export default MyComponent; ## Props - - + +
-### mode +### mode
- + - +
-### children (required) +### children (required)
- + - +
-### onLongPress +### onLongPress
- + - +
-### onPress +### onPress
- + - +
-### onPressIn +### onPressIn
- + - +
-### onPressOut +### onPressOut
- + - +
-### delayLongPress +### delayLongPress
- + - +
-### disabled +### disabled
- + - +
-### elevation +### elevation
- + - +
-### contentStyle +### contentStyle
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - +
-### accessible +### accessible
- + - - + + @@ -167,6 +167,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardActions.mdx b/docs/6.x/docs/components/Card/CardActions.mdx similarity index 62% rename from docs/versioned_docs/version-6.x/components/Card/CardActions.mdx rename to docs/6.x/docs/components/Card/CardActions.mdx index 856f640420..fb2af05f91 100644 --- a/docs/versioned_docs/version-6.x/components/Card/CardActions.mdx +++ b/docs/6.x/docs/components/Card/CardActions.mdx @@ -2,19 +2,19 @@ title: Card.Actions --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a list of actions inside a Card. - - + + ## Usage ```js @@ -36,34 +36,34 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardContent.mdx b/docs/6.x/docs/components/Card/CardContent.mdx similarity index 61% rename from docs/versioned_docs/version-6.x/components/Card/CardContent.mdx rename to docs/6.x/docs/components/Card/CardContent.mdx index bb7496c1dd..54bf3c61be 100644 --- a/docs/versioned_docs/version-6.x/components/Card/CardContent.mdx +++ b/docs/6.x/docs/components/Card/CardContent.mdx @@ -2,19 +2,19 @@ title: Card.Content --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show content inside a Card. - - + + ## Usage ```js @@ -36,26 +36,26 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardCover.mdx b/docs/6.x/docs/components/Card/CardCover.mdx similarity index 57% rename from docs/versioned_docs/version-6.x/components/Card/CardCover.mdx rename to docs/6.x/docs/components/Card/CardCover.mdx index 0bfac77665..9808e9bfd8 100644 --- a/docs/versioned_docs/version-6.x/components/Card/CardCover.mdx +++ b/docs/6.x/docs/components/Card/CardCover.mdx @@ -2,19 +2,19 @@ title: Card.Cover --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a cover image inside a Card. - - + + ## Usage ```js @@ -37,24 +37,24 @@ export default MyComponent; ### Image props - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Card/CardTitle.mdx b/docs/6.x/docs/components/Card/CardTitle.mdx similarity index 75% rename from docs/versioned_docs/version-6.x/components/Card/CardTitle.mdx rename to docs/6.x/docs/components/Card/CardTitle.mdx index 912d3f51a8..90d114e478 100644 --- a/docs/versioned_docs/version-6.x/components/Card/CardTitle.mdx +++ b/docs/6.x/docs/components/Card/CardTitle.mdx @@ -2,19 +2,19 @@ title: Card.Title --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a title, subtitle and an avatar inside a Card. - - + + ## Usage ```js @@ -36,138 +36,138 @@ export default MyComponent; ## Props - - + +
-### title (required) +### title (required)
- + - +
-### titleStyle +### titleStyle
- + - +
-### titleNumberOfLines +### titleNumberOfLines
- + - +
### titleVariant Available in v5.x with theme version 3
- + - +
-### subtitle +### subtitle
- + - +
-### subtitleStyle +### subtitleStyle
- + - +
-### subtitleNumberOfLines +### subtitleNumberOfLines
- + - +
### subtitleVariant Available in v5.x with theme version 3
- + - +
-### left +### left
- + - +
-### leftStyle +### leftStyle
- + - +
-### right +### right
- + - +
-### rightStyle +### rightStyle
- + - +
-### titleMaxFontSizeMultiplier +### titleMaxFontSizeMultiplier
- + - +
-### subtitleMaxFontSizeMultiplier +### subtitleMaxFontSizeMultiplier
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/6.x/docs/components/Card/_meta.json b/docs/6.x/docs/components/Card/_meta.json new file mode 100644 index 0000000000..3b7edb1615 --- /dev/null +++ b/docs/6.x/docs/components/Card/_meta.json @@ -0,0 +1,7 @@ +[ + "Card", + "CardActions", + "CardContent", + "CardCover", + "CardTitle" +] diff --git a/docs/versioned_docs/version-6.x/components/Checkbox/Checkbox.mdx b/docs/6.x/docs/components/Checkbox/Checkbox.mdx similarity index 72% rename from docs/versioned_docs/version-6.x/components/Checkbox/Checkbox.mdx rename to docs/6.x/docs/components/Checkbox/Checkbox.mdx index d8c4419527..b12b7e56b9 100644 --- a/docs/versioned_docs/version-6.x/components/Checkbox/Checkbox.mdx +++ b/docs/6.x/docs/components/Checkbox/Checkbox.mdx @@ -2,19 +2,19 @@ title: Checkbox --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Checkboxes allow the selection of multiple options from a set. - - + + ## Usage ```js @@ -40,82 +40,82 @@ export default MyComponent; ## Props - - + +
-### status (required) +### status (required)
- + - +
-### disabled +### disabled
- + - +
-### onPress +### onPress
- + - +
-### uncheckedColor +### uncheckedColor
- + - +
-### color +### color
- + - +
-### error +### error
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - +
-### style +### style
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Checkbox/CheckboxItem.mdx b/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx similarity index 73% rename from docs/versioned_docs/version-6.x/components/Checkbox/CheckboxItem.mdx rename to docs/6.x/docs/components/Checkbox/CheckboxItem.mdx index faa96df173..016438cb2f 100644 --- a/docs/versioned_docs/version-6.x/components/Checkbox/CheckboxItem.mdx +++ b/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx @@ -2,11 +2,11 @@ title: Checkbox.Item --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox. @@ -32,146 +32,146 @@ export default MyComponent; ## Props - - + +
-### status (required) +### status (required)
- + - +
-### disabled +### disabled
- + - +
-### label (required) +### label (required)
- + - +
-### onPress +### onPress
- + - +
-### onLongPress +### onLongPress
- + - +
-### background +### background
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### uncheckedColor +### uncheckedColor
- + - +
-### color +### color
- + - +
-### style +### style
- + - +
-### labelMaxFontSizeMultiplier +### labelMaxFontSizeMultiplier
- + - +
-### labelStyle +### labelStyle
- + - +
### labelVariant Available in v5.x with theme version 3
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - +
-### position +### position
- + - +
-### hitSlop +### hitSlop
- + - - + + diff --git a/docs/6.x/docs/components/Checkbox/_meta.json b/docs/6.x/docs/components/Checkbox/_meta.json new file mode 100644 index 0000000000..69a54c00fd --- /dev/null +++ b/docs/6.x/docs/components/Checkbox/_meta.json @@ -0,0 +1,4 @@ +[ + "Checkbox", + "CheckboxItem" +] diff --git a/docs/versioned_docs/version-6.x/components/Chip/Chip.mdx b/docs/6.x/docs/components/Chip/Chip.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/Chip/Chip.mdx rename to docs/6.x/docs/components/Chip/Chip.mdx index 87b98dc6fc..27fa00b7d3 100644 --- a/docs/versioned_docs/version-6.x/components/Chip/Chip.mdx +++ b/docs/6.x/docs/components/Chip/Chip.mdx @@ -2,11 +2,11 @@ title: Chip --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Chips are compact elements that can represent inputs, attributes, or actions. They can have an icon or avatar on the left, and a close button icon on the right. @@ -21,8 +21,8 @@ They are typically used to: - - + + ## Usage ```js @@ -39,242 +39,242 @@ export default MyComponent; ## Props - - + +
-### mode +### mode
- + - +
-### children (required) +### children (required)
- + - +
-### icon +### icon
- + - +
-### avatar +### avatar
- + - +
-### closeIcon +### closeIcon
- + - +
-### selected +### selected
- + - +
-### selectedColor +### selectedColor
- + - +
### showSelectedOverlay Available in v5.x with theme version 3
- + - +
-### showSelectedCheck +### showSelectedCheck
- + - +
-### disabled +### disabled
- + - +
-### background +### background
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### closeIconAccessibilityLabel +### closeIconAccessibilityLabel
- + - +
-### onPress +### onPress
- + - +
-### onLongPress +### onLongPress
- + - +
-### onPressIn +### onPressIn
- + - +
-### onPressOut +### onPressOut
- + - +
-### onClose +### onClose
- + - +
-### delayLongPress +### delayLongPress
- + - +
### compact Available in v5.x with theme version 3
- + - +
### elevated Available in v5.x with theme version 3
- + - +
-### textStyle +### textStyle
- + - +
-### style +### style
- + - +
-### hitSlop +### hitSlop
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - +
-### ellipsizeMode +### ellipsizeMode
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - +
-### accessibilityRole +### accessibilityRole
- + - - + + diff --git a/docs/6.x/docs/components/Chip/_meta.json b/docs/6.x/docs/components/Chip/_meta.json new file mode 100644 index 0000000000..02eb8c63e5 --- /dev/null +++ b/docs/6.x/docs/components/Chip/_meta.json @@ -0,0 +1,3 @@ +[ + "Chip" +] diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTable.mdx b/docs/6.x/docs/components/DataTable/DataTable.mdx similarity index 84% rename from docs/versioned_docs/version-6.x/components/DataTable/DataTable.mdx rename to docs/6.x/docs/components/DataTable/DataTable.mdx index 510a195f7e..2a9e4ddae7 100644 --- a/docs/versioned_docs/version-6.x/components/DataTable/DataTable.mdx +++ b/docs/6.x/docs/components/DataTable/DataTable.mdx @@ -2,19 +2,19 @@ title: DataTable --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Data tables allow displaying sets of data. - - + + ## Usage ```js @@ -99,26 +99,26 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableCell.mdx b/docs/6.x/docs/components/DataTable/DataTableCell.mdx similarity index 74% rename from docs/versioned_docs/version-6.x/components/DataTable/DataTableCell.mdx rename to docs/6.x/docs/components/DataTable/DataTableCell.mdx index b21f71127e..89d4809627 100644 --- a/docs/versioned_docs/version-6.x/components/DataTable/DataTableCell.mdx +++ b/docs/6.x/docs/components/DataTable/DataTableCell.mdx @@ -2,19 +2,19 @@ title: DataTable.Cell --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a single cell inside of a table. - - + + ## Usage ```js @@ -43,64 +43,64 @@ MD Guidelines (https://github.com/callstack/react-native-paper/issues/2381). ### TouchableRipple props - +
-### children (required) +### children (required)
- + - +
-### numeric +### numeric
- + - +
-### onPress +### onPress
- + - +
-### style +### style
- + - +
-### textStyle +### textStyle
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableHeader.mdx b/docs/6.x/docs/components/DataTable/DataTableHeader.mdx similarity index 68% rename from docs/versioned_docs/version-6.x/components/DataTable/DataTableHeader.mdx rename to docs/6.x/docs/components/DataTable/DataTableHeader.mdx index bf318e56db..8b1ee0dd8f 100644 --- a/docs/versioned_docs/version-6.x/components/DataTable/DataTableHeader.mdx +++ b/docs/6.x/docs/components/DataTable/DataTableHeader.mdx @@ -2,19 +2,19 @@ title: DataTable.Header --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to display title in table header. - - + + ## Usage ```js @@ -41,34 +41,34 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTablePagination.mdx b/docs/6.x/docs/components/DataTable/DataTablePagination.mdx similarity index 77% rename from docs/versioned_docs/version-6.x/components/DataTable/DataTablePagination.mdx rename to docs/6.x/docs/components/DataTable/DataTablePagination.mdx index eb6afe233e..38f1dd0c36 100644 --- a/docs/versioned_docs/version-6.x/components/DataTable/DataTablePagination.mdx +++ b/docs/6.x/docs/components/DataTable/DataTablePagination.mdx @@ -2,19 +2,19 @@ title: DataTable.Pagination --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show pagination for data table. - - + + ## Usage ```js @@ -71,114 +71,114 @@ export default MyComponent; ## Props - - + +
-### page (required) +### page (required)
- + - +
-### numberOfPages (required) +### numberOfPages (required)
- + - +
-### onPageChange (required) +### onPageChange (required)
- + - +
-### showFastPaginationControls +### showFastPaginationControls
- + - +
-### theme +### theme
- + - +
-### numberOfItemsPerPage +### numberOfItemsPerPage
- + - +
-### numberOfItemsPerPageList +### numberOfItemsPerPageList
- + - +
-### onItemsPerPageChange +### onItemsPerPageChange
- + - +
-### selectPageDropdownLabel +### selectPageDropdownLabel
- + - +
-### selectPageDropdownAccessibilityLabel +### selectPageDropdownAccessibilityLabel
- + - +
-### label +### label
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### style +### style
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableRow.mdx b/docs/6.x/docs/components/DataTable/DataTableRow.mdx similarity index 70% rename from docs/versioned_docs/version-6.x/components/DataTable/DataTableRow.mdx rename to docs/6.x/docs/components/DataTable/DataTableRow.mdx index e2765dcca8..59e6a4f5be 100644 --- a/docs/versioned_docs/version-6.x/components/DataTable/DataTableRow.mdx +++ b/docs/6.x/docs/components/DataTable/DataTableRow.mdx @@ -2,19 +2,19 @@ title: DataTable.Row --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a single row inside of a table. - - + + ## Usage ```js @@ -40,48 +40,48 @@ export default MyComponent; ### TouchableRipple props - +
-### children (required) +### children (required)
- + - +
-### onPress +### onPress
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### pointerEvents +### pointerEvents
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/DataTable/DataTableTitle.mdx b/docs/6.x/docs/components/DataTable/DataTableTitle.mdx similarity index 71% rename from docs/versioned_docs/version-6.x/components/DataTable/DataTableTitle.mdx rename to docs/6.x/docs/components/DataTable/DataTableTitle.mdx index 8967b338c8..f8087199f7 100644 --- a/docs/versioned_docs/version-6.x/components/DataTable/DataTableTitle.mdx +++ b/docs/6.x/docs/components/DataTable/DataTableTitle.mdx @@ -2,19 +2,19 @@ title: DataTable.Title --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to display title in table header. - - + + ## Usage ```js @@ -41,82 +41,82 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### numeric +### numeric
- + - +
-### sortDirection +### sortDirection
- + - +
-### numberOfLines +### numberOfLines
- + - +
-### onPress +### onPress
- + - +
-### style +### style
- + - +
-### textStyle +### textStyle
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/6.x/docs/components/DataTable/_meta.json b/docs/6.x/docs/components/DataTable/_meta.json new file mode 100644 index 0000000000..aaed448e4d --- /dev/null +++ b/docs/6.x/docs/components/DataTable/_meta.json @@ -0,0 +1,8 @@ +[ + "DataTable", + "DataTableCell", + "DataTableHeader", + "DataTablePagination", + "DataTableRow", + "DataTableTitle" +] diff --git a/docs/versioned_docs/version-6.x/components/Dialog/Dialog.mdx b/docs/6.x/docs/components/Dialog/Dialog.mdx similarity index 74% rename from docs/versioned_docs/version-6.x/components/Dialog/Dialog.mdx rename to docs/6.x/docs/components/Dialog/Dialog.mdx index 29450a344b..e871f7f750 100644 --- a/docs/versioned_docs/version-6.x/components/Dialog/Dialog.mdx +++ b/docs/6.x/docs/components/Dialog/Dialog.mdx @@ -2,20 +2,20 @@ title: Dialog --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. -To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component. +To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal/Portal) component. - - + + ## Usage ```js @@ -56,74 +56,74 @@ export default MyComponent; ## Props - - + +
-### dismissable +### dismissable
- + - +
-### dismissableBackButton +### dismissableBackButton
- + - +
-### onDismiss +### onDismiss
- + - +
-### visible +### visible
- + - +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + @@ -131,6 +131,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogActions.mdx b/docs/6.x/docs/components/Dialog/DialogActions.mdx similarity index 70% rename from docs/versioned_docs/version-6.x/components/Dialog/DialogActions.mdx rename to docs/6.x/docs/components/Dialog/DialogActions.mdx index b8936894a6..b1d6bda29f 100644 --- a/docs/versioned_docs/version-6.x/components/Dialog/DialogActions.mdx +++ b/docs/6.x/docs/components/Dialog/DialogActions.mdx @@ -2,19 +2,19 @@ title: Dialog.Actions --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a list of actions in a Dialog. - - + + ## Usage ```js @@ -44,34 +44,34 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogContent.mdx b/docs/6.x/docs/components/Dialog/DialogContent.mdx similarity index 67% rename from docs/versioned_docs/version-6.x/components/Dialog/DialogContent.mdx rename to docs/6.x/docs/components/Dialog/DialogContent.mdx index 18dc952022..38771647f2 100644 --- a/docs/versioned_docs/version-6.x/components/Dialog/DialogContent.mdx +++ b/docs/6.x/docs/components/Dialog/DialogContent.mdx @@ -2,19 +2,19 @@ title: Dialog.Content --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show content in a Dialog. - - + + ## Usage ```js @@ -43,26 +43,26 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogIcon.mdx b/docs/6.x/docs/components/Dialog/DialogIcon.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/Dialog/DialogIcon.mdx rename to docs/6.x/docs/components/Dialog/DialogIcon.mdx index 30e0d48f25..34777210ca 100644 --- a/docs/versioned_docs/version-6.x/components/Dialog/DialogIcon.mdx +++ b/docs/6.x/docs/components/Dialog/DialogIcon.mdx @@ -2,11 +2,11 @@ title: Dialog.Icon --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; @supported Available in v5.x with theme version 3 A component to show an icon in a Dialog. @@ -14,8 +14,8 @@ A component to show an icon in a Dialog. - - + + ## Usage ```js @@ -53,42 +53,42 @@ export default MyComponent; ## Props - - + +
-### color +### color
- + - +
-### icon (required) +### icon (required)
- + - +
-### size +### size
- + - +
-### theme +### theme
- + - - + + @@ -96,6 +96,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogScrollArea.mdx b/docs/6.x/docs/components/Dialog/DialogScrollArea.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/Dialog/DialogScrollArea.mdx rename to docs/6.x/docs/components/Dialog/DialogScrollArea.mdx index cc49955a83..f5fc1904d2 100644 --- a/docs/versioned_docs/version-6.x/components/Dialog/DialogScrollArea.mdx +++ b/docs/6.x/docs/components/Dialog/DialogScrollArea.mdx @@ -2,11 +2,11 @@ title: Dialog.ScrollArea --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a scrollable content in a Dialog. The component only provides appropriate styling. For the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement. @@ -14,8 +14,8 @@ For the scrollable content you can use `ScrollView`, `FlatList` etc. depending o - - + + ## Usage ```js @@ -47,34 +47,34 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + @@ -82,6 +82,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Dialog/DialogTitle.mdx b/docs/6.x/docs/components/Dialog/DialogTitle.mdx similarity index 69% rename from docs/versioned_docs/version-6.x/components/Dialog/DialogTitle.mdx rename to docs/6.x/docs/components/Dialog/DialogTitle.mdx index 4755404144..d548cffb85 100644 --- a/docs/versioned_docs/version-6.x/components/Dialog/DialogTitle.mdx +++ b/docs/6.x/docs/components/Dialog/DialogTitle.mdx @@ -2,19 +2,19 @@ title: Dialog.Title --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a title in a Dialog. - - + + ## Usage ```js @@ -44,34 +44,34 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/6.x/docs/components/Dialog/_meta.json b/docs/6.x/docs/components/Dialog/_meta.json new file mode 100644 index 0000000000..81373811ab --- /dev/null +++ b/docs/6.x/docs/components/Dialog/_meta.json @@ -0,0 +1,8 @@ +[ + "Dialog", + "DialogActions", + "DialogContent", + "DialogIcon", + "DialogScrollArea", + "DialogTitle" +] diff --git a/docs/versioned_docs/version-6.x/components/Divider.mdx b/docs/6.x/docs/components/Divider.mdx similarity index 73% rename from docs/versioned_docs/version-6.x/components/Divider.mdx rename to docs/6.x/docs/components/Divider.mdx index d3b868eefe..02ff41392f 100644 --- a/docs/versioned_docs/version-6.x/components/Divider.mdx +++ b/docs/6.x/docs/components/Divider.mdx @@ -2,19 +2,19 @@ title: Divider --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A divider is a thin, lightweight separator that groups content in lists and page layouts. - - + + ## Usage ```js @@ -37,50 +37,50 @@ export default MyComponent; ## Props - - + +
### leftInset Renamed from 'inset' to 'leftInset` in v5.x
- + - +
### horizontalInset Available in v5.x with theme version 3
- + - +
### bold Available in v5.x with theme version 3
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + @@ -88,6 +88,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Drawer/DrawerCollapsedItem.mdx b/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx similarity index 79% rename from docs/versioned_docs/version-6.x/components/Drawer/DrawerCollapsedItem.mdx rename to docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx index 7193539e8f..b17999c4df 100644 --- a/docs/versioned_docs/version-6.x/components/Drawer/DrawerCollapsedItem.mdx +++ b/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx @@ -2,11 +2,11 @@ title: Drawer.CollapsedItem --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Note: Available in v5.x with theme version 3 @@ -15,8 +15,8 @@ Collapsed component used to show an action item with an icon and optionally labe - - + + ## Usage ```js @@ -37,106 +37,106 @@ export default MyComponent; ## Props - - + +
-### label +### label
- + - +
-### badge +### badge
- + - +
-### disabled +### disabled
- + - +
### focusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x
- + - +
### unfocusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x
- + - +
-### active +### active
- + - +
-### onPress +### onPress
- + - +
-### labelMaxFontSizeMultiplier +### labelMaxFontSizeMultiplier
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + @@ -144,6 +144,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Drawer/DrawerItem.mdx b/docs/6.x/docs/components/Drawer/DrawerItem.mdx similarity index 73% rename from docs/versioned_docs/version-6.x/components/Drawer/DrawerItem.mdx rename to docs/6.x/docs/components/Drawer/DrawerItem.mdx index 8320143a75..dab7c90e67 100644 --- a/docs/versioned_docs/version-6.x/components/Drawer/DrawerItem.mdx +++ b/docs/6.x/docs/components/Drawer/DrawerItem.mdx @@ -2,19 +2,19 @@ title: Drawer.Item --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component used to show an action item with an icon and a label in a navigation drawer. - - + + ## Usage ```js @@ -35,106 +35,106 @@ export default MyComponent; ## Props - - + +
-### label (required) +### label (required)
- + - +
-### icon +### icon
- + - +
-### active +### active
- + - +
-### disabled +### disabled
- + - +
-### onPress +### onPress
- + - +
-### background +### background
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### right +### right
- + - +
-### labelMaxFontSizeMultiplier +### labelMaxFontSizeMultiplier
- + - +
-### hitSlop +### hitSlop
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + @@ -142,6 +142,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Drawer/DrawerSection.mdx b/docs/6.x/docs/components/Drawer/DrawerSection.mdx similarity index 73% rename from docs/versioned_docs/version-6.x/components/Drawer/DrawerSection.mdx rename to docs/6.x/docs/components/Drawer/DrawerSection.mdx index 63c7b98497..5c34c9aef3 100644 --- a/docs/versioned_docs/version-6.x/components/Drawer/DrawerSection.mdx +++ b/docs/6.x/docs/components/Drawer/DrawerSection.mdx @@ -2,19 +2,19 @@ title: Drawer.Section --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to group content inside a navigation drawer. - - + + ## Usage ```js @@ -46,58 +46,58 @@ export default MyComponent; ## Props - - + +
-### title +### title
- + - +
-### children (required) +### children (required)
- + - +
-### showDivider +### showDivider
- + - +
-### titleMaxFontSizeMultiplier +### titleMaxFontSizeMultiplier
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + @@ -105,6 +105,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/Drawer/_meta.json b/docs/6.x/docs/components/Drawer/_meta.json new file mode 100644 index 0000000000..0099a35f77 --- /dev/null +++ b/docs/6.x/docs/components/Drawer/_meta.json @@ -0,0 +1,5 @@ +[ + "DrawerCollapsedItem", + "DrawerItem", + "DrawerSection" +] diff --git a/docs/versioned_docs/version-6.x/components/FAB/FAB.mdx b/docs/6.x/docs/components/FAB/FAB.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/FAB/FAB.mdx rename to docs/6.x/docs/components/FAB/FAB.mdx index 4c3990ce52..87ce7e9761 100644 --- a/docs/versioned_docs/version-6.x/components/FAB/FAB.mdx +++ b/docs/6.x/docs/components/FAB/FAB.mdx @@ -2,19 +2,19 @@ title: FAB --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A floating action button represents the primary action on a screen. - - + + ## Usage ```js @@ -45,122 +45,122 @@ export default MyComponent; ## Props - - + +
-### icon (required) +### icon (required)
- + - +
-### variant +### variant
- + - +
-### containerColor +### containerColor
- + - +
-### contentColor +### contentColor
- + - +
-### size +### size
- + - +
-### visible +### visible
- + - +
-### onPress +### onPress
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### accessibilityState +### accessibilityState
- + - +
-### background +### background
- + - +
-### style +### style
- + - +
-### testID +### testID
- + - +
-### theme +### theme
- + - +
-### ref +### ref
- + - - + + @@ -168,6 +168,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/FAB/FABExtended.mdx b/docs/6.x/docs/components/FAB/FABExtended.mdx similarity index 74% rename from docs/versioned_docs/version-6.x/components/FAB/FABExtended.mdx rename to docs/6.x/docs/components/FAB/FABExtended.mdx index 2ab71359a8..c5873ee2ee 100644 --- a/docs/versioned_docs/version-6.x/components/FAB/FABExtended.mdx +++ b/docs/6.x/docs/components/FAB/FABExtended.mdx @@ -2,11 +2,11 @@ title: Extended --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; An extended floating action button represents the primary action on a screen and shows a label next to the icon. Animates between expanded (icon + label) @@ -51,146 +51,146 @@ export default MyComponent; ## Props - - + +
-### icon (required) +### icon (required)
- + - +
-### label (required) +### label (required)
- + - +
-### variant +### variant
- + - +
-### containerColor +### containerColor
- + - +
-### contentColor +### contentColor
- + - +
-### size +### size
- + - +
-### expanded (required) +### expanded (required)
- + - +
-### visible +### visible
- + - +
-### onPress +### onPress
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### accessibilityState +### accessibilityState
- + - +
-### labelMaxFontSizeMultiplier +### labelMaxFontSizeMultiplier
- + - +
-### background +### background
- + - +
-### style +### style
- + - +
-### testID +### testID
- + - +
-### theme +### theme
- + - +
-### ref +### ref
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/FAB/FABMenu.mdx b/docs/6.x/docs/components/FAB/FABMenu.mdx similarity index 72% rename from docs/versioned_docs/version-6.x/components/FAB/FABMenu.mdx rename to docs/6.x/docs/components/FAB/FABMenu.mdx index e0c82b0f86..4d460f0fc4 100644 --- a/docs/versioned_docs/version-6.x/components/FAB/FABMenu.mdx +++ b/docs/6.x/docs/components/FAB/FABMenu.mdx @@ -2,11 +2,11 @@ title: Menu --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Floating action button menu. Wraps a trigger FAB; when `expanded` is true, items appear stacked above and the trigger morphs into the spec'd close @@ -19,8 +19,8 @@ via the close button or by tapping an item. - - + + ## Usage ```tsx @@ -42,74 +42,74 @@ const [open, setOpen] = React.useState(false); ## Props - - + +
-### expanded (required) +### expanded (required)
- + - +
-### onDismiss (required) +### onDismiss (required)
- + - +
-### trigger (required) +### trigger (required)
- + - +
-### alignment +### alignment
- + - +
-### closeIcon +### closeIcon
- + - +
-### items (required) +### items (required)
- + - +
-### testID +### testID
- + - +
-### theme +### theme
- + - - + + @@ -117,6 +117,6 @@ const [open, setOpen] = React.useState(false); ## Theme colors - + diff --git a/docs/6.x/docs/components/FAB/_meta.json b/docs/6.x/docs/components/FAB/_meta.json new file mode 100644 index 0000000000..5de9f066d9 --- /dev/null +++ b/docs/6.x/docs/components/FAB/_meta.json @@ -0,0 +1,5 @@ +[ + "FAB", + "FABExtended", + "FABMenu" +] diff --git a/docs/versioned_docs/version-6.x/components/Icon.mdx b/docs/6.x/docs/components/Icon.mdx similarity index 58% rename from docs/versioned_docs/version-6.x/components/Icon.mdx rename to docs/6.x/docs/components/Icon.mdx index 5dad95ddd6..0250150ef0 100644 --- a/docs/versioned_docs/version-6.x/components/Icon.mdx +++ b/docs/6.x/docs/components/Icon.mdx @@ -2,19 +2,19 @@ title: Icon --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; An icon component which renders icon from vector library. - - + + ## Usage ```js @@ -35,58 +35,58 @@ export default MyComponent; ## Props - - + +
-### size (required) +### size (required)
- + - +
-### allowFontScaling +### allowFontScaling
- + - +
-### source (required) +### source (required)
- + - +
-### color +### color
- + - +
-### testID +### testID
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/IconButton/IconButton.mdx b/docs/6.x/docs/components/IconButton/IconButton.mdx similarity index 84% rename from docs/versioned_docs/version-6.x/components/IconButton/IconButton.mdx rename to docs/6.x/docs/components/IconButton/IconButton.mdx index 00d0a69e4e..7daacaaf7f 100644 --- a/docs/versioned_docs/version-6.x/components/IconButton/IconButton.mdx +++ b/docs/6.x/docs/components/IconButton/IconButton.mdx @@ -2,19 +2,19 @@ title: IconButton --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; An icon button is a button which displays only an icon without a label. - - + + ## Usage ```js @@ -40,136 +40,136 @@ export default MyComponent; ### TouchableRipple props - +
-### icon (required) +### icon (required)
- + - +
### mode Available in v5.x with theme version 3
- + - +
### iconColor Renamed from 'color' to 'iconColor' in v5.x
- + - +
-### containerColor +### containerColor
- + - +
-### selected +### selected
- + - +
-### size +### size
- + - +
-### disabled +### disabled
- + - +
-### animated +### animated
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### contentStyle +### contentStyle
- + - +
-### onPress +### onPress
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - +
-### testID +### testID
- + - +
-### theme +### theme
- + - +
-### loading +### loading
- + - - + + @@ -177,6 +177,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/IconButton/_meta.json b/docs/6.x/docs/components/IconButton/_meta.json new file mode 100644 index 0000000000..00d733ae01 --- /dev/null +++ b/docs/6.x/docs/components/IconButton/_meta.json @@ -0,0 +1,3 @@ +[ + "IconButton" +] diff --git a/docs/versioned_docs/version-6.x/components/List/ListAccordion.mdx b/docs/6.x/docs/components/List/ListAccordion.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/List/ListAccordion.mdx rename to docs/6.x/docs/components/List/ListAccordion.mdx index eed29d3232..ea06d3a081 100644 --- a/docs/versioned_docs/version-6.x/components/List/ListAccordion.mdx +++ b/docs/6.x/docs/components/List/ListAccordion.mdx @@ -2,19 +2,19 @@ title: List.Accordion --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component used to display an expandable list item. - - + + ## Usage ```js @@ -53,210 +53,210 @@ export default MyComponent; ## Props - - + +
-### title (required) +### title (required)
- + - +
-### description +### description
- + - +
-### left +### left
- + - +
-### right +### right
- + - +
-### expanded +### expanded
- + - +
-### onPress +### onPress
- + - +
-### onLongPress +### onLongPress
- + - +
-### delayLongPress +### delayLongPress
- + - +
-### children (required) +### children (required)
- + - +
-### theme +### theme
- + - +
-### background +### background
- + - +
-### style +### style
- + - +
-### containerStyle +### containerStyle
- + - +
-### contentStyle +### contentStyle
- + - +
-### titleStyle +### titleStyle
- + - +
-### descriptionStyle +### descriptionStyle
- + - +
-### titleNumberOfLines +### titleNumberOfLines
- + - +
-### descriptionNumberOfLines +### descriptionNumberOfLines
- + - +
-### titleMaxFontSizeMultiplier +### titleMaxFontSizeMultiplier
- + - +
-### descriptionMaxFontSizeMultiplier +### descriptionMaxFontSizeMultiplier
- + - +
-### id +### id
- + - +
-### testID +### testID
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### pointerEvents +### pointerEvents
- + - +
-### hitSlop +### hitSlop
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListAccordionGroup.mdx b/docs/6.x/docs/components/List/ListAccordionGroup.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/List/ListAccordionGroup.mdx rename to docs/6.x/docs/components/List/ListAccordionGroup.mdx index 7224e87927..8829f84049 100644 --- a/docs/versioned_docs/version-6.x/components/List/ListAccordionGroup.mdx +++ b/docs/6.x/docs/components/List/ListAccordionGroup.mdx @@ -2,11 +2,11 @@ title: List.AccordionGroup --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work. List.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version. @@ -15,8 +15,8 @@ At most one Accordion can be expanded at a given time. - - + + ## Usage ```js @@ -49,34 +49,34 @@ export default MyComponent; ## Props - - + +
-### onAccordionPress +### onAccordionPress
- + - +
-### expandedId +### expandedId
- + - +
-### children (required) +### children (required)
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListIcon.mdx b/docs/6.x/docs/components/List/ListIcon.mdx similarity index 60% rename from docs/versioned_docs/version-6.x/components/List/ListIcon.mdx rename to docs/6.x/docs/components/List/ListIcon.mdx index c8b5ee12df..d9eb09a250 100644 --- a/docs/versioned_docs/version-6.x/components/List/ListIcon.mdx +++ b/docs/6.x/docs/components/List/ListIcon.mdx @@ -2,19 +2,19 @@ title: List.Icon --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show an icon in a list item. - - + + ## Usage ```js @@ -35,42 +35,42 @@ export default MyComponent; ## Props - - + +
-### icon (required) +### icon (required)
- + - +
-### color +### color
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListItem.mdx b/docs/6.x/docs/components/List/ListItem.mdx similarity index 71% rename from docs/versioned_docs/version-6.x/components/List/ListItem.mdx rename to docs/6.x/docs/components/List/ListItem.mdx index 7ba8aa8dfa..c962a55f9c 100644 --- a/docs/versioned_docs/version-6.x/components/List/ListItem.mdx +++ b/docs/6.x/docs/components/List/ListItem.mdx @@ -2,19 +2,19 @@ title: List.Item --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show tiles inside a List. - - + + ## Usage ```js @@ -39,160 +39,160 @@ export default MyComponent; ### TouchableRipple props - +
-### title (required) +### title (required)
- + - +
-### description +### description
- + - +
-### left +### left
- + - +
-### right +### right
- + - +
-### onPress +### onPress
- + - +
-### theme +### theme
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - +
-### containerStyle +### containerStyle
- + - +
-### contentStyle +### contentStyle
- + - +
-### titleStyle +### titleStyle
- + - +
-### descriptionStyle +### descriptionStyle
- + - +
-### titleNumberOfLines +### titleNumberOfLines
- + - +
-### descriptionNumberOfLines +### descriptionNumberOfLines
- + - +
-### titleEllipsizeMode +### titleEllipsizeMode
- + - +
-### descriptionEllipsizeMode +### descriptionEllipsizeMode
- + - +
-### titleMaxFontSizeMultiplier +### titleMaxFontSizeMultiplier
- + - +
-### descriptionMaxFontSizeMultiplier +### descriptionMaxFontSizeMultiplier
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/List/ListSection.mdx b/docs/6.x/docs/components/List/ListSection.mdx similarity index 68% rename from docs/versioned_docs/version-6.x/components/List/ListSection.mdx rename to docs/6.x/docs/components/List/ListSection.mdx index c90da6872d..8a145c20d0 100644 --- a/docs/versioned_docs/version-6.x/components/List/ListSection.mdx +++ b/docs/6.x/docs/components/List/ListSection.mdx @@ -2,19 +2,19 @@ title: List.Section --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component used to group list items. - - + + ## Usage ```js @@ -38,50 +38,50 @@ export default MyComponent; ## Props - - + +
-### title +### title
- + - +
-### children (required) +### children (required)
- + - +
-### theme +### theme
- + - +
-### titleStyle +### titleStyle
- + - +
-### style +### style
- + - - + + diff --git a/docs/versioned_docs/version-5.x/components/List/ListSubheader.mdx b/docs/6.x/docs/components/List/ListSubheader.mdx similarity index 59% rename from docs/versioned_docs/version-5.x/components/List/ListSubheader.mdx rename to docs/6.x/docs/components/List/ListSubheader.mdx index a5f507ec34..78db549903 100644 --- a/docs/versioned_docs/version-5.x/components/List/ListSubheader.mdx +++ b/docs/6.x/docs/components/List/ListSubheader.mdx @@ -2,11 +2,11 @@ title: List.Subheader --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component used to display a header in lists. @@ -27,34 +27,34 @@ export default MyComponent; ## Props - - + +
-### theme +### theme
- + - +
-### style +### style
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - - + + diff --git a/docs/6.x/docs/components/List/_meta.json b/docs/6.x/docs/components/List/_meta.json new file mode 100644 index 0000000000..4ba864114c --- /dev/null +++ b/docs/6.x/docs/components/List/_meta.json @@ -0,0 +1,8 @@ +[ + "ListAccordion", + "ListAccordionGroup", + "ListIcon", + "ListItem", + "ListSection", + "ListSubheader" +] diff --git a/docs/versioned_docs/version-6.x/components/Menu/Menu.mdx b/docs/6.x/docs/components/Menu/Menu.mdx similarity index 77% rename from docs/versioned_docs/version-6.x/components/Menu/Menu.mdx rename to docs/6.x/docs/components/Menu/Menu.mdx index 0d4044f003..1d1ee928b2 100644 --- a/docs/versioned_docs/version-6.x/components/Menu/Menu.mdx +++ b/docs/6.x/docs/components/Menu/Menu.mdx @@ -2,19 +2,19 @@ title: Menu --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them. - - + + ## Usage ```js @@ -62,122 +62,122 @@ wrapping is not necessary if you use Paper's `Modal` instead. ## Props - - + +
-### visible (required) +### visible (required)
- + - +
-### anchor (required) +### anchor (required)
- + - +
-### anchorPosition +### anchorPosition
- + - +
-### statusBarHeight +### statusBarHeight
- + - +
-### onDismiss +### onDismiss
- + - +
-### overlayAccessibilityLabel +### overlayAccessibilityLabel
- + - +
-### children (required) +### children (required)
- + - +
-### contentStyle +### contentStyle
- + - +
-### style +### style
- + - +
-### elevation +### elevation
- + - +
-### mode +### mode
- + - +
-### theme +### theme
- + - +
-### keyboardShouldPersistTaps +### keyboardShouldPersistTaps
- + - +
-### testID +### testID
- + - - + + @@ -185,6 +185,6 @@ wrapping is not necessary if you use Paper's `Modal` instead. ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Menu/MenuItem.mdx b/docs/6.x/docs/components/Menu/MenuItem.mdx similarity index 77% rename from docs/versioned_docs/version-6.x/components/Menu/MenuItem.mdx rename to docs/6.x/docs/components/Menu/MenuItem.mdx index 3db471c8b8..d63e012efc 100644 --- a/docs/versioned_docs/version-6.x/components/Menu/MenuItem.mdx +++ b/docs/6.x/docs/components/Menu/MenuItem.mdx @@ -2,19 +2,19 @@ title: Menu.Item --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to show a single list item inside a Menu. - - + + ## Usage ```js @@ -38,146 +38,146 @@ export default MyComponent; ## Props - - + +
-### title (required) +### title (required)
- + - +
### leadingIcon Renamed from 'icon' to 'leadingIcon' in v5.x
- + - +
### trailingIcon Available in v5.x with theme version 3
- + - +
-### disabled +### disabled
- + - +
### dense Available in v5.x with theme version 3
- + - +
-### background +### background
- + - +
-### onPress +### onPress
- + - +
-### titleMaxFontSizeMultiplier +### titleMaxFontSizeMultiplier
- + - +
-### style +### style
- + - +
-### containerStyle +### containerStyle
- + - +
-### contentStyle +### contentStyle
- + - +
-### titleStyle +### titleStyle
- + - +
-### theme +### theme
- + - +
-### hitSlop +### hitSlop
- + - +
-### testID +### testID
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### accessibilityState +### accessibilityState
- + - - + + @@ -185,6 +185,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/Menu/_meta.json b/docs/6.x/docs/components/Menu/_meta.json new file mode 100644 index 0000000000..50192eb25e --- /dev/null +++ b/docs/6.x/docs/components/Menu/_meta.json @@ -0,0 +1,4 @@ +[ + "Menu", + "MenuItem" +] diff --git a/docs/versioned_docs/version-6.x/components/Modal.mdx b/docs/6.x/docs/components/Modal.mdx similarity index 73% rename from docs/versioned_docs/version-6.x/components/Modal.mdx rename to docs/6.x/docs/components/Modal.mdx index c96efbf7b5..72691042b1 100644 --- a/docs/versioned_docs/version-6.x/components/Modal.mdx +++ b/docs/6.x/docs/components/Modal.mdx @@ -2,21 +2,21 @@ title: Modal --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; The Modal component is a simple way to present content above an enclosing view. -To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component. +To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal/Portal) component. Note that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal. - - + + ## Usage ```js @@ -50,90 +50,90 @@ export default MyComponent; ## Props - - + +
-### dismissable +### dismissable
- + - +
-### dismissableBackButton +### dismissableBackButton
- + - +
-### onDismiss +### onDismiss
- + - +
-### overlayAccessibilityLabel +### overlayAccessibilityLabel
- + - +
-### visible +### visible
- + - +
-### children (required) +### children (required)
- + - +
-### contentContainerStyle +### contentContainerStyle
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + @@ -141,6 +141,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-5.x/components/Portal/Portal.mdx b/docs/6.x/docs/components/Portal/Portal.mdx similarity index 73% rename from docs/versioned_docs/version-5.x/components/Portal/Portal.mdx rename to docs/6.x/docs/components/Portal/Portal.mdx index 9d3d325820..027dec2299 100644 --- a/docs/versioned_docs/version-5.x/components/Portal/Portal.mdx +++ b/docs/6.x/docs/components/Portal/Portal.mdx @@ -2,11 +2,11 @@ title: Portal --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Portal allows rendering a component at a different place in the parent tree. You can use it to render content which should appear above other elements, similar to `Modal`. @@ -34,40 +34,40 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### theme (required) +### theme (required)
- + - - + + ## More Examples
Toggle to grab more examples
- + diff --git a/docs/versioned_docs/version-6.x/components/Portal/PortalHost.mdx b/docs/6.x/docs/components/Portal/PortalHost.mdx similarity index 67% rename from docs/versioned_docs/version-6.x/components/Portal/PortalHost.mdx rename to docs/6.x/docs/components/Portal/PortalHost.mdx index f54c0b8609..9962cea0a9 100644 --- a/docs/versioned_docs/version-6.x/components/Portal/PortalHost.mdx +++ b/docs/6.x/docs/components/Portal/PortalHost.mdx @@ -2,11 +2,11 @@ title: Portal.Host --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Portal host renders all of its children `Portal` elements. For example, you can wrap a screen in `Portal.Host` to render items above the screen. @@ -36,18 +36,18 @@ Here any `Portal` elements under `` are rendered alongside `` and ## Props - - + +
-### children (required) +### children (required)
- + - - + + diff --git a/docs/6.x/docs/components/Portal/_meta.json b/docs/6.x/docs/components/Portal/_meta.json new file mode 100644 index 0000000000..6e5b7b374b --- /dev/null +++ b/docs/6.x/docs/components/Portal/_meta.json @@ -0,0 +1,4 @@ +[ + "Portal", + "PortalHost" +] diff --git a/docs/versioned_docs/version-6.x/components/ProgressBar.mdx b/docs/6.x/docs/components/ProgressBar.mdx similarity index 70% rename from docs/versioned_docs/version-6.x/components/ProgressBar.mdx rename to docs/6.x/docs/components/ProgressBar.mdx index 9492b0f7e9..5dd0044793 100644 --- a/docs/versioned_docs/version-6.x/components/ProgressBar.mdx +++ b/docs/6.x/docs/components/ProgressBar.mdx @@ -2,19 +2,19 @@ title: ProgressBar --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Progress bar is an indicator used to present progress of some activity in the app. - - + + ## Usage ```js @@ -31,82 +31,82 @@ export default MyComponent; ## Props - - + +
-### animatedValue +### animatedValue
- + - +
-### progress +### progress
- + - +
-### color +### color
- + - +
-### indeterminate +### indeterminate
- + - +
-### visible +### visible
- + - +
-### fillStyle +### fillStyle
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + @@ -114,6 +114,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButton.mdx b/docs/6.x/docs/components/RadioButton/RadioButton.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/RadioButton/RadioButton.mdx rename to docs/6.x/docs/components/RadioButton/RadioButton.mdx index 260bd42fe7..e4c0bec6e7 100644 --- a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButton.mdx +++ b/docs/6.x/docs/components/RadioButton/RadioButton.mdx @@ -2,19 +2,19 @@ title: RadioButton --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Radio buttons allow the selection a single option from a set. - - + + ## Usage ```js @@ -47,74 +47,74 @@ export default MyComponent; ## Props - - + +
-### value (required) +### value (required)
- + - +
-### status +### status
- + - +
-### disabled +### disabled
- + - +
-### onPress +### onPress
- + - +
-### uncheckedColor +### uncheckedColor
- + - +
-### color +### color
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonAndroid.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonAndroid.mdx similarity index 70% rename from docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonAndroid.mdx rename to docs/6.x/docs/components/RadioButton/RadioButtonAndroid.mdx index 2faf286f16..a71620c84f 100644 --- a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonAndroid.mdx +++ b/docs/6.x/docs/components/RadioButton/RadioButtonAndroid.mdx @@ -2,17 +2,17 @@ title: RadioButton.Android --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - + + Radio buttons allow the selection a single option from a set. This component follows platform guidelines for Android, but can be used @@ -25,72 +25,72 @@ on any platform. ### TouchableRipple props - +
-### value (required) +### value (required)
- + - +
-### status +### status
- + - +
-### disabled +### disabled
- + - +
-### onPress +### onPress
- + - +
-### uncheckedColor +### uncheckedColor
- + - +
-### color +### color
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonGroup.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonGroup.mdx similarity index 71% rename from docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonGroup.mdx rename to docs/6.x/docs/components/RadioButton/RadioButtonGroup.mdx index bcaa9333d0..12493f5c32 100644 --- a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonGroup.mdx +++ b/docs/6.x/docs/components/RadioButton/RadioButtonGroup.mdx @@ -2,19 +2,19 @@ title: RadioButton.Group --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Radio button group allows to control a group of radio buttons. - - + + ## Usage ```js @@ -45,34 +45,34 @@ export default MyComponent; ## Props - - + +
-### onValueChange (required) +### onValueChange (required)
- + - +
-### value (required) +### value (required)
- + - +
-### children (required) +### children (required)
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonIOS.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonIOS.mdx similarity index 69% rename from docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonIOS.mdx rename to docs/6.x/docs/components/RadioButton/RadioButtonIOS.mdx index 83db4152cc..ae561c9972 100644 --- a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonIOS.mdx +++ b/docs/6.x/docs/components/RadioButton/RadioButtonIOS.mdx @@ -2,17 +2,17 @@ title: RadioButton.IOS --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - + + Radio buttons allow the selection a single option from a set. This component follows platform guidelines for iOS, but can be used @@ -25,64 +25,64 @@ on any platform. ### TouchableRipple props - +
-### value (required) +### value (required)
- + - +
-### status +### status
- + - +
-### disabled +### disabled
- + - +
-### onPress +### onPress
- + - +
-### color +### color
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonItem.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonItem.mdx rename to docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx index c5f863d44b..c74403899e 100644 --- a/docs/versioned_docs/version-6.x/components/RadioButton/RadioButtonItem.mdx +++ b/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx @@ -2,19 +2,19 @@ title: RadioButton.Item --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton. - - + + ## Usage ```js @@ -38,162 +38,162 @@ export default MyComponent; ## Props - - + +
-### value (required) +### value (required)
- + - +
-### label (required) +### label (required)
- + - +
-### disabled +### disabled
- + - +
-### background +### background
- + - +
-### onPress +### onPress
- + - +
-### onLongPress +### onLongPress
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### uncheckedColor +### uncheckedColor
- + - +
-### color +### color
- + - +
-### status +### status
- + - +
-### style +### style
- + - +
-### labelStyle +### labelStyle
- + - +
### labelVariant Available in v5.x with theme version 3
- + - +
-### labelMaxFontSizeMultiplier +### labelMaxFontSizeMultiplier
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - +
-### mode +### mode
- + - +
-### position +### position
- + - +
-### hitSlop +### hitSlop
- + - - + + diff --git a/docs/6.x/docs/components/RadioButton/_meta.json b/docs/6.x/docs/components/RadioButton/_meta.json new file mode 100644 index 0000000000..f9c20c781a --- /dev/null +++ b/docs/6.x/docs/components/RadioButton/_meta.json @@ -0,0 +1,7 @@ +[ + "RadioButton", + "RadioButtonAndroid", + "RadioButtonGroup", + "RadioButtonIOS", + "RadioButtonItem" +] diff --git a/docs/versioned_docs/version-6.x/components/Searchbar.mdx b/docs/6.x/docs/components/Searchbar.mdx similarity index 79% rename from docs/versioned_docs/version-6.x/components/Searchbar.mdx rename to docs/6.x/docs/components/Searchbar.mdx index 657f96101f..76e475256f 100644 --- a/docs/versioned_docs/version-6.x/components/Searchbar.mdx +++ b/docs/6.x/docs/components/Searchbar.mdx @@ -2,19 +2,19 @@ title: Searchbar --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Searchbar is a simple input box where users can type search queries. - - + + ## Usage ```js @@ -40,202 +40,202 @@ export default MyComponent; ## Props - - + +
-### placeholder +### placeholder
- + - +
-### value (required) +### value (required)
- + - +
-### onChangeText +### onChangeText
- + - +
### mode Available in v5.x with theme version 3
- + - +
-### icon +### icon
- + - +
-### iconColor +### iconColor
- + - +
-### onIconPress +### onIconPress
- + - +
-### onClearIconPress +### onClearIconPress
- + - +
-### searchAccessibilityLabel +### searchAccessibilityLabel
- + - +
-### clearIcon +### clearIcon
- + - +
-### clearAccessibilityLabel +### clearAccessibilityLabel
- + - +
### traileringIcon Available in v5.x with theme version 3
- + - +
### traileringIconColor Available in v5.x with theme version 3
- + - +
-### onTraileringIconPress +### onTraileringIconPress
- + - +
-### traileringIconAccessibilityLabel +### traileringIconAccessibilityLabel
- + - +
### right Available in v5.x with theme version 3
- + - +
### showDivider Available in v5.x with theme version 3
- + - +
### elevation Available in v5.x with theme version 3
- + - +
-### inputStyle +### inputStyle
- + - +
-### style +### style
- + - +
-### loading +### loading
- + - +
-### testID +### testID
- + - +
-### theme +### theme
- + - +
-### ref +### ref
- + - - + + @@ -243,6 +243,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/SegmentedButtons/SegmentedButtons.mdx b/docs/6.x/docs/components/SegmentedButtons/SegmentedButtons.mdx similarity index 79% rename from docs/versioned_docs/version-6.x/components/SegmentedButtons/SegmentedButtons.mdx rename to docs/6.x/docs/components/SegmentedButtons/SegmentedButtons.mdx index 36496a7c1b..fbd734c612 100644 --- a/docs/versioned_docs/version-6.x/components/SegmentedButtons/SegmentedButtons.mdx +++ b/docs/6.x/docs/components/SegmentedButtons/SegmentedButtons.mdx @@ -2,19 +2,19 @@ title: SegmentedButtons --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Segmented buttons can be used to select options, switch views or sort elements. - - + + ## Usage ```js @@ -59,42 +59,42 @@ export default MyComponent; ## Props - - + +
-### buttons (required) +### buttons (required)
- + - +
-### density +### density
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - - + + @@ -102,6 +102,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/SegmentedButtons/_meta.json b/docs/6.x/docs/components/SegmentedButtons/_meta.json new file mode 100644 index 0000000000..85b4317790 --- /dev/null +++ b/docs/6.x/docs/components/SegmentedButtons/_meta.json @@ -0,0 +1,3 @@ +[ + "SegmentedButtons" +] diff --git a/docs/versioned_docs/version-6.x/components/Snackbar.mdx b/docs/6.x/docs/components/Snackbar.mdx similarity index 81% rename from docs/versioned_docs/version-6.x/components/Snackbar.mdx rename to docs/6.x/docs/components/Snackbar.mdx index 66581f61fc..f9ab14a337 100644 --- a/docs/versioned_docs/version-6.x/components/Snackbar.mdx +++ b/docs/6.x/docs/components/Snackbar.mdx @@ -2,11 +2,11 @@ title: Snackbar --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped. @@ -15,8 +15,8 @@ Note: To display it as a popup, regardless of the parent's position, wrap it wit - - + + ## Usage ```js @@ -62,157 +62,157 @@ export default MyComponent; ## Props - - + +
-### visible (required) +### visible (required)
- + - +
-### action +### action
- + - +
### icon Available in v5.x with theme version 3
- + - +
-### onIconPress +### onIconPress
- + - +
### iconAccessibilityLabel Available in v5.x with theme version 3
- + - +
-### duration +### duration
- + - +
-### onDismiss (required) +### onDismiss (required)
- + - +
-### children (required) +### children (required)
- + - +
### elevation Available in v5.x with theme version 3
- + - +
-### maxFontSizeMultiplier +### maxFontSizeMultiplier
- + - +
-### wrapperStyle +### wrapperStyle
- + - +
-### contentStyle +### contentStyle
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - - + + ## More Examples
Toggle to grab more examples
- + ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Surface.mdx b/docs/6.x/docs/components/Surface.mdx similarity index 80% rename from docs/versioned_docs/version-6.x/components/Surface.mdx rename to docs/6.x/docs/components/Surface.mdx index e70b85c42e..841a74e3e4 100644 --- a/docs/versioned_docs/version-6.x/components/Surface.mdx +++ b/docs/6.x/docs/components/Surface.mdx @@ -2,11 +2,11 @@ title: Surface --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Surface is a basic container that can give depth to an element with elevation shadow. On dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface. @@ -16,8 +16,8 @@ Overlay and shadow can be applied by specifying the `elevation` property both on - - + + ## Usage ```js @@ -47,66 +47,66 @@ const styles = StyleSheet.create({ ## Props - - + +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
### elevation Available in v5.x with theme version 3
- + - +
### mode Available in v5.x with theme version 3
- + - +
-### theme +### theme
- + - +
-### testID +### testID
- + - +
-### ref +### ref
- + - - + + @@ -114,6 +114,6 @@ const styles = StyleSheet.create({ ## Theme colors - + diff --git a/docs/versioned_docs/version-6.x/components/Switch/Switch.mdx b/docs/6.x/docs/components/Switch/Switch.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/Switch/Switch.mdx rename to docs/6.x/docs/components/Switch/Switch.mdx index 12d997fa26..460b34024b 100644 --- a/docs/versioned_docs/version-6.x/components/Switch/Switch.mdx +++ b/docs/6.x/docs/components/Switch/Switch.mdx @@ -2,19 +2,19 @@ title: Switch --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Material 3 toggle between two mutually exclusive states (on / off). - - + + ## Usage ```js @@ -41,82 +41,82 @@ Customize by overriding these `theme.colors` roles: ## Props - - + +
-### value +### value
- + - +
-### onValueChange +### onValueChange
- + - +
-### disabled +### disabled
- + - +
-### checkedIcon +### checkedIcon
- + - +
-### uncheckedIcon +### uncheckedIcon
- + - +
-### style +### style
- + - +
-### testID +### testID
- + - +
-### theme +### theme
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - - + + diff --git a/docs/6.x/docs/components/Switch/_meta.json b/docs/6.x/docs/components/Switch/_meta.json new file mode 100644 index 0000000000..3bc6b9e49c --- /dev/null +++ b/docs/6.x/docs/components/Switch/_meta.json @@ -0,0 +1,3 @@ +[ + "Switch" +] diff --git a/docs/versioned_docs/version-6.x/components/Text/Text.mdx b/docs/6.x/docs/components/Text/Text.mdx similarity index 78% rename from docs/versioned_docs/version-6.x/components/Text/Text.mdx rename to docs/6.x/docs/components/Text/Text.mdx index 076a690898..2e38542594 100644 --- a/docs/versioned_docs/version-6.x/components/Text/Text.mdx +++ b/docs/6.x/docs/components/Text/Text.mdx @@ -2,19 +2,19 @@ title: Text --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Typography component showing styles complied with passed `variant` prop and supported by the type system. - - + + ## Usage ```js @@ -55,48 +55,48 @@ export default MyComponent; ### Text props - +
### variant Available in v5.x with theme version 3
- + - +
-### children (required) +### children (required)
- + - +
-### theme +### theme
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - - + + @@ -104,6 +104,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/Text/_meta.json b/docs/6.x/docs/components/Text/_meta.json new file mode 100644 index 0000000000..2837ff31af --- /dev/null +++ b/docs/6.x/docs/components/Text/_meta.json @@ -0,0 +1,3 @@ +[ + "Text" +] diff --git a/docs/versioned_docs/version-6.x/components/TextInput/TextInput.mdx b/docs/6.x/docs/components/TextInput/TextInput.mdx similarity index 79% rename from docs/versioned_docs/version-6.x/components/TextInput/TextInput.mdx rename to docs/6.x/docs/components/TextInput/TextInput.mdx index 1928ab0072..5a3fb96042 100644 --- a/docs/versioned_docs/version-6.x/components/TextInput/TextInput.mdx +++ b/docs/6.x/docs/components/TextInput/TextInput.mdx @@ -2,11 +2,11 @@ title: TextInput --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A text input lets users enter and edit text. It shows an optional floating label, supports `filled` and `outlined` variants, optional supporting text (including @@ -15,8 +15,8 @@ error state), and start/end accessories. - - + + ## Usage ```js @@ -63,112 +63,112 @@ export default MyComponent; ### TextInput props - +
-### ref +### ref
- + - +
-### variant +### variant
- + - +
-### error +### error
- + - +
-### label +### label
- + - +
-### supportingText +### supportingText
- + - +
-### counter +### counter
- + - +
-### disabled +### disabled
- + - +
-### prefix +### prefix
- + - +
-### suffix +### suffix
- + - +
-### theme +### theme
- + - +
-### startAccessory +### startAccessory
- + - +
-### endAccessory +### endAccessory
- + - +
-### render +### render
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/TextInput/TextInputIcon.mdx b/docs/6.x/docs/components/TextInput/TextInputIcon.mdx similarity index 75% rename from docs/versioned_docs/version-6.x/components/TextInput/TextInputIcon.mdx rename to docs/6.x/docs/components/TextInput/TextInputIcon.mdx index 96271b8578..9076293c9f 100644 --- a/docs/versioned_docs/version-6.x/components/TextInput/TextInputIcon.mdx +++ b/docs/6.x/docs/components/TextInput/TextInputIcon.mdx @@ -2,11 +2,11 @@ title: TextInput.Icon --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A component to render a leading / trailing icon in the TextInput (return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as @@ -53,40 +53,40 @@ export default MyComponent; ### IconButton props - +
-### style (required) +### style (required)
- + - +
-### multiline (required) +### multiline (required)
- + - +
-### disabled (required) +### disabled (required)
- + - +
-### error (required) +### error (required)
- + - - + + diff --git a/docs/6.x/docs/components/TextInput/_meta.json b/docs/6.x/docs/components/TextInput/_meta.json new file mode 100644 index 0000000000..0d94a124d7 --- /dev/null +++ b/docs/6.x/docs/components/TextInput/_meta.json @@ -0,0 +1,4 @@ +[ + "TextInput", + "TextInputIcon" +] diff --git a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButton.mdx b/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx similarity index 75% rename from docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButton.mdx rename to docs/6.x/docs/components/ToggleButton/ToggleButton.mdx index 990a014737..a0dca55b21 100644 --- a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButton.mdx +++ b/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx @@ -2,11 +2,11 @@ title: ToggleButton --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons, a group should share a common container. @@ -14,8 +14,8 @@ a group should share a common container. - - + + ## Usage ```js @@ -46,106 +46,106 @@ export default ToggleButtonExample; ## Props - - + +
-### icon (required) +### icon (required)
- + - +
-### size +### size
- + - +
-### iconColor +### iconColor
- + - +
-### disabled +### disabled
- + - +
-### accessibilityLabel +### accessibilityLabel
- + - +
-### onPress +### onPress
- + - +
-### value +### value
- + - +
-### status +### status
- + - +
-### style +### style
- + - +
-### theme +### theme
- + - +
-### ref +### ref
- + - +
-### testID +### testID
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonGroup.mdx b/docs/6.x/docs/components/ToggleButton/ToggleButtonGroup.mdx similarity index 70% rename from docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonGroup.mdx rename to docs/6.x/docs/components/ToggleButton/ToggleButtonGroup.mdx index e29c66ad41..efc0aa4037 100644 --- a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonGroup.mdx +++ b/docs/6.x/docs/components/ToggleButton/ToggleButtonGroup.mdx @@ -2,11 +2,11 @@ title: ToggleButton.Group --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Toggle group allows to control a group of toggle buttons. It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow). @@ -14,8 +14,8 @@ It doesn't change the appearance of the toggle buttons. If you want to group the - - + + ## Usage ```js @@ -42,34 +42,34 @@ export default MyComponent; ## Props - - + +
-### onValueChange (required) +### onValueChange (required)
- + - +
-### value (required) +### value (required)
- + - +
-### children (required) +### children (required)
- + - - + + diff --git a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonRow.mdx b/docs/6.x/docs/components/ToggleButton/ToggleButtonRow.mdx similarity index 68% rename from docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonRow.mdx rename to docs/6.x/docs/components/ToggleButton/ToggleButtonRow.mdx index 8ef840854d..7274440515 100644 --- a/docs/versioned_docs/version-6.x/components/ToggleButton/ToggleButtonRow.mdx +++ b/docs/6.x/docs/components/ToggleButton/ToggleButtonRow.mdx @@ -2,19 +2,19 @@ title: ToggleButton.Row --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Toggle button row renders a group of toggle buttons in a row. - - + + ## Usage ```js @@ -39,42 +39,42 @@ export default MyComponent; ## Props - - + +
-### onValueChange (required) +### onValueChange (required)
- + - +
-### value (required) +### value (required)
- + - +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - - + + diff --git a/docs/6.x/docs/components/ToggleButton/_meta.json b/docs/6.x/docs/components/ToggleButton/_meta.json new file mode 100644 index 0000000000..935c2b9584 --- /dev/null +++ b/docs/6.x/docs/components/ToggleButton/_meta.json @@ -0,0 +1,5 @@ +[ + "ToggleButton", + "ToggleButtonGroup", + "ToggleButtonRow" +] diff --git a/docs/versioned_docs/version-6.x/components/Tooltip/Tooltip.mdx b/docs/6.x/docs/components/Tooltip/Tooltip.mdx similarity index 69% rename from docs/versioned_docs/version-6.x/components/Tooltip/Tooltip.mdx rename to docs/6.x/docs/components/Tooltip/Tooltip.mdx index 840879f1b8..2e4cc57fe8 100644 --- a/docs/versioned_docs/version-6.x/components/Tooltip/Tooltip.mdx +++ b/docs/6.x/docs/components/Tooltip/Tooltip.mdx @@ -2,11 +2,11 @@ title: Tooltip --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Tooltips display informative text when users hover over, focus on, or tap an element. @@ -15,8 +15,8 @@ Plain tooltips, when activated, display a text label identifying an element, suc - - + + ## Usage ```js @@ -35,58 +35,58 @@ export default MyComponent; ## Props - - + +
-### children (required) +### children (required)
- + - +
-### enterTouchDelay +### enterTouchDelay
- + - +
-### leaveTouchDelay +### leaveTouchDelay
- + - +
-### title (required) +### title (required)
- + - +
-### titleMaxFontSizeMultiplier +### titleMaxFontSizeMultiplier
- + - +
-### theme +### theme
- + - - + + @@ -94,6 +94,6 @@ export default MyComponent; ## Theme colors - + diff --git a/docs/6.x/docs/components/Tooltip/_meta.json b/docs/6.x/docs/components/Tooltip/_meta.json new file mode 100644 index 0000000000..c39a54301f --- /dev/null +++ b/docs/6.x/docs/components/Tooltip/_meta.json @@ -0,0 +1,3 @@ +[ + "Tooltip" +] diff --git a/docs/versioned_docs/version-6.x/components/TouchableRipple/TouchableRipple.mdx b/docs/6.x/docs/components/TouchableRipple/TouchableRipple.mdx similarity index 76% rename from docs/versioned_docs/version-6.x/components/TouchableRipple/TouchableRipple.mdx rename to docs/6.x/docs/components/TouchableRipple/TouchableRipple.mdx index c94b4f9b64..ba5c8bfcc4 100644 --- a/docs/versioned_docs/version-6.x/components/TouchableRipple/TouchableRipple.mdx +++ b/docs/6.x/docs/components/TouchableRipple/TouchableRipple.mdx @@ -2,11 +2,11 @@ title: TouchableRipple --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; A wrapper for views that should respond to touches. Provides a material "ink ripple" interaction effect for supported platforms (>= Android Lollipop). @@ -15,8 +15,8 @@ On unsupported platforms, it falls back to a highlight effect. - - + + ## Usage ```js @@ -43,120 +43,120 @@ export default MyComponent; ### Pressable props - +
-### borderless +### borderless
- + - +
-### background +### background
- + - +
-### centered +### centered
- + - +
-### disabled +### disabled
- + - +
-### onPress +### onPress
- + - +
-### onLongPress +### onLongPress
- + - +
-### onPressIn +### onPressIn
- + - +
-### onPressOut +### onPressOut
- + - +
-### rippleColor +### rippleColor
- + - +
-### underlayColor +### underlayColor
- + - +
-### children (required) +### children (required)
- + - +
-### style +### style
- + - +
-### ref +### ref
- + - +
-### theme +### theme
- + - - + + diff --git a/docs/6.x/docs/components/TouchableRipple/_meta.json b/docs/6.x/docs/components/TouchableRipple/_meta.json new file mode 100644 index 0000000000..91b70a001b --- /dev/null +++ b/docs/6.x/docs/components/TouchableRipple/_meta.json @@ -0,0 +1,3 @@ +[ + "TouchableRipple" +] diff --git a/docs/6.x/docs/components/_meta.json b/docs/6.x/docs/components/_meta.json new file mode 100644 index 0000000000..6f793d41a3 --- /dev/null +++ b/docs/6.x/docs/components/_meta.json @@ -0,0 +1,173 @@ +[ + "ActivityIndicator", + { + "type": "dir", + "name": "Appbar", + "label": "Appbar", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Avatar", + "label": "Avatar", + "collapsible": true, + "collapsed": false + }, + "Badge", + "Banner", + { + "type": "dir", + "name": "BottomNavigation", + "label": "BottomNavigation", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Button", + "label": "Button", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Card", + "label": "Card", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Checkbox", + "label": "Checkbox", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Chip", + "label": "Chip", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "DataTable", + "label": "DataTable", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Dialog", + "label": "Dialog", + "collapsible": true, + "collapsed": false + }, + "Divider", + { + "type": "dir", + "name": "Drawer", + "label": "Drawer", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "FAB", + "label": "FAB", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "IconButton", + "label": "IconButton", + "collapsible": true, + "collapsed": false + }, + "Icon", + { + "type": "dir", + "name": "List", + "label": "List", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Menu", + "label": "Menu", + "collapsible": true, + "collapsed": false + }, + "Modal", + { + "type": "dir", + "name": "Portal", + "label": "Portal", + "collapsible": true, + "collapsed": false + }, + "ProgressBar", + { + "type": "dir", + "name": "RadioButton", + "label": "RadioButton", + "collapsible": true, + "collapsed": false + }, + "Searchbar", + { + "type": "dir", + "name": "SegmentedButtons", + "label": "SegmentedButtons", + "collapsible": true, + "collapsed": false + }, + "Snackbar", + "Surface", + { + "type": "dir", + "name": "Switch", + "label": "Switch", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "TextInput", + "label": "TextInput", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "ToggleButton", + "label": "ToggleButton", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Tooltip", + "label": "Tooltip", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "TouchableRipple", + "label": "TouchableRipple", + "collapsible": true, + "collapsed": false + }, + { + "type": "dir", + "name": "Text", + "label": "Text", + "collapsible": true, + "collapsed": false + } +] diff --git a/docs/6.x/docs/guides/_meta.json b/docs/6.x/docs/guides/_meta.json new file mode 100644 index 0000000000..11525283c4 --- /dev/null +++ b/docs/6.x/docs/guides/_meta.json @@ -0,0 +1,15 @@ +[ + "getting-started", + "theming", + "icons", + "fonts", + "react-native-web", + "recommended-libraries", + "contributing", + "theming-with-react-navigation", + "bottom-navigation", + "react-navigation", + "rtl", + "ripple-effect", + "migration" +] diff --git a/docs/versioned_docs/version-6.x/guides/09-bottom-navigation.md b/docs/6.x/docs/guides/bottom-navigation.md similarity index 100% rename from docs/versioned_docs/version-6.x/guides/09-bottom-navigation.md rename to docs/6.x/docs/guides/bottom-navigation.md diff --git a/docs/versioned_docs/version-6.x/guides/07-contributing.mdx b/docs/6.x/docs/guides/contributing.mdx similarity index 100% rename from docs/versioned_docs/version-6.x/guides/07-contributing.mdx rename to docs/6.x/docs/guides/contributing.mdx diff --git a/docs/versioned_docs/version-6.x/guides/04-fonts.md b/docs/6.x/docs/guides/fonts.md similarity index 54% rename from docs/versioned_docs/version-6.x/guides/04-fonts.md rename to docs/6.x/docs/guides/fonts.md index 5e8e435c0a..94a8eb40fe 100644 --- a/docs/versioned_docs/version-6.x/guides/04-fonts.md +++ b/docs/6.x/docs/guides/fonts.md @@ -8,40 +8,39 @@ title: Fonts The easiest way to install custom fonts to your RN project is do as follows: - 1. Define path to assets directory with fonts in project: +1. Define path to assets directory with fonts in project: - Example: +Example: - ```js - module.exports = { - ... - assets: [ - './assets/fonts' - ], - ``` +```js + module.exports = { + ... + assets: [ + './assets/fonts' + ], +``` :::note `fonts` is a folder with `.ttf` files ::: - 2. Place your font files in your assets directory. - - 3. Link font files, using the following command in the terminal: +2. Place your font files in your assets directory. - * React Native `>= 0.69`: +3. Link font files, using the following command in the terminal: - ```sh - npx react-native-asset - ``` +- React Native `>= 0.69`: - * React Native `< 0.69`: +```sh +npx react-native-asset +``` - ```sh - npx react-native link - ``` +- React Native `< 0.69`: +```sh +npx react-native link +``` - 4. Restart your project to refresh changes. +4. Restart your project to refresh changes. Now, you are able to use `fontFamily` from font files. @@ -67,234 +66,235 @@ Platform.select({ default: 'sans-serif', // and 'sans-serif-medium' for `fontWeight:"500"` }), ``` + ::: -* #### Display +- #### Display
- ```json - "displaySmall": { - "fontFamily": "Font", - "fontSize": 36, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 44, - } - ``` +```json +"displaySmall": { + "fontFamily": "Font", + "fontSize": 36, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 44, +} +```
- ```json - "displayMedium": { - "fontFamily": "Font", - "fontSize": 45, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 52, - } - ``` +```json +"displayMedium": { + "fontFamily": "Font", + "fontSize": 45, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 52, +} +```
- ```json - "displayLarge": { - "fontFamily": "Font", - "fontSize": 57, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 64, - } - ``` +```json +"displayLarge": { + "fontFamily": "Font", + "fontSize": 57, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 64, +} +```
-* #### Headline +- #### Headline
- ```json - "headlineSmall": { - "fontFamily": "Font", - "fontSize": 24, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 32, - } - ``` +```json +"headlineSmall": { + "fontFamily": "Font", + "fontSize": 24, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 32, +} +```
- ```json - "headlineMedium": { - "fontFamily": "Font", - "fontSize": 28, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 36, - } - ``` +```json +"headlineMedium": { + "fontFamily": "Font", + "fontSize": 28, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 36, +} +```
- ```json - "headlineLarge": { - "fontFamily": "Font", - "fontSize": 32, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 40, - } - ``` +```json +"headlineLarge": { + "fontFamily": "Font", + "fontSize": 32, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 40, +} +```
-* #### Title +- #### Title
- ```json - "titleSmall": { - "fontFamily": "Font", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - } - ``` +```json +"titleSmall": { + "fontFamily": "Font", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, +} +```
- ```json - "titleMedium": { - "fontFamily": "Font", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - } - ``` +```json +"titleMedium": { + "fontFamily": "Font", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, +} +```
- ```json - "titleLarge": { - "fontFamily": "Font", - "fontSize": 22, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 28, - } - ``` +```json +"titleLarge": { + "fontFamily": "Font", + "fontSize": 22, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 28, +} +```
-* #### Label +- #### Label
- ```json - "labelSmall": { - "fontFamily": "Font", - "fontSize": 11, - "fontWeight": "500", - "letterSpacing": 0.5, - "lineHeight": 16, - } - ``` +```json +"labelSmall": { + "fontFamily": "Font", + "fontSize": 11, + "fontWeight": "500", + "letterSpacing": 0.5, + "lineHeight": 16, +} +```
- ```json - "labelMedium": { - "fontFamily": "Font", - "fontSize": 12, - "fontWeight": "500", - "letterSpacing": 0.5, - "lineHeight": 16, - } - ``` +```json +"labelMedium": { + "fontFamily": "Font", + "fontSize": 12, + "fontWeight": "500", + "letterSpacing": 0.5, + "lineHeight": 16, +} +```
- ```json - "labelLarge": { - "fontFamily": "Font", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - } - ``` +```json +"labelLarge": { + "fontFamily": "Font", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, +} +```
-* #### Body +- #### Body
- ```json - "bodySmall": { - "fontFamily": "Font", - "fontSize": 12, - "fontWeight": "400", - "letterSpacing": 0.4, - "lineHeight": 16, - } - ``` +```json +"bodySmall": { + "fontFamily": "Font", + "fontSize": 12, + "fontWeight": "400", + "letterSpacing": 0.4, + "lineHeight": 16, +} +```
- ```json - "bodyMedium": { - "fontFamily": "Font", - "fontSize": 14, - "fontWeight": "400", - "letterSpacing": 0.25, - "lineHeight": 20, - } - ``` +```json +"bodyMedium": { + "fontFamily": "Font", + "fontSize": 14, + "fontWeight": "400", + "letterSpacing": 0.25, + "lineHeight": 20, +} +```
- ```json - "bodyLarge": { - "fontFamily": "Font", - "fontSize": 16, - "fontWeight": "400", - "letterSpacing": 0.15, - "lineHeight": 24, - } - ``` +```json +"bodyLarge": { + "fontFamily": "Font", + "fontSize": 16, + "fontWeight": "400", + "letterSpacing": 0.15, + "lineHeight": 24, +} +```
@@ -309,16 +309,21 @@ If any component uses Paper's `Text` component, without specified variant "letterSpacing": 0, }, ``` + ::: #### Using `configureFonts` helper -* If there is a need to create a custom font variant, prepare its config object including required all fonts properties. After that, defined `fontConfig` has to be passed under the `variant` name as `config` into the params object: +- If there is a need to create a custom font variant, prepare its config object including required all fonts properties. After that, defined `fontConfig` has to be passed under the `variant` name as `config` into the params object: ```js import * as React from 'react'; -import { configureFonts, MD3LightTheme, PaperProvider } from 'react-native-paper'; -import App from './src/App'; +import { + configureFonts, + MD3LightTheme, + PaperProvider, +} from 'react-native-paper'; +import App from './App'; const fontConfig = { customVariant: { @@ -331,12 +336,12 @@ const fontConfig = { letterSpacing: 0.5, lineHeight: 22, fontSize: 20, - } + }, }; const theme = { ...MD3LightTheme, - fonts: configureFonts({config: fontConfig}), + fonts: configureFonts({ config: fontConfig }), }; export default function Main() { @@ -351,31 +356,34 @@ export default function Main() { If you're using TypeScript you will need to create a custom `Text` component which accepts your custom variants: ```typescript -import { customText } from 'react-native-paper' +import { customText } from 'react-native-paper'; // Use this instead of importing `Text` from `react-native-paper` -export const Text = customText<'customVariant'>() +export const Text = customText<'customVariant'>(); ``` - -* In order to override one of the available `variant`'s font properties, pass the modified `fontConfig` under specific `variant` name as `config` into the params object: +- In order to override one of the available `variant`'s font properties, pass the modified `fontConfig` under specific `variant` name as `config` into the params object: ```js import * as React from 'react'; -import { configureFonts, MD3LightTheme, PaperProvider } from 'react-native-paper'; -import App from './src/App'; +import { + configureFonts, + MD3LightTheme, + PaperProvider, +} from 'react-native-paper'; +import App from './App'; const fontConfig = { bodyLarge: { letterSpacing: 0.5, lineHeight: 22, fontSize: 20, - } + }, }; const theme = { ...MD3LightTheme, - fonts: configureFonts({config: fontConfig}), + fonts: configureFonts({ config: fontConfig }), }; export default function Main() { @@ -387,20 +395,24 @@ export default function Main() { } ``` -* However, if you just want to override any font property e.g. `fontFamily` or `letterSpacing` for all variants, you can pass the `fontConfig` as a `config` into the params object without specifying variant name: +- However, if you just want to override any font property e.g. `fontFamily` or `letterSpacing` for all variants, you can pass the `fontConfig` as a `config` into the params object without specifying variant name: ```js import * as React from 'react'; -import { configureFonts, MD3LightTheme, PaperProvider } from 'react-native-paper'; -import App from './src/App'; +import { + configureFonts, + MD3LightTheme, + PaperProvider, +} from 'react-native-paper'; +import App from './App'; const fontConfig = { - fontFamily: 'NotoSans' + fontFamily: 'NotoSans', }; const theme = { ...MD3LightTheme, - fonts: configureFonts({config: fontConfig}), + fonts: configureFonts({ config: fontConfig }), }; export default function Main() { @@ -412,17 +424,16 @@ export default function Main() { } ``` - ## Variable fonts -Although React Native Paper supports `fontWeight` and `fontStyle` properties, there are multiple limitations to custom -fonts in React Native. Using custom [variable fonts](https://fonts.google.com/knowledge/introducing_type/introducing_variable_fonts) +Although React Native Paper supports `fontWeight` and `fontStyle` properties, there are multiple limitations to custom +fonts in React Native. Using custom [variable fonts](https://fonts.google.com/knowledge/introducing_type/introducing_variable_fonts) is especially problematic, with some platforms failing to render variants entirely. To ensure correct typography in your -app, we suggest installing each font variant as a separate file. Below you'll find example on how to set up React Native Paper -theme with custom fonts. +app, we suggest installing each font variant as a separate file. Below you'll find example on how to set up React Native Paper +theme with custom fonts. Should you decide to use a variable font anyway, second example will show you how to test if the font is rendered correctly in React Native on all platforms. - +
Variable fonts examples
    diff --git a/docs/versioned_docs/version-6.x/guides/01-getting-started.mdx b/docs/6.x/docs/guides/getting-started.mdx similarity index 98% rename from docs/versioned_docs/version-6.x/guides/01-getting-started.mdx rename to docs/6.x/docs/guides/getting-started.mdx index 53c3c3519e..f5c438c080 100644 --- a/docs/versioned_docs/version-6.x/guides/01-getting-started.mdx +++ b/docs/6.x/docs/guides/getting-started.mdx @@ -2,8 +2,8 @@ title: Getting Started --- -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import TabItem from '@docs/components/TabItem'; +import Tabs from '@docs/components/Tabs'; # Getting Started diff --git a/docs/versioned_docs/version-6.x/guides/03-icons.mdx b/docs/6.x/docs/guides/icons.mdx similarity index 96% rename from docs/versioned_docs/version-6.x/guides/03-icons.mdx rename to docs/6.x/docs/guides/icons.mdx index 5b6b4ba724..813678733d 100644 --- a/docs/versioned_docs/version-6.x/guides/03-icons.mdx +++ b/docs/6.x/docs/guides/icons.mdx @@ -2,15 +2,15 @@ title: Icons --- -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; -import IconsList from '@site/src/components/IconsList.tsx'; +import TabItem from '@docs/components/TabItem'; +import Tabs from '@docs/components/Tabs'; +import IconsList from '@docs/components/IconsList.tsx'; # Icons ## Configuring icons -Many of the components render icon names using [`@react-native-vector-icons/material-design-icons`](https://github.com/oblador/react-native-vector-icons/tree/master/packages/material-design-icons). In Expo projects this usually works without extra setup. In vanilla React Native projects, install the package and complete any required platform setup as described in the [getting started guide](./01-getting-started.mdx). +Many of the components render icon names using [`@react-native-vector-icons/material-design-icons`](https://github.com/oblador/react-native-vector-icons/tree/master/packages/material-design-icons). In Expo projects this usually works without extra setup. In vanilla React Native projects, install the package and complete any required platform setup as described in the [getting started guide](./getting-started). :::note If you opted out of vector icons support using [babel-plugin-optional-require](https://github.com/satya164/babel-plugin-optional-require), you won't be able to use icon names for the icon prop. Some components may not look correct without vector icons and might need extra configuration. diff --git a/docs/versioned_docs/version-6.x/guides/12-migration.md b/docs/6.x/docs/guides/migration.md similarity index 100% rename from docs/versioned_docs/version-6.x/guides/12-migration.md rename to docs/6.x/docs/guides/migration.md diff --git a/docs/versioned_docs/version-6.x/guides/05-react-native-web.md b/docs/6.x/docs/guides/react-native-web.md similarity index 92% rename from docs/versioned_docs/version-6.x/guides/05-react-native-web.md rename to docs/6.x/docs/guides/react-native-web.md index 747fee8b05..9b9d4e075f 100644 --- a/docs/versioned_docs/version-6.x/guides/05-react-native-web.md +++ b/docs/6.x/docs/guides/react-native-web.md @@ -6,7 +6,7 @@ title: Using on the Web React Native Paper supports web via [React Native for Web](https://necolas.github.io/react-native-web/), which lets you run React Native components in a browser using React DOM. -Before continuing, make sure you have React Native Paper installed and configured by following the [Getting Started guide](01-getting-started.mdx). +Before continuing, make sure you have React Native Paper installed and configured by following the [Getting Started guide](getting-started). ## Setting up web support with Expo @@ -33,7 +33,8 @@ You will also need to manually load the Material Design icon font used by Paper. ```css @font-face { font-family: 'MaterialDesignIcons'; - src: url('~@react-native-vector-icons/material-design-icons/fonts/MaterialDesignIcons.ttf') format('truetype'); + src: url('~@react-native-vector-icons/material-design-icons/fonts/MaterialDesignIcons.ttf') + format('truetype'); } ``` diff --git a/docs/versioned_docs/version-6.x/guides/09-react-navigation.md b/docs/6.x/docs/guides/react-navigation.md similarity index 93% rename from docs/versioned_docs/version-6.x/guides/09-react-navigation.md rename to docs/6.x/docs/guides/react-navigation.md index 5ea5d556fa..6c01200f11 100644 --- a/docs/versioned_docs/version-6.x/guides/09-react-navigation.md +++ b/docs/6.x/docs/guides/react-navigation.md @@ -6,14 +6,14 @@ title: Integrate AppBar with React Navigation ## Prerequisites - - `react-native-paper` - - `react-navigation` +- `react-native-paper` +- `react-navigation` We assume that you have already installed the mentioned libraries above, otherwise please check out the guides below. - [React Native Paper - Getting Started](https://callstack.github.io/react-native-paper/docs/guides/getting-started) +[React Native Paper - Getting Started](https://callstack.github.io/react-native-paper/docs/guides/getting-started) - [React Navigation - Getting Started](https://reactnavigation.org/docs/getting-started/) +[React Navigation - Getting Started](https://reactnavigation.org/docs/getting-started/) ## Stack Navigator @@ -46,7 +46,7 @@ At the moment our navigation stack contains two screens and will render `HomeScr ```js import React from 'react'; -import {View, Text, Button, StyleSheet} from 'react-native'; +import { View, Text, Button, StyleSheet } from 'react-native'; function HomeScreen() { return ( @@ -120,7 +120,8 @@ export default function App() { initialRouteName="Home" screenOptions={{ header: (props) => , - }}> + }} + > @@ -169,7 +170,12 @@ In order to achieve it, we firstly check, if the navigation bar receives a `back import { Appbar } from 'react-native-paper'; import { getHeaderTitle } from '@react-navigation/elements'; -export default function CustomNavigationBar({ navigation, route, options, back }) { +export default function CustomNavigationBar({ + navigation, + route, + options, + back, +}) { const title = getHeaderTitle(options, route.name); return ( @@ -186,6 +192,7 @@ export default function CustomNavigationBar({ navigation, route, options, back } ### Adding more items to `Appbar` Another interesting pattern that can be implemented with `react-native-paper` and `react-navigation` is a "menu" button. Thanks to the `Menu` component we can add a nice looking pop-up to our `Appbar`. To implement this feature we need to make a couple of changes in `CustomNavigationBar`: + - Render a `Menu` component - Pass `Appbar.Action` to the anchor prop - Add a state to control `Menu` visibility @@ -204,13 +211,15 @@ import { PaperProvider } from 'react-native-paper'; initialRouteName="Home" screenOptions={{ header: (props) => , - }}> + }} + > - +; ``` + ::: We also want the menu to appear only on `HomeScreen`, which means we will render it conditionally based on the `back` prop. @@ -240,12 +249,8 @@ export default function CustomNavigationBar({ - }> + anchor={} + > { console.log('Option 1 was pressed'); diff --git a/docs/versioned_docs/version-6.x/guides/06-recommended-libraries.md b/docs/6.x/docs/guides/recommended-libraries.md similarity index 99% rename from docs/versioned_docs/version-6.x/guides/06-recommended-libraries.md rename to docs/6.x/docs/guides/recommended-libraries.md index 7be319cedf..568effba4e 100644 --- a/docs/versioned_docs/version-6.x/guides/06-recommended-libraries.md +++ b/docs/6.x/docs/guides/recommended-libraries.md @@ -24,13 +24,14 @@ An implementation of the [bottom sheet behaviour](https://material.io/design/com [gorhom/react-native-bottom-sheet](https://github.com/gorhom/react-native-bottom-sheet) An implementation of the [bottom sheet behaviour](https://material.io/design/components/sheets-bottom.html), maintained by [@Gorhom](https://twitter.com/Gorhom). - ## Date Picker + [web-ridge/react-native-paper-dates](https://github.com/web-ridge/react-native-paper-dates) Material Design themed [date picker](https://material.io/components/date-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) - + [react-native-community/react-native-datetimepicker](https://github.com/react-native-community/react-native-datetimepicker) ## Time Picker + [web-ridge/react-native-paper-dates](https://github.com/web-ridge/react-native-paper-dates) Material Design themed [time picker](https://material.io/components/time-pickers), maintained by [@RichardLindhout](https://twitter.com/RichardLindhout) diff --git a/docs/versioned_docs/version-5.x/guides/11-ripple-effect.md b/docs/6.x/docs/guides/ripple-effect.md similarity index 99% rename from docs/versioned_docs/version-5.x/guides/11-ripple-effect.md rename to docs/6.x/docs/guides/ripple-effect.md index 31845060d7..e6f6e503ef 100644 --- a/docs/versioned_docs/version-5.x/guides/11-ripple-effect.md +++ b/docs/6.x/docs/guides/ripple-effect.md @@ -40,6 +40,3 @@ import { Provider as PaperProvider } from 'react-native-paper'; // ... ``` - - - diff --git a/docs/versioned_docs/version-6.x/guides/10-rtl.md b/docs/6.x/docs/guides/rtl.md similarity index 98% rename from docs/versioned_docs/version-6.x/guides/10-rtl.md rename to docs/6.x/docs/guides/rtl.md index 796ec3595c..7a3bb1bb51 100644 --- a/docs/versioned_docs/version-6.x/guides/10-rtl.md +++ b/docs/6.x/docs/guides/rtl.md @@ -38,7 +38,7 @@ Supported values: ```js import * as React from 'react'; import { PaperProvider } from 'react-native-paper'; -import App from './src/App'; +import App from './App'; export default function Main() { return ( diff --git a/docs/versioned_docs/version-6.x/guides/08-theming-with-react-navigation.md b/docs/6.x/docs/guides/theming-with-react-navigation.md similarity index 96% rename from docs/versioned_docs/version-6.x/guides/08-theming-with-react-navigation.md rename to docs/6.x/docs/guides/theming-with-react-navigation.md index aa4f5386ee..06be61e5c8 100644 --- a/docs/versioned_docs/version-6.x/guides/08-theming-with-react-navigation.md +++ b/docs/6.x/docs/guides/theming-with-react-navigation.md @@ -33,10 +33,7 @@ const { LightTheme, DarkTheme } = adaptNavigationTheme({ Library exports also Material Design 3 themes in both modes: ```js -import { - MD3LightTheme, - MD3DarkTheme, -} from 'react-native-paper'; +import { MD3LightTheme, MD3DarkTheme } from 'react-native-paper'; ``` ## Combining theme objects @@ -49,12 +46,7 @@ For React Native Paper theme to work, we need to use `PaperProvider` also at the import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { TouchableOpacity } from 'react-native'; -import { - Card, - Text, - List, - PaperProvider, -} from 'react-native-paper'; +import { Card, Text, List, PaperProvider } from 'react-native-paper'; const Stack = createNativeStackNavigator(); @@ -100,8 +92,6 @@ export default function App() { } ``` - - Our goal here is to combine those two themes, so that we could control the theme for the entire application from a single place. To make things easier we can use [deepmerge](https://www.npmjs.com/package/deepmerge) package. We can install it with: @@ -275,11 +265,7 @@ const Header = ({ scene }) => { }} > - + ); }; @@ -287,7 +273,7 @@ const Header = ({ scene }) => { And now you can switch between light and dark theme! -![paperGuide1](../../../static/screenshots/themingWithReactNavigationDarkLightSwitch.gif) +![paperGuide1](/react-native-paper/screenshots/themingWithReactNavigationDarkLightSwitch.gif) Thanks to the linking of themes that we did earlier, switching themes can be controlled with only one piece of state. diff --git a/docs/versioned_docs/version-6.x/guides/02-theming.mdx b/docs/6.x/docs/guides/theming.mdx similarity index 98% rename from docs/versioned_docs/version-6.x/guides/02-theming.mdx rename to docs/6.x/docs/guides/theming.mdx index e35a3f1068..261fe128be 100644 --- a/docs/versioned_docs/version-6.x/guides/02-theming.mdx +++ b/docs/6.x/docs/guides/theming.mdx @@ -2,7 +2,7 @@ title: Theming --- -import DynamicColorTheme from '@site/src/components/DynamicColorTheme.tsx'; +import DynamicColorTheme from '@docs/components/DynamicColorTheme.tsx'; # Theming @@ -71,14 +71,12 @@ A theme usually contains the following properties: - `colors` (`object`): various colors used throughout different elements. > The primary key color is used to derive roles for key components across the UI, such as the FAB, prominent buttons, active states, as well as the tint of elevated surfaces. - - `primary` - `onPrimary` - `primaryContainer` - `onPrimaryContainer` > The secondary key color is used for less prominent components in the UI such as filter chips, while expanding the opportunity for color expression. - - `secondary` - `onSecondary` - `secondaryContainer` @@ -87,34 +85,29 @@ A theme usually contains the following properties: > The tertiary key color is used to derive the roles of contrasting accents that can be used to balance primary and secondary colors or bring heightened attention to an element. > The tertiary color role is left for teams to use at their discretion and is intended to support broader color expression in products. - - `tertiary` - `onTertiary` - `tertiaryContainer` - `onTertiaryContainer` > The neutral key color is used to derive the roles of surface and background, as well as high emphasis text and icons. - - `background` - `onBackground` - `surface` - `onSurface` > The neutral variant key color is used to derive medium emphasis text and icons, surface variants, and component outlines. - - `surfaceVariant` - `onSurfaceVariant` - `outline` > In addition to the accent and neutral key color, the color system includes a semantic color role for error - - `error` - `onError` - `errorContainer` - `onErrorContainer` > Surfaces at elevation levels 0-5 are tinted via color overlays based on the primary color, such as app bars or menus. The addition of a grade from 0-5 introduces tonal variation to the surface baseline. - - `elevation` (`object`) - `level0` - transparent - `level1` - 5% opacity @@ -124,12 +117,10 @@ A theme usually contains the following properties: - `level5` - 14% opacity > Colors for disabled state - - `surfaceDisabled` - `onSurfaceDisabled` > These additional role mappings exist in a scheme and are mapped to components where needed. - - `shadow` - `inverseOnSurface` - `inverseSurface` @@ -195,7 +186,7 @@ Created schemes are following the Material Design 3 color system and covering co Passed source color into the util is translated into tones to automatically provide the range of tones that map to color roles. -![customColors](../../../static/screenshots/custom-colors.png) +![customColors](/react-native-paper/screenshots/custom-colors.png) _Source: [Material You Color System](https://m3.material.io/styles/color/the-color-system/custom-colors)_ @@ -340,11 +331,10 @@ By default, TypeScript works well whenever you change the value of the built-in There are two supported ways of overriding the theme: -1. Simple built-in theme overrides - when you only customize the values and - the whole theme schema remains the same -2. Advanced theme overrides - when you add new properties or - change the built-in schema shape - +1. Simple built-in theme overrides - when you only customize the values + and the whole theme schema remains the same +2. Advanced theme overrides - when you add new properties or + change the built-in schema shape :::caution TypeScript support for `withTheme` is currently limited to Material You (MD3) theme only. diff --git a/docs/versioned_docs/version-6.x/showcase.mdx b/docs/6.x/docs/showcase.mdx similarity index 78% rename from docs/versioned_docs/version-6.x/showcase.mdx rename to docs/6.x/docs/showcase.mdx index 7024ab018d..19bce467e3 100644 --- a/docs/versioned_docs/version-6.x/showcase.mdx +++ b/docs/6.x/docs/showcase.mdx @@ -2,7 +2,7 @@ title: Showcase --- -import Showcase from '@site/src/components/Showcase.tsx'; +import Showcase from '@docs/components/Showcase.tsx'; # Who's using Paper? diff --git a/docs/6.x/index.mdx b/docs/6.x/index.mdx new file mode 100644 index 0000000000..91f12670f9 --- /dev/null +++ b/docs/6.x/index.mdx @@ -0,0 +1,38 @@ +--- +pageType: home +hero: + name: Cross-platform Material Design for React Native + tagline: Paper is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. + actions: + - text: Get started + link: /docs/guides/getting-started + theme: brand + - text: Try on Snack + link: https://snack.expo.dev/@react-native-paper/react-native-paper-example_v5 + theme: alt +features: + - title: Production-ready components + details: Buttons, inputs, lists, cards, menus, dialogs, navigation, and feedback patterns built for day-to-day product work. + icon: /react-native-paper/images/home/components.svg + span: 4 + - title: Material You theming + details: Adopt Material 3 color, typography, elevation, and state layers with a theme system designed for real apps. + icon: /react-native-paper/images/home/palette.svg + span: 4 + - title: Accessible by default + details: Common interactions, contrast, and state handling are already baked into the component surface instead of left to each screen. + icon: /react-native-paper/images/home/accessibility.svg + span: 4 + - title: Cross-platform parity + details: Keep design language and component behavior aligned across iOS, Android, and React Native Web from one codebase. + icon: /react-native-paper/images/home/devices.svg + span: 4 + - title: Dark mode support + details: Ship light and dark experiences with the same primitives, including adaptive surfaces and semantic color roles. + icon: /react-native-paper/images/home/moon.svg + span: 4 + - title: Migration-friendly docs + details: Browse stable 5.x, preview 6.x, and legacy static docs from one version selector without splitting the docs experience. + icon: /react-native-paper/images/home/versions.svg + span: 4 +--- diff --git a/docs/README.md b/docs/README.md index aaba2fa1e1..cb95b349af 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Website -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. +This website is built using [Rspress](https://rspress.dev/) with the Callstack theme preset. ### Installation @@ -14,7 +14,7 @@ $ yarn $ yarn start ``` -This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. +This command regenerates the versioned content and starts the local Rspress development server. Most changes are reflected live without having to restart the server. ### Build @@ -22,20 +22,4 @@ This command starts a local development server and opens up a browser window. Mo $ yarn build ``` -This command generates static content into the `build` directory and can be served using any static contents hosting service. - -### Deployment - -Using SSH: - -``` -$ USE_SSH=true yarn deploy -``` - -Not using SSH: - -``` -$ GIT_USER= yarn deploy -``` - -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. +This command regenerates the versioned content and outputs the production site to the `build` directory. diff --git a/docs/babel.config.js b/docs/babel.config.js deleted file mode 100644 index b36362b0fd..0000000000 --- a/docs/babel.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - presets: ['@docusaurus/core/lib/babel/preset'], - plugins: [ - ['@babel/plugin-transform-react-jsx', { runtime: 'automatic' }], - 'react-native-reanimated/plugin', - ], -}; diff --git a/docs/component-docs-plugin/generatePageMDX.js b/docs/component-docs-plugin/generatePageMDX.ts similarity index 68% rename from docs/component-docs-plugin/generatePageMDX.js rename to docs/component-docs-plugin/generatePageMDX.ts index 5d07def6ef..274f3622d8 100644 --- a/docs/component-docs-plugin/generatePageMDX.js +++ b/docs/component-docs-plugin/generatePageMDX.ts @@ -1,14 +1,32 @@ -const config = require('../docusaurus.config'); +import componentDocsConfig from '../component-docs.config.ts'; -const { baseUrl, customFields } = config; +type ComponentProp = { + description?: string; + required?: boolean; +}; -function renderBadge(annotation) { +export type ComponentDoc = { + title: string; + description: string; + data: { + props: Record; + }; +}; + +type ExtendsAttribute = { + name: string; + link: string; +}; + +const { customFields } = componentDocsConfig; + +function renderBadge(annotation: string) { const [annotType, ...annotLabel] = annotation.split(' '); return `${annotLabel.join(' ')}`; } -function generateKnownIssues(componentName) { +function generateKnownIssues(componentName: string) { const componentKnownIssues = customFields.knownIssues[componentName]; if (!componentKnownIssues) { @@ -36,7 +54,7 @@ function generateKnownIssues(componentName) { `; } -function generateMoreExamples(componentName) { +function generateMoreExamples(componentName: string) { const componentMoreExamples = customFields.moreExamples[componentName]; if (!componentMoreExamples) { @@ -64,7 +82,10 @@ function generateMoreExamples(componentName) { `; } -function generateThemeColors(componentName, themeColorsData) { +function generateThemeColors( + componentName: string, + themeColorsData: string | undefined +) { if (!themeColorsData) { return ``; } @@ -75,17 +96,21 @@ function generateThemeColors(componentName, themeColorsData) { `; } -function generateScreenshots(componentName, screenshotData) { +function generateScreenshots(screenshotData: string | undefined) { if (!screenshotData) { return ``; } return ` - + `; } -function generatePropsTable(data, link, extendsAttributes) { +function generatePropsTable( + data: Record, + link: string, + extendsAttributes: ExtendsAttribute[] +) { const ANNOTATION_OPTIONAL = '@optional'; const ANNOTATION_INTERNAL = '@internal'; @@ -142,16 +167,16 @@ function generatePropsTable(data, link, extendsAttributes) { `; } -function generateExtendsAttributes(doc) { +function generateExtendsAttributes(doc: ComponentDoc) { const ANNOTATION_EXTENDS = '@extends'; - const extendsAttributes = []; - doc?.description + const extendsAttributes: ExtendsAttribute[] = []; + doc.description .split('\n') .filter((line) => { if (line.startsWith(ANNOTATION_EXTENDS)) { const parts = line.split(' ').slice(1); - const link = parts.pop(); + const link = parts.pop() ?? ''; extendsAttributes.push({ name: parts.join(' '), link, @@ -165,7 +190,10 @@ function generateExtendsAttributes(doc) { return extendsAttributes; } -function generateExtendedExamples(usage, extendedExamplesData) { +function generateExtendedExamples( + usage: string, + extendedExamplesData: string | undefined +) { if (!extendedExamplesData) { return usage; } @@ -181,18 +209,19 @@ function generateExtendedExamples(usage, extendedExamplesData) { `; } -function generatePageMDX(doc, link) { +export default function generatePageMDX(doc: ComponentDoc, link: string) { const summaryRegex = /([\s\S]*?)## Usage/; const description = doc.description .replace(/<\/br>/g, '') .replace(/style="[a-zA-Z0-9:;.\s()\-,]*"/gi, '') - .replace(/src="screenshots/g, `src="${baseUrl}screenshots`) + .replace(/src="screenshots/g, 'src="/react-native-paper/screenshots') + .replace(/\(\.\/Portal\)/g, '(./Portal/Portal)') + .replace(/\(\.\.\/Portal\)/g, '(../Portal/Portal)') .replace(/@extends.+$/, ''); - const summary = summaryRegex.exec(description) - ? summaryRegex.exec(description)[1] - : ''; + const summaryMatch = summaryRegex.exec(description); + const summary = summaryMatch ? summaryMatch[1] : ''; const usage = description.replace(summary, ''); const themeColorsData = JSON.stringify(customFields.themeColors[doc.title]); @@ -208,15 +237,15 @@ function generatePageMDX(doc, link) { title: ${doc.title} --- -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; ${summary} -${generateScreenshots(doc.title, screenshotData)} +${generateScreenshots(screenshotData)} ${generateExtendedExamples(usage, extendedExamplesData)} @@ -229,7 +258,9 @@ ${generateThemeColors(doc.title, themeColorsData)} ${generateKnownIssues(doc.title)} `; - return mdx.slice(1); + return mdx + .slice(1) + .split('\n') + .map((line) => line.trimEnd()) + .join('\n'); } - -module.exports = generatePageMDX; diff --git a/docs/component-docs-plugin/index.js b/docs/component-docs-plugin/index.js deleted file mode 100644 index ae6f470b0e..0000000000 --- a/docs/component-docs-plugin/index.js +++ /dev/null @@ -1,92 +0,0 @@ -const { - default: parseComponentDocs, -} = require('component-docs/dist/parsers/component'); -const fs = require('fs'); -const path = require('path'); - -const generatePageMDX = require('./generatePageMDX'); - -const pluginName = 'component-docs-plugin'; - -function componentsPlugin(_, options) { - const { docsRootDir, libsRootDir, pages } = options; - - function clean() { - fs.rmSync(docsRootDir, { recursive: true, force: true }); - } - - function createCategory(label, dir = '.') { - const categoryJSON = JSON.stringify({ label }, undefined, 2); - const docsCategoryDir = path.join(docsRootDir, dir); - if (!fs.existsSync(docsCategoryDir)) { - fs.mkdirSync(docsCategoryDir); - } - fs.writeFileSync( - path.join(docsCategoryDir, `_category_.json`), - categoryJSON - ); - } - - function createPageForComponent(targetArray, source) { - const [item, subitem] = targetArray; - const target = subitem ? `${item}/${subitem}` : item; - const targetPath = target + '.mdx'; - const sourcePath = source + '.tsx'; - - // Parse component using component-docs. - const doc = parseComponentDocs(path.join(libsRootDir, sourcePath), { - root: libsRootDir, - }); - - // Create directory for the output mdx file. - fs.mkdirSync( - path.join(docsRootDir, targetPath).split('/').slice(0, -1).join('/'), - { recursive: true } - ); - - // Generate and write mdx file. - fs.writeFileSync( - path.join(docsRootDir, targetPath), - generatePageMDX(doc, source) - ); - - return doc; - } - - return { - name: pluginName, - loadContent() { - // Clean up docs directory. - clean(); - // Create root components category. - createCategory('Components', '.'); - - const docs = {}; - - for (const item in pages) { - if (typeof pages[item] === 'string') { - const doc = createPageForComponent([item], pages[item]); - docs[pages[item]] = doc; - } else { - for (const subitem in pages[item]) { - const doc = createPageForComponent( - [item, subitem], - pages[item][subitem] - ); - docs[pages[item][subitem]] = doc; - } - } - } - - return docs; - }, - contentLoaded({ content: docs, actions }) { - // Store component docs global data so it can be used in `PropsTable` component. - actions.setGlobalData({ - docs, - }); - }, - }; -} - -module.exports = componentsPlugin; diff --git a/docs/component-docs-plugin/useDocs.tsx b/docs/component-docs-plugin/useDocs.tsx index 5747fafb5c..e4e9edc636 100644 --- a/docs/component-docs-plugin/useDocs.tsx +++ b/docs/component-docs-plugin/useDocs.tsx @@ -1,11 +1,5 @@ -import { useActiveDocContext } from '@docusaurus/plugin-content-docs/client'; -// @ts-ignore -// eslint-disable-next-line import/no-unresolved -import { usePluginData } from '@docusaurus/useGlobalData'; - import componentDocs5x from '../src/data/componentDocs5x.json'; - -const pluginName = 'component-docs-plugin'; +import componentDocs6x from '../src/data/componentDocs6x.json'; export interface ComponentDocsPluginData { docs: { [key in string]: PageDoc }; @@ -42,17 +36,11 @@ export interface Prop { const versionedDocs: { [versionName: string]: ComponentDocsPluginData } = { '5.x': componentDocs5x, + '6.x': componentDocs6x, }; -function useDoc(withPath: string) { - const activeDocContext = useActiveDocContext(undefined); - const pluginData = usePluginData(pluginName) as ComponentDocsPluginData; - const versionName = activeDocContext.activeVersion?.name; - const versionedDoc = versionName - ? versionedDocs[versionName]?.docs?.[withPath] - : undefined; - - return versionedDoc ?? pluginData?.docs?.[withPath]; +function useDoc(withPath: string, versionName = '5.x') { + return versionedDocs[versionName]?.docs?.[withPath]; } export default useDoc; diff --git a/docs/component-docs.config.ts b/docs/component-docs.config.ts new file mode 100644 index 0000000000..bad25d4eec --- /dev/null +++ b/docs/component-docs.config.ts @@ -0,0 +1,169 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { extendedExamples } from './src/data/extendedExamples.ts'; +import { screenshots } from './src/data/screenshots.ts'; +import { themeColors } from './src/data/themeColors.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +export type Pages = Record>; + +type ComponentDocsConfig = { + docsRootDir: string; + libsRootDir: string; + pages: Pages; + customFields: { + moreExamples: Record>; + knownIssues: Record>; + themeColors: Record; + screenshots: Record; + extendedExamples: Record; + }; +}; + +const pages = { + ActivityIndicator: 'ActivityIndicator', + Appbar: { + Appbar: 'Appbar/Appbar', + AppbarAction: 'Appbar/AppbarAction', + AppbarBackAction: 'Appbar/AppbarBackAction', + AppbarContent: 'Appbar/AppbarContent', + AppbarHeader: 'Appbar/AppbarHeader', + }, + Avatar: { + AvatarIcon: 'Avatar/AvatarIcon', + AvatarImage: 'Avatar/AvatarImage', + AvatarText: 'Avatar/AvatarText', + }, + Badge: 'Badge', + Banner: 'Banner', + BottomNavigation: { + BottomNavigation: 'BottomNavigation/BottomNavigation', + BottomNavigationBar: 'BottomNavigation/BottomNavigationBar', + }, + Button: { + Button: 'Button/Button', + }, + Card: { + Card: 'Card/Card', + CardActions: 'Card/CardActions', + CardContent: 'Card/CardContent', + CardCover: 'Card/CardCover', + CardTitle: 'Card/CardTitle', + }, + Checkbox: { + Checkbox: 'Checkbox/Checkbox', + CheckboxItem: 'Checkbox/CheckboxItem', + }, + Chip: { + Chip: 'Chip/Chip', + }, + DataTable: { + DataTable: 'DataTable/DataTable', + DataTableCell: 'DataTable/DataTableCell', + DataTableHeader: 'DataTable/DataTableHeader', + DataTablePagination: 'DataTable/DataTablePagination', + DataTableRow: 'DataTable/DataTableRow', + DataTableTitle: 'DataTable/DataTableTitle', + }, + Dialog: { + Dialog: 'Dialog/Dialog', + DialogActions: 'Dialog/DialogActions', + DialogContent: 'Dialog/DialogContent', + DialogIcon: 'Dialog/DialogIcon', + DialogScrollArea: 'Dialog/DialogScrollArea', + DialogTitle: 'Dialog/DialogTitle', + }, + Divider: 'Divider', + Drawer: { + DrawerCollapsedItem: 'Drawer/DrawerCollapsedItem', + DrawerItem: 'Drawer/DrawerItem', + DrawerSection: 'Drawer/DrawerSection', + }, + FAB: { + FAB: 'FAB/FAB', + FABExtended: 'FAB/Extended', + FABMenu: 'FAB/Menu', + }, + IconButton: { + IconButton: 'IconButton/IconButton', + }, + Icon: 'Icon', + List: { + ListAccordion: 'List/ListAccordion', + ListAccordionGroup: 'List/ListAccordionGroup', + ListIcon: 'List/ListIcon', + ListItem: 'List/ListItem', + ListSection: 'List/ListSection', + ListSubheader: 'List/ListSubheader', + }, + Menu: { + Menu: 'Menu/Menu', + MenuItem: 'Menu/MenuItem', + }, + Modal: 'Modal', + Portal: { + Portal: 'Portal/Portal', + PortalHost: 'Portal/PortalHost', + }, + ProgressBar: 'ProgressBar', + RadioButton: { + RadioButton: 'RadioButton/RadioButton', + RadioButtonAndroid: 'RadioButton/RadioButtonAndroid', + RadioButtonGroup: 'RadioButton/RadioButtonGroup', + RadioButtonIOS: 'RadioButton/RadioButtonIOS', + RadioButtonItem: 'RadioButton/RadioButtonItem', + }, + Searchbar: 'Searchbar', + SegmentedButtons: { + SegmentedButtons: 'SegmentedButtons/SegmentedButtons', + }, + Snackbar: 'Snackbar', + Surface: 'Surface', + Switch: { + Switch: 'Switch/Switch', + }, + TextInput: { + TextInput: 'TextInput/TextInput', + TextInputIcon: 'TextInput/TextInputIcon', + }, + ToggleButton: { + ToggleButton: 'ToggleButton/ToggleButton', + ToggleButtonGroup: 'ToggleButton/ToggleButtonGroup', + ToggleButtonRow: 'ToggleButton/ToggleButtonRow', + }, + Tooltip: { + Tooltip: 'Tooltip/Tooltip', + }, + TouchableRipple: { + TouchableRipple: 'TouchableRipple/TouchableRipple', + }, + Text: { + Text: 'Typography/Text', + }, +}; + +const componentDocsConfig: ComponentDocsConfig = { + docsRootDir: path.join(__dirname, '6.x', 'docs', 'components'), + libsRootDir: path.join(__dirname, '..', 'src', 'components'), + pages, + customFields: { + moreExamples: { + Portal: { + 'Comprehensive Portal example': + 'https://snack.expo.dev/@react-native-paper/more-examples---comprehensive-portal-example', + }, + Snackbar: { + 'Snackbar rendered regardless of the parent positioning': + 'https://snack.expo.dev/@react-native-paper/more-examples---snackbar-rendered-regardless-of-the-parent-positioning', + }, + }, + knownIssues: {}, + themeColors, + screenshots, + extendedExamples, + }, +}; + +export default componentDocsConfig; diff --git a/docs/component-docs.d.ts b/docs/component-docs.d.ts new file mode 100644 index 0000000000..a270ae0ce9 --- /dev/null +++ b/docs/component-docs.d.ts @@ -0,0 +1,21 @@ +declare module 'component-docs/dist/parsers/component.js' { + type ComponentDoc = { + title: string; + description: string; + data: { + props: Record< + string, + { + description?: string; + required?: boolean; + } + >; + }; + }; + + const componentDocsParser: { + default: (filePath: string, options: { root: string }) => ComponentDoc; + }; + + export default componentDocsParser; +} diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js deleted file mode 100644 index ee54135042..0000000000 --- a/docs/docusaurus.config.js +++ /dev/null @@ -1,351 +0,0 @@ -// @ts-check -// Note: type annotations allow type checking and IDEs autocompletion - -const path = require('path'); -const darkCodeTheme = require('prism-react-renderer/themes/dracula'); -const lightCodeTheme = require('prism-react-renderer/themes/github'); - -const { extendedExamples } = require('./src/data/extendedExamples.js'); -const { screenshots } = require('./src/data/screenshots.js'); -const { themeColors } = require('./src/data/themeColors.js'); - -const { NODE_ENV, DOCUSAURUS_BASE_URL } = process.env; - -const title = 'React Native Paper'; - -const url = 'https://callstack.github.io'; - -/** - * There are 3 environments that we want to support: - * 1. Development - Docs should work on `http://localhost:3000/` - * 2. Production - Docs should work on `https://callstack.github.io/react-native-paper/ - * 3. CI - CircleCI builds and stores preview artifacts. - * Docs should work on `https://output.circle-artifacts.com/output/job/$CIRCLE_WORKFLOW_JOB_ID/artifacts/$CIRCLE_NODE_INDEX/` - * - * Both `baseUrl` and `publicUrl` work together to ensure assets and links work properly on every deployment - */ -const baseUrl = - DOCUSAURUS_BASE_URL || - (NODE_ENV === 'development' ? '/' : '/react-native-paper/'); -const publicUrl = - NODE_ENV === 'development' ? 'http://localhost:3000/' : `${url}${baseUrl}`; - -/** @type {import('@docusaurus/types').Config} */ -const config = { - title, - staticDirectories: ['public', 'static'], - tagline: 'Material Design for React Native', - url, - baseUrl, - favicon: 'images/favicon.ico', - - // GitHub pages deployment config. - // If you aren't using GitHub pages, you don't need these. - organizationName: 'callstack', // Usually your GitHub org/user name. - projectName: 'react-native-paper', // Usually your repo name. - - // Even if you don't use internalization, you can use this field to set useful - // metadata like html lang. For example, if your site is Chinese, you may want - // to replace "en" with "zh-Hans". - i18n: { - defaultLocale: 'en', - locales: ['en'], - }, - - onBrokenLinks: 'throw', - onDuplicateRoutes: 'throw', - onBrokenMarkdownLinks: 'throw', - - plugins: [ - [ - './component-docs-plugin', - { - docsRootDir: path.join( - __dirname, - 'versioned_docs', - 'version-6.x', - 'components' - ), - libsRootDir: path.join(__dirname, '..', 'src', 'components'), - pages: { - ActivityIndicator: 'ActivityIndicator', - Appbar: { - Appbar: 'Appbar/Appbar', - AppbarAction: 'Appbar/AppbarAction', - AppbarBackAction: 'Appbar/AppbarBackAction', - AppbarContent: 'Appbar/AppbarContent', - AppbarHeader: 'Appbar/AppbarHeader', - }, - Avatar: { - AvatarIcon: 'Avatar/AvatarIcon', - AvatarImage: 'Avatar/AvatarImage', - AvatarText: 'Avatar/AvatarText', - }, - Badge: 'Badge', - Banner: 'Banner', - BottomNavigation: { - BottomNavigation: 'BottomNavigation/BottomNavigation', - BottomNavigationBar: 'BottomNavigation/BottomNavigationBar', - }, - Button: { - Button: 'Button/Button', - }, - Card: { - Card: 'Card/Card', - CardActions: 'Card/CardActions', - CardContent: 'Card/CardContent', - CardCover: 'Card/CardCover', - CardTitle: 'Card/CardTitle', - }, - Checkbox: { - Checkbox: 'Checkbox/Checkbox', - CheckboxItem: 'Checkbox/CheckboxItem', - }, - Chip: { - Chip: 'Chip/Chip', - }, - DataTable: { - DataTable: 'DataTable/DataTable', - DataTableCell: 'DataTable/DataTableCell', - DataTableHeader: 'DataTable/DataTableHeader', - DataTablePagination: 'DataTable/DataTablePagination', - DataTableRow: 'DataTable/DataTableRow', - DataTableTitle: 'DataTable/DataTableTitle', - }, - Dialog: { - Dialog: 'Dialog/Dialog', - DialogActions: 'Dialog/DialogActions', - DialogContent: 'Dialog/DialogContent', - DialogIcon: 'Dialog/DialogIcon', - DialogScrollArea: 'Dialog/DialogScrollArea', - DialogTitle: 'Dialog/DialogTitle', - }, - Divider: 'Divider', - Drawer: { - DrawerCollapsedItem: 'Drawer/DrawerCollapsedItem', - DrawerItem: 'Drawer/DrawerItem', - DrawerSection: 'Drawer/DrawerSection', - }, - FAB: { - FAB: 'FAB/FAB', - FABExtended: 'FAB/Extended', - FABMenu: 'FAB/Menu', - }, - IconButton: { - IconButton: 'IconButton/IconButton', - }, - Icon: 'Icon', - List: { - ListAccordion: 'List/ListAccordion', - ListAccordionGroup: 'List/ListAccordionGroup', - ListIcon: 'List/ListIcon', - ListItem: 'List/ListItem', - ListSection: 'List/ListSection', - ListSubheader: 'List/ListSubheader', - }, - Menu: { - Menu: 'Menu/Menu', - MenuItem: 'Menu/MenuItem', - }, - Modal: 'Modal', - Portal: { - Portal: 'Portal/Portal', - PortalHost: 'Portal/PortalHost', - }, - ProgressBar: 'ProgressBar', - RadioButton: { - RadioButton: 'RadioButton/RadioButton', - RadioButtonAndroid: 'RadioButton/RadioButtonAndroid', - RadioButtonGroup: 'RadioButton/RadioButtonGroup', - RadioButtonIOS: 'RadioButton/RadioButtonIOS', - RadioButtonItem: 'RadioButton/RadioButtonItem', - }, - Searchbar: 'Searchbar', - SegmentedButtons: { - SegmentedButtons: 'SegmentedButtons/SegmentedButtons', - }, - Snackbar: 'Snackbar', - Surface: 'Surface', - Switch: { - Switch: 'Switch/Switch', - }, - TextInput: { - TextInput: 'TextInput/TextInput', - TextInputIcon: 'TextInput/TextInputIcon', - }, - ToggleButton: { - ToggleButton: 'ToggleButton/ToggleButton', - ToggleButtonGroup: 'ToggleButton/ToggleButtonGroup', - ToggleButtonRow: 'ToggleButton/ToggleButtonRow', - }, - Tooltip: { - Tooltip: 'Tooltip/Tooltip', - }, - TouchableRipple: { - TouchableRipple: 'TouchableRipple/TouchableRipple', - }, - Text: { - Text: 'Typography/Text', - }, - }, - }, - ], - require('./plugins/docusaurus-react-native-plugin'), - ], - - presets: [ - [ - 'classic', - /** @type {import('@docusaurus/preset-classic').Options} */ - ({ - docs: { - sidebarPath: require.resolve('./sidebars.js'), - includeCurrentVersion: false, - lastVersion: '5.x', - versions: { - '6.x': { - label: '6.x', - banner: 'unreleased', - }, - '5.x': { - label: '5.x', - banner: 'none', - }, - }, - editUrl: (params) => { - const urlToMain = - 'https://github.com/callstack/react-native-paper/tree/main'; - - if (params.docPath.includes('guides')) { - return `${urlToMain}/docs/${params.versionDocsDirPath}/${params.docPath}`; - } - - const customUrls = { - TextInput: 'src/components/TextInput/TextInput.tsx', - TextInputIcon: 'src/components/TextInput/TextInputIcon.tsx', - - Text: 'src/components/Typography/Text.tsx', - showcase: 'docs/src/components/Showcase.tsx', - }; - - const customUrlComponent = - params.docPath.match(/\/?([^/]+)\.mdx$/)[1]; - - if (customUrls[customUrlComponent]) { - return `${urlToMain}/${customUrls[customUrlComponent]}`; - } - - return `${urlToMain}/src/${params.docPath.replace('mdx', 'tsx')}`; - }, - - remarkPlugins: [ - [require('@docusaurus/remark-plugin-npm2yarn'), { sync: true }], - ], - }, - theme: { - customCss: require.resolve('./src/css/custom.css'), - }, - }), - ], - ], - - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - ({ - navbar: { - logo: { - alt: 'React Native Paper', - src: 'images/sidebar-logo.svg', - }, - items: [ - { - type: 'doc', - docId: 'guides/getting-started', - position: 'left', - label: 'Guides', - }, - { - type: 'doc', - docId: 'components/ActivityIndicator', - position: 'left', - label: 'Components', - }, - { - type: 'doc', - docId: 'showcase', - position: 'left', - label: 'Showcase', - }, - { - type: 'docsVersionDropdown', - position: 'right', - dropdownItemsAfter: [ - { - label: '4.x', - href: `${publicUrl}4.0/`, - }, - { - label: '3.x', - href: `${publicUrl}3.0/`, - }, - { - label: '2.x', - href: `${publicUrl}2.0/`, - }, - { - label: '1.x', - href: `${publicUrl}1.0/`, - }, - ], - }, - { - href: 'https://discord.gg/zwR2Cdh', - className: 'nav-link nav-discord-link', - 'aria-label': 'Discord', - position: 'right', - }, - { - href: 'https://twitter.com/rn_paper', - className: 'nav-link nav-twitter-link', - 'aria-label': 'Twitter', - position: 'right', - }, - { - href: 'https://github.com/callstack/react-native-paper', - className: 'nav-link nav-github-link', - 'aria-label': 'GitHub', - position: 'right', - }, - ], - title, - }, - prism: { - theme: lightCodeTheme, - darkTheme: darkCodeTheme, - }, - algolia: { - appId: 'U16QMJNNW7', - apiKey: 'c32fdc9aa2bbfaa3d44481f5cbdb3c04', - indexName: 'crawler_React Native Paper Docs', - }, - }), - - customFields: { - moreExamples: { - Portal: { - 'Comprehensive Portal example': - 'https://snack.expo.dev/@react-native-paper/more-examples---comprehensive-portal-example', - }, - Snackbar: { - 'Snackbar rendered regardless of the parent positioning': - 'https://snack.expo.dev/@react-native-paper/more-examples---snackbar-rendered-regardless-of-the-parent-positioning', - }, - }, - knownIssues: {}, - themeColors, - screenshots, - extendedExamples, - }, -}; - -module.exports = config; diff --git a/docs/package.json b/docs/package.json index 89c447287c..af0bf70b9c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -2,51 +2,39 @@ "name": "docs", "version": "0.0.0", "private": true, + "type": "module", "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", - "clear": "docusaurus clear", - "serve": "docusaurus serve", - "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc", - "prepare": "patch-package" + "dev": "yarn generate && rspress dev", + "start": "yarn dev", + "build": "yarn generate && rspress build", + "preview": "rspress preview", + "typecheck": "tsc --noEmit", + "generate": "node scripts/generate-rspress-content.ts && node scripts/generate-current-component-docs.ts" }, "dependencies": { "@callstack/react-theme-provider": "^3.0.9", - "@docusaurus/core": "^2.3.6", - "@docusaurus/preset-classic": "^2.3.6", - "@docusaurus/remark-plugin-npm2yarn": "^2.3.6", - "@docusaurus/theme-common": "^2.3.6", - "@easyops-cn/docusaurus-search-local": "^0.33.4", + "@callstack/rspress-preset": "0.6.6", + "@callstack/rspress-theme": "0.6.6", "@material/material-color-utilities": "0.2.4", - "@mdx-js/react": "^1.6.22", "@react-native-vector-icons/material-design-icons": "^12.0.0", + "@rspress/core": "2.0.14", "camelcase": "^7.0.1", - "clsx": "^1.2.1", "color": "^4.2.3", "marked": "^4.1.1", - "prism-react-renderer": "^1.3.5", - "process": "^0.11.10", - "react": "17.0.2", + "react": "19.2.6", "react-color": "^2.19.3", - "react-dom": "17.0.2", - "react-native-safe-area-context": "^4.5.0", - "react-native-web": "^0.18.12", - "use-latest-callback": "^0.1.7" + "react-dom": "19.2.6", + "react-native-safe-area-context": "5.7.0", + "react-native-web": "^0.21.2", + "use-latest-callback": "^0.2.3" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^2.3.6", - "@tsconfig/docusaurus": "^1.0.6", "@types/marked": "^4.0.7", + "@types/react": "^19.2.7", "@types/react-color": "^3.0.6", + "@types/react-dom": "^19.2.3", "component-docs": "^0.24.0", - "patch-package": "^6.5.0", - "typescript": "^4.8.4", - "webpack": "^5.73.0" + "typescript": "5.8.3" }, "browserslist": { "production": [ @@ -61,10 +49,6 @@ ] }, "engines": { - "node": ">=18" - }, - "resolutions": { - "@types/react": "18.0.8", - "@types/react-dom": "18.0.8" + "node": ">=20" } } diff --git a/docs/plugins/docusaurus-react-native-plugin.js b/docs/plugins/docusaurus-react-native-plugin.js deleted file mode 100644 index 229325015a..0000000000 --- a/docs/plugins/docusaurus-react-native-plugin.js +++ /dev/null @@ -1,30 +0,0 @@ -const path = require('path'); -const webpack = require('webpack'); - -module.exports = function () { - return { - name: 'docusaurus-react-native-plugin', - configureWebpack() { - return { - mergeStrategy: { 'resolve.extensions': 'prepend' }, - resolve: { - alias: { - react: path.resolve('node_modules/react'), - 'react-native$': 'react-native-web', - 'react-native-paper': path.resolve('../src'), - '@react-native-vector-icons/get-image': false, - }, - extensions: ['.web.js'], - }, - plugins: [ - new webpack.ProvidePlugin({ - process: 'process/browser.js', - }), - new webpack.DefinePlugin({ - __DEV__: JSON.stringify(process.env.NODE_ENV !== 'production'), - }), - ], - }; - }, - }; -}; diff --git a/docs/static/.nojekyll b/docs/public/.nojekyll similarity index 100% rename from docs/static/.nojekyll rename to docs/public/.nojekyll diff --git a/docs/static/gallery/appbar-dark.png b/docs/public/gallery/appbar-dark.png similarity index 100% rename from docs/static/gallery/appbar-dark.png rename to docs/public/gallery/appbar-dark.png diff --git a/docs/static/gallery/appbar.png b/docs/public/gallery/appbar.png similarity index 100% rename from docs/static/gallery/appbar.png rename to docs/public/gallery/appbar.png diff --git a/docs/static/gallery/bottom-navigation-dark.png b/docs/public/gallery/bottom-navigation-dark.png similarity index 100% rename from docs/static/gallery/bottom-navigation-dark.png rename to docs/public/gallery/bottom-navigation-dark.png diff --git a/docs/static/gallery/bottom-navigation.png b/docs/public/gallery/bottom-navigation.png similarity index 100% rename from docs/static/gallery/bottom-navigation.png rename to docs/public/gallery/bottom-navigation.png diff --git a/docs/static/gallery/button-dark.png b/docs/public/gallery/button-dark.png similarity index 100% rename from docs/static/gallery/button-dark.png rename to docs/public/gallery/button-dark.png diff --git a/docs/static/gallery/button.png b/docs/public/gallery/button.png similarity index 100% rename from docs/static/gallery/button.png rename to docs/public/gallery/button.png diff --git a/docs/static/gallery/card-dark.png b/docs/public/gallery/card-dark.png similarity index 100% rename from docs/static/gallery/card-dark.png rename to docs/public/gallery/card-dark.png diff --git a/docs/static/gallery/card.png b/docs/public/gallery/card.png similarity index 100% rename from docs/static/gallery/card.png rename to docs/public/gallery/card.png diff --git a/docs/static/gallery/chip-dark.png b/docs/public/gallery/chip-dark.png similarity index 100% rename from docs/static/gallery/chip-dark.png rename to docs/public/gallery/chip-dark.png diff --git a/docs/static/gallery/chip.png b/docs/public/gallery/chip.png similarity index 100% rename from docs/static/gallery/chip.png rename to docs/public/gallery/chip.png diff --git a/docs/static/gallery/fab-dark.png b/docs/public/gallery/fab-dark.png similarity index 100% rename from docs/static/gallery/fab-dark.png rename to docs/public/gallery/fab-dark.png diff --git a/docs/static/gallery/fab.png b/docs/public/gallery/fab.png similarity index 100% rename from docs/static/gallery/fab.png rename to docs/public/gallery/fab.png diff --git a/docs/static/gallery/input-dark.png b/docs/public/gallery/input-dark.png similarity index 100% rename from docs/static/gallery/input-dark.png rename to docs/public/gallery/input-dark.png diff --git a/docs/static/gallery/input.png b/docs/public/gallery/input.png similarity index 100% rename from docs/static/gallery/input.png rename to docs/public/gallery/input.png diff --git a/docs/static/gallery/list-accordion-dark.png b/docs/public/gallery/list-accordion-dark.png similarity index 100% rename from docs/static/gallery/list-accordion-dark.png rename to docs/public/gallery/list-accordion-dark.png diff --git a/docs/static/gallery/list-accordion.png b/docs/public/gallery/list-accordion.png similarity index 100% rename from docs/static/gallery/list-accordion.png rename to docs/public/gallery/list-accordion.png diff --git a/docs/static/gallery/list-dark.png b/docs/public/gallery/list-dark.png similarity index 100% rename from docs/static/gallery/list-dark.png rename to docs/public/gallery/list-dark.png diff --git a/docs/static/gallery/list.png b/docs/public/gallery/list.png similarity index 100% rename from docs/static/gallery/list.png rename to docs/public/gallery/list.png diff --git a/docs/static/gallery/searchbar-dark.png b/docs/public/gallery/searchbar-dark.png similarity index 100% rename from docs/static/gallery/searchbar-dark.png rename to docs/public/gallery/searchbar-dark.png diff --git a/docs/static/gallery/searchbar.png b/docs/public/gallery/searchbar.png similarity index 100% rename from docs/static/gallery/searchbar.png rename to docs/public/gallery/searchbar.png diff --git a/docs/static/gallery/snackbar-dark.png b/docs/public/gallery/snackbar-dark.png similarity index 100% rename from docs/static/gallery/snackbar-dark.png rename to docs/public/gallery/snackbar-dark.png diff --git a/docs/static/gallery/snackbar.png b/docs/public/gallery/snackbar.png similarity index 100% rename from docs/static/gallery/snackbar.png rename to docs/public/gallery/snackbar.png diff --git a/docs/static/gallery/toggle-button.png b/docs/public/gallery/toggle-button.png similarity index 100% rename from docs/static/gallery/toggle-button.png rename to docs/public/gallery/toggle-button.png diff --git a/docs/static/gallery/typography-dark.png b/docs/public/gallery/typography-dark.png similarity index 100% rename from docs/static/gallery/typography-dark.png rename to docs/public/gallery/typography-dark.png diff --git a/docs/static/gallery/typography.png b/docs/public/gallery/typography.png similarity index 100% rename from docs/static/gallery/typography.png rename to docs/public/gallery/typography.png diff --git a/docs/static/images/expo-qr.png b/docs/public/images/expo-qr.png similarity index 100% rename from docs/static/images/expo-qr.png rename to docs/public/images/expo-qr.png diff --git a/docs/static/images/favicon.ico b/docs/public/images/favicon.ico similarity index 100% rename from docs/static/images/favicon.ico rename to docs/public/images/favicon.ico diff --git a/docs/public/images/home/accessibility.svg b/docs/public/images/home/accessibility.svg new file mode 100644 index 0000000000..4b4aabefe5 --- /dev/null +++ b/docs/public/images/home/accessibility.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/public/images/home/components.svg b/docs/public/images/home/components.svg new file mode 100644 index 0000000000..69bd14f161 --- /dev/null +++ b/docs/public/images/home/components.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/public/images/home/devices.svg b/docs/public/images/home/devices.svg new file mode 100644 index 0000000000..6660a426b1 --- /dev/null +++ b/docs/public/images/home/devices.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/public/images/home/moon.svg b/docs/public/images/home/moon.svg new file mode 100644 index 0000000000..88edf2d9d9 --- /dev/null +++ b/docs/public/images/home/moon.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/public/images/home/palette.svg b/docs/public/images/home/palette.svg new file mode 100644 index 0000000000..7cd25bc873 --- /dev/null +++ b/docs/public/images/home/palette.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/public/images/home/versions.svg b/docs/public/images/home/versions.svg new file mode 100644 index 0000000000..76c32cfc9d --- /dev/null +++ b/docs/public/images/home/versions.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/static/images/paper-logo.svg b/docs/public/images/paper-logo.svg similarity index 100% rename from docs/static/images/paper-logo.svg rename to docs/public/images/paper-logo.svg diff --git a/docs/static/images/sidebar-logo.svg b/docs/public/images/sidebar-logo.svg similarity index 100% rename from docs/static/images/sidebar-logo.svg rename to docs/public/images/sidebar-logo.svg diff --git a/docs/static/migration/color-palette.png b/docs/public/migration/color-palette.png similarity index 100% rename from docs/static/migration/color-palette.png rename to docs/public/migration/color-palette.png diff --git a/docs/static/screenshots/activity-indicator.gif b/docs/public/screenshots/activity-indicator.gif similarity index 100% rename from docs/static/screenshots/activity-indicator.gif rename to docs/public/screenshots/activity-indicator.gif diff --git a/docs/static/screenshots/animated-fab.gif b/docs/public/screenshots/animated-fab.gif similarity index 100% rename from docs/static/screenshots/animated-fab.gif rename to docs/public/screenshots/animated-fab.gif diff --git a/docs/static/screenshots/appbar-action-android.png b/docs/public/screenshots/appbar-action-android.png similarity index 100% rename from docs/static/screenshots/appbar-action-android.png rename to docs/public/screenshots/appbar-action-android.png diff --git a/docs/static/screenshots/appbar-backaction-android.png b/docs/public/screenshots/appbar-backaction-android.png similarity index 100% rename from docs/static/screenshots/appbar-backaction-android.png rename to docs/public/screenshots/appbar-backaction-android.png diff --git a/docs/static/screenshots/appbar-center-aligned.png b/docs/public/screenshots/appbar-center-aligned.png similarity index 100% rename from docs/static/screenshots/appbar-center-aligned.png rename to docs/public/screenshots/appbar-center-aligned.png diff --git a/docs/static/screenshots/appbar-content.png b/docs/public/screenshots/appbar-content.png similarity index 100% rename from docs/static/screenshots/appbar-content.png rename to docs/public/screenshots/appbar-content.png diff --git a/docs/static/screenshots/appbar-large.png b/docs/public/screenshots/appbar-large.png similarity index 100% rename from docs/static/screenshots/appbar-large.png rename to docs/public/screenshots/appbar-large.png diff --git a/docs/static/screenshots/appbar-medium.png b/docs/public/screenshots/appbar-medium.png similarity index 100% rename from docs/static/screenshots/appbar-medium.png rename to docs/public/screenshots/appbar-medium.png diff --git a/docs/static/screenshots/appbar-small.png b/docs/public/screenshots/appbar-small.png similarity index 100% rename from docs/static/screenshots/appbar-small.png rename to docs/public/screenshots/appbar-small.png diff --git a/docs/static/screenshots/appbar.png b/docs/public/screenshots/appbar.png similarity index 100% rename from docs/static/screenshots/appbar.png rename to docs/public/screenshots/appbar.png diff --git a/docs/static/screenshots/avatar-icon.png b/docs/public/screenshots/avatar-icon.png similarity index 100% rename from docs/static/screenshots/avatar-icon.png rename to docs/public/screenshots/avatar-icon.png diff --git a/docs/static/screenshots/avatar-image.png b/docs/public/screenshots/avatar-image.png similarity index 100% rename from docs/static/screenshots/avatar-image.png rename to docs/public/screenshots/avatar-image.png diff --git a/docs/static/screenshots/avatar-text.png b/docs/public/screenshots/avatar-text.png similarity index 100% rename from docs/static/screenshots/avatar-text.png rename to docs/public/screenshots/avatar-text.png diff --git a/docs/static/screenshots/badge-1.png b/docs/public/screenshots/badge-1.png similarity index 100% rename from docs/static/screenshots/badge-1.png rename to docs/public/screenshots/badge-1.png diff --git a/docs/static/screenshots/badge-2.png b/docs/public/screenshots/badge-2.png similarity index 100% rename from docs/static/screenshots/badge-2.png rename to docs/public/screenshots/badge-2.png diff --git a/docs/static/screenshots/badge-3.png b/docs/public/screenshots/badge-3.png similarity index 100% rename from docs/static/screenshots/badge-3.png rename to docs/public/screenshots/badge-3.png diff --git a/docs/static/screenshots/badge-example.png b/docs/public/screenshots/badge-example.png similarity index 100% rename from docs/static/screenshots/badge-example.png rename to docs/public/screenshots/badge-example.png diff --git a/docs/static/screenshots/badge-wrapper.png b/docs/public/screenshots/badge-wrapper.png similarity index 100% rename from docs/static/screenshots/badge-wrapper.png rename to docs/public/screenshots/badge-wrapper.png diff --git a/docs/static/screenshots/banner.gif b/docs/public/screenshots/banner.gif similarity index 100% rename from docs/static/screenshots/banner.gif rename to docs/public/screenshots/banner.gif diff --git a/docs/static/screenshots/bottom-navigation-shifting.gif b/docs/public/screenshots/bottom-navigation-shifting.gif similarity index 100% rename from docs/static/screenshots/bottom-navigation-shifting.gif rename to docs/public/screenshots/bottom-navigation-shifting.gif diff --git a/docs/static/screenshots/bottom-navigation-tabs.jpg b/docs/public/screenshots/bottom-navigation-tabs.jpg similarity index 100% rename from docs/static/screenshots/bottom-navigation-tabs.jpg rename to docs/public/screenshots/bottom-navigation-tabs.jpg diff --git a/docs/static/screenshots/bottom-navigation.gif b/docs/public/screenshots/bottom-navigation.gif similarity index 100% rename from docs/static/screenshots/bottom-navigation.gif rename to docs/public/screenshots/bottom-navigation.gif diff --git a/docs/static/screenshots/button-1.png b/docs/public/screenshots/button-1.png similarity index 100% rename from docs/static/screenshots/button-1.png rename to docs/public/screenshots/button-1.png diff --git a/docs/static/screenshots/button-2.png b/docs/public/screenshots/button-2.png similarity index 100% rename from docs/static/screenshots/button-2.png rename to docs/public/screenshots/button-2.png diff --git a/docs/static/screenshots/button-3.png b/docs/public/screenshots/button-3.png similarity index 100% rename from docs/static/screenshots/button-3.png rename to docs/public/screenshots/button-3.png diff --git a/docs/static/screenshots/button-4.png b/docs/public/screenshots/button-4.png similarity index 100% rename from docs/static/screenshots/button-4.png rename to docs/public/screenshots/button-4.png diff --git a/docs/static/screenshots/button-5.png b/docs/public/screenshots/button-5.png similarity index 100% rename from docs/static/screenshots/button-5.png rename to docs/public/screenshots/button-5.png diff --git a/docs/static/screenshots/card-1.png b/docs/public/screenshots/card-1.png similarity index 100% rename from docs/static/screenshots/card-1.png rename to docs/public/screenshots/card-1.png diff --git a/docs/static/screenshots/card-2.png b/docs/public/screenshots/card-2.png similarity index 100% rename from docs/static/screenshots/card-2.png rename to docs/public/screenshots/card-2.png diff --git a/docs/static/screenshots/card-3.png b/docs/public/screenshots/card-3.png similarity index 100% rename from docs/static/screenshots/card-3.png rename to docs/public/screenshots/card-3.png diff --git a/docs/static/screenshots/card-actions.png b/docs/public/screenshots/card-actions.png similarity index 100% rename from docs/static/screenshots/card-actions.png rename to docs/public/screenshots/card-actions.png diff --git a/docs/static/screenshots/card-content-example.png b/docs/public/screenshots/card-content-example.png similarity index 100% rename from docs/static/screenshots/card-content-example.png rename to docs/public/screenshots/card-content-example.png diff --git a/docs/static/screenshots/card-cover.png b/docs/public/screenshots/card-cover.png similarity index 100% rename from docs/static/screenshots/card-cover.png rename to docs/public/screenshots/card-cover.png diff --git a/docs/static/screenshots/card-title-1.png b/docs/public/screenshots/card-title-1.png similarity index 100% rename from docs/static/screenshots/card-title-1.png rename to docs/public/screenshots/card-title-1.png diff --git a/docs/static/screenshots/checkbox-disabled.android.png b/docs/public/screenshots/checkbox-disabled.android.png similarity index 100% rename from docs/static/screenshots/checkbox-disabled.android.png rename to docs/public/screenshots/checkbox-disabled.android.png diff --git a/docs/static/screenshots/checkbox-disabled.ios.png b/docs/public/screenshots/checkbox-disabled.ios.png similarity index 100% rename from docs/static/screenshots/checkbox-disabled.ios.png rename to docs/public/screenshots/checkbox-disabled.ios.png diff --git a/docs/static/screenshots/checkbox-enabled.android.png b/docs/public/screenshots/checkbox-enabled.android.png similarity index 100% rename from docs/static/screenshots/checkbox-enabled.android.png rename to docs/public/screenshots/checkbox-enabled.android.png diff --git a/docs/static/screenshots/checkbox-enabled.ios.png b/docs/public/screenshots/checkbox-enabled.ios.png similarity index 100% rename from docs/static/screenshots/checkbox-enabled.ios.png rename to docs/public/screenshots/checkbox-enabled.ios.png diff --git a/docs/static/screenshots/chip-1.png b/docs/public/screenshots/chip-1.png similarity index 100% rename from docs/static/screenshots/chip-1.png rename to docs/public/screenshots/chip-1.png diff --git a/docs/static/screenshots/chip-2.png b/docs/public/screenshots/chip-2.png similarity index 100% rename from docs/static/screenshots/chip-2.png rename to docs/public/screenshots/chip-2.png diff --git a/docs/static/screenshots/custom-colors.png b/docs/public/screenshots/custom-colors.png similarity index 100% rename from docs/static/screenshots/custom-colors.png rename to docs/public/screenshots/custom-colors.png diff --git a/docs/static/screenshots/data-table-full-width.png b/docs/public/screenshots/data-table-full-width.png similarity index 100% rename from docs/static/screenshots/data-table-full-width.png rename to docs/public/screenshots/data-table-full-width.png diff --git a/docs/static/screenshots/data-table-header.png b/docs/public/screenshots/data-table-header.png similarity index 100% rename from docs/static/screenshots/data-table-header.png rename to docs/public/screenshots/data-table-header.png diff --git a/docs/static/screenshots/data-table-pagination.png b/docs/public/screenshots/data-table-pagination.png similarity index 100% rename from docs/static/screenshots/data-table-pagination.png rename to docs/public/screenshots/data-table-pagination.png diff --git a/docs/static/screenshots/data-table-row-cell.png b/docs/public/screenshots/data-table-row-cell.png similarity index 100% rename from docs/static/screenshots/data-table-row-cell.png rename to docs/public/screenshots/data-table-row-cell.png diff --git a/docs/static/screenshots/dialog-1.png b/docs/public/screenshots/dialog-1.png similarity index 100% rename from docs/static/screenshots/dialog-1.png rename to docs/public/screenshots/dialog-1.png diff --git a/docs/static/screenshots/dialog-2.png b/docs/public/screenshots/dialog-2.png similarity index 100% rename from docs/static/screenshots/dialog-2.png rename to docs/public/screenshots/dialog-2.png diff --git a/docs/static/screenshots/dialog-actions.png b/docs/public/screenshots/dialog-actions.png similarity index 100% rename from docs/static/screenshots/dialog-actions.png rename to docs/public/screenshots/dialog-actions.png diff --git a/docs/static/screenshots/dialog-content.png b/docs/public/screenshots/dialog-content.png similarity index 100% rename from docs/static/screenshots/dialog-content.png rename to docs/public/screenshots/dialog-content.png diff --git a/docs/static/screenshots/dialog-icon.png b/docs/public/screenshots/dialog-icon.png similarity index 100% rename from docs/static/screenshots/dialog-icon.png rename to docs/public/screenshots/dialog-icon.png diff --git a/docs/static/screenshots/dialog-scroll-area.gif b/docs/public/screenshots/dialog-scroll-area.gif similarity index 100% rename from docs/static/screenshots/dialog-scroll-area.gif rename to docs/public/screenshots/dialog-scroll-area.gif diff --git a/docs/static/screenshots/dialog-title.png b/docs/public/screenshots/dialog-title.png similarity index 100% rename from docs/static/screenshots/dialog-title.png rename to docs/public/screenshots/dialog-title.png diff --git a/docs/static/screenshots/divider.png b/docs/public/screenshots/divider.png similarity index 100% rename from docs/static/screenshots/divider.png rename to docs/public/screenshots/divider.png diff --git a/docs/static/screenshots/dot-badges.png b/docs/public/screenshots/dot-badges.png similarity index 100% rename from docs/static/screenshots/dot-badges.png rename to docs/public/screenshots/dot-badges.png diff --git a/docs/static/screenshots/drawer-collapsed.png b/docs/public/screenshots/drawer-collapsed.png similarity index 100% rename from docs/static/screenshots/drawer-collapsed.png rename to docs/public/screenshots/drawer-collapsed.png diff --git a/docs/static/screenshots/drawer-item.png b/docs/public/screenshots/drawer-item.png similarity index 100% rename from docs/static/screenshots/drawer-item.png rename to docs/public/screenshots/drawer-item.png diff --git a/docs/static/screenshots/drawer-section.png b/docs/public/screenshots/drawer-section.png similarity index 100% rename from docs/static/screenshots/drawer-section.png rename to docs/public/screenshots/drawer-section.png diff --git a/docs/static/screenshots/fab-1.png b/docs/public/screenshots/fab-1.png similarity index 100% rename from docs/static/screenshots/fab-1.png rename to docs/public/screenshots/fab-1.png diff --git a/docs/static/screenshots/fab-2.png b/docs/public/screenshots/fab-2.png similarity index 100% rename from docs/static/screenshots/fab-2.png rename to docs/public/screenshots/fab-2.png diff --git a/docs/static/screenshots/fab-3.png b/docs/public/screenshots/fab-3.png similarity index 100% rename from docs/static/screenshots/fab-3.png rename to docs/public/screenshots/fab-3.png diff --git a/docs/static/screenshots/fab-4.png b/docs/public/screenshots/fab-4.png similarity index 100% rename from docs/static/screenshots/fab-4.png rename to docs/public/screenshots/fab-4.png diff --git a/docs/static/screenshots/fab-group.gif b/docs/public/screenshots/fab-group.gif similarity index 100% rename from docs/static/screenshots/fab-group.gif rename to docs/public/screenshots/fab-group.gif diff --git a/docs/static/screenshots/fab-group.png b/docs/public/screenshots/fab-group.png similarity index 100% rename from docs/static/screenshots/fab-group.png rename to docs/public/screenshots/fab-group.png diff --git a/docs/static/screenshots/helper-text.gif b/docs/public/screenshots/helper-text.gif similarity index 100% rename from docs/static/screenshots/helper-text.gif rename to docs/public/screenshots/helper-text.gif diff --git a/docs/static/screenshots/icon-button-1.png b/docs/public/screenshots/icon-button-1.png similarity index 100% rename from docs/static/screenshots/icon-button-1.png rename to docs/public/screenshots/icon-button-1.png diff --git a/docs/static/screenshots/icon-button-2.png b/docs/public/screenshots/icon-button-2.png similarity index 100% rename from docs/static/screenshots/icon-button-2.png rename to docs/public/screenshots/icon-button-2.png diff --git a/docs/static/screenshots/icon-button-3.png b/docs/public/screenshots/icon-button-3.png similarity index 100% rename from docs/static/screenshots/icon-button-3.png rename to docs/public/screenshots/icon-button-3.png diff --git a/docs/static/screenshots/icon-button-4.png b/docs/public/screenshots/icon-button-4.png similarity index 100% rename from docs/static/screenshots/icon-button-4.png rename to docs/public/screenshots/icon-button-4.png diff --git a/docs/static/screenshots/icon.png b/docs/public/screenshots/icon.png similarity index 100% rename from docs/static/screenshots/icon.png rename to docs/public/screenshots/icon.png diff --git a/docs/static/screenshots/list-accordion-1.png b/docs/public/screenshots/list-accordion-1.png similarity index 100% rename from docs/static/screenshots/list-accordion-1.png rename to docs/public/screenshots/list-accordion-1.png diff --git a/docs/static/screenshots/list-accordion-2.png b/docs/public/screenshots/list-accordion-2.png similarity index 100% rename from docs/static/screenshots/list-accordion-2.png rename to docs/public/screenshots/list-accordion-2.png diff --git a/docs/static/screenshots/list-accordion-3.png b/docs/public/screenshots/list-accordion-3.png similarity index 100% rename from docs/static/screenshots/list-accordion-3.png rename to docs/public/screenshots/list-accordion-3.png diff --git a/docs/static/screenshots/list-accordion-group.png b/docs/public/screenshots/list-accordion-group.png similarity index 100% rename from docs/static/screenshots/list-accordion-group.png rename to docs/public/screenshots/list-accordion-group.png diff --git a/docs/static/screenshots/list-icon.png b/docs/public/screenshots/list-icon.png similarity index 100% rename from docs/static/screenshots/list-icon.png rename to docs/public/screenshots/list-icon.png diff --git a/docs/static/screenshots/list-item-1.png b/docs/public/screenshots/list-item-1.png similarity index 100% rename from docs/static/screenshots/list-item-1.png rename to docs/public/screenshots/list-item-1.png diff --git a/docs/static/screenshots/list-item-2.png b/docs/public/screenshots/list-item-2.png similarity index 100% rename from docs/static/screenshots/list-item-2.png rename to docs/public/screenshots/list-item-2.png diff --git a/docs/static/screenshots/list-item-3.png b/docs/public/screenshots/list-item-3.png similarity index 100% rename from docs/static/screenshots/list-item-3.png rename to docs/public/screenshots/list-item-3.png diff --git a/docs/static/screenshots/list-section.png b/docs/public/screenshots/list-section.png similarity index 100% rename from docs/static/screenshots/list-section.png rename to docs/public/screenshots/list-section.png diff --git a/docs/static/screenshots/material-bottom-tabs.gif b/docs/public/screenshots/material-bottom-tabs.gif similarity index 100% rename from docs/static/screenshots/material-bottom-tabs.gif rename to docs/public/screenshots/material-bottom-tabs.gif diff --git a/docs/static/screenshots/menu-1.png b/docs/public/screenshots/menu-1.png similarity index 100% rename from docs/static/screenshots/menu-1.png rename to docs/public/screenshots/menu-1.png diff --git a/docs/static/screenshots/menu-2.png b/docs/public/screenshots/menu-2.png similarity index 100% rename from docs/static/screenshots/menu-2.png rename to docs/public/screenshots/menu-2.png diff --git a/docs/static/screenshots/menu-item.png b/docs/public/screenshots/menu-item.png similarity index 100% rename from docs/static/screenshots/menu-item.png rename to docs/public/screenshots/menu-item.png diff --git a/docs/static/screenshots/modal.gif b/docs/public/screenshots/modal.gif similarity index 100% rename from docs/static/screenshots/modal.gif rename to docs/public/screenshots/modal.gif diff --git a/docs/static/screenshots/progress-bar.png b/docs/public/screenshots/progress-bar.png similarity index 100% rename from docs/static/screenshots/progress-bar.png rename to docs/public/screenshots/progress-bar.png diff --git a/docs/static/screenshots/radio-button-group-android.gif b/docs/public/screenshots/radio-button-group-android.gif similarity index 100% rename from docs/static/screenshots/radio-button-group-android.gif rename to docs/public/screenshots/radio-button-group-android.gif diff --git a/docs/static/screenshots/radio-button-group-ios.gif b/docs/public/screenshots/radio-button-group-ios.gif similarity index 100% rename from docs/static/screenshots/radio-button-group-ios.gif rename to docs/public/screenshots/radio-button-group-ios.gif diff --git a/docs/static/screenshots/radio-disabled.android.png b/docs/public/screenshots/radio-disabled.android.png similarity index 100% rename from docs/static/screenshots/radio-disabled.android.png rename to docs/public/screenshots/radio-disabled.android.png diff --git a/docs/static/screenshots/radio-disabled.ios.png b/docs/public/screenshots/radio-disabled.ios.png similarity index 100% rename from docs/static/screenshots/radio-disabled.ios.png rename to docs/public/screenshots/radio-disabled.ios.png diff --git a/docs/static/screenshots/radio-enabled.android.png b/docs/public/screenshots/radio-enabled.android.png similarity index 100% rename from docs/static/screenshots/radio-enabled.android.png rename to docs/public/screenshots/radio-enabled.android.png diff --git a/docs/static/screenshots/radio-enabled.ios.png b/docs/public/screenshots/radio-enabled.ios.png similarity index 100% rename from docs/static/screenshots/radio-enabled.ios.png rename to docs/public/screenshots/radio-enabled.ios.png diff --git a/docs/static/screenshots/radio-item.ios.png b/docs/public/screenshots/radio-item.ios.png similarity index 100% rename from docs/static/screenshots/radio-item.ios.png rename to docs/public/screenshots/radio-item.ios.png diff --git a/docs/static/screenshots/react-navigation-appBar1.png b/docs/public/screenshots/react-navigation-appBar1.png similarity index 100% rename from docs/static/screenshots/react-navigation-appBar1.png rename to docs/public/screenshots/react-navigation-appBar1.png diff --git a/docs/static/screenshots/react-navigation-appBar2.gif b/docs/public/screenshots/react-navigation-appBar2.gif similarity index 100% rename from docs/static/screenshots/react-navigation-appBar2.gif rename to docs/public/screenshots/react-navigation-appBar2.gif diff --git a/docs/static/screenshots/react-navigation-appBar3.gif b/docs/public/screenshots/react-navigation-appBar3.gif similarity index 100% rename from docs/static/screenshots/react-navigation-appBar3.gif rename to docs/public/screenshots/react-navigation-appBar3.gif diff --git a/docs/static/screenshots/react-navigation-appBar4.gif b/docs/public/screenshots/react-navigation-appBar4.gif similarity index 100% rename from docs/static/screenshots/react-navigation-appBar4.gif rename to docs/public/screenshots/react-navigation-appBar4.gif diff --git a/docs/static/screenshots/searchbar-view.png b/docs/public/screenshots/searchbar-view.png similarity index 100% rename from docs/static/screenshots/searchbar-view.png rename to docs/public/screenshots/searchbar-view.png diff --git a/docs/static/screenshots/searchbar.png b/docs/public/screenshots/searchbar.png similarity index 100% rename from docs/static/screenshots/searchbar.png rename to docs/public/screenshots/searchbar.png diff --git a/docs/static/screenshots/segmented-button-multi-select.png b/docs/public/screenshots/segmented-button-multi-select.png similarity index 100% rename from docs/static/screenshots/segmented-button-multi-select.png rename to docs/public/screenshots/segmented-button-multi-select.png diff --git a/docs/static/screenshots/segmented-button-single-select.png b/docs/public/screenshots/segmented-button-single-select.png similarity index 100% rename from docs/static/screenshots/segmented-button-single-select.png rename to docs/public/screenshots/segmented-button-single-select.png diff --git a/docs/static/screenshots/segmentedbuttons.gif b/docs/public/screenshots/segmentedbuttons.gif similarity index 100% rename from docs/static/screenshots/segmentedbuttons.gif rename to docs/public/screenshots/segmentedbuttons.gif diff --git a/docs/static/screenshots/snackbar.gif b/docs/public/screenshots/snackbar.gif similarity index 100% rename from docs/static/screenshots/snackbar.gif rename to docs/public/screenshots/snackbar.gif diff --git a/docs/static/screenshots/surface-elevated-full-width.png b/docs/public/screenshots/surface-elevated-full-width.png similarity index 100% rename from docs/static/screenshots/surface-elevated-full-width.png rename to docs/public/screenshots/surface-elevated-full-width.png diff --git a/docs/static/screenshots/surface-flat-full-width.png b/docs/public/screenshots/surface-flat-full-width.png similarity index 100% rename from docs/static/screenshots/surface-flat-full-width.png rename to docs/public/screenshots/surface-flat-full-width.png diff --git a/docs/static/screenshots/switch-disabled.android.png b/docs/public/screenshots/switch-disabled.android.png similarity index 100% rename from docs/static/screenshots/switch-disabled.android.png rename to docs/public/screenshots/switch-disabled.android.png diff --git a/docs/static/screenshots/switch-disabled.ios.png b/docs/public/screenshots/switch-disabled.ios.png similarity index 100% rename from docs/static/screenshots/switch-disabled.ios.png rename to docs/public/screenshots/switch-disabled.ios.png diff --git a/docs/static/screenshots/switch-enabled.android.png b/docs/public/screenshots/switch-enabled.android.png similarity index 100% rename from docs/static/screenshots/switch-enabled.android.png rename to docs/public/screenshots/switch-enabled.android.png diff --git a/docs/static/screenshots/switch-enabled.ios.png b/docs/public/screenshots/switch-enabled.ios.png similarity index 100% rename from docs/static/screenshots/switch-enabled.ios.png rename to docs/public/screenshots/switch-enabled.ios.png diff --git a/docs/static/screenshots/text-badges.png b/docs/public/screenshots/text-badges.png similarity index 100% rename from docs/static/screenshots/text-badges.png rename to docs/public/screenshots/text-badges.png diff --git a/docs/static/screenshots/text-input-filled.png b/docs/public/screenshots/text-input-filled.png similarity index 100% rename from docs/static/screenshots/text-input-filled.png rename to docs/public/screenshots/text-input-filled.png diff --git a/docs/static/screenshots/text-input-outlined.png b/docs/public/screenshots/text-input-outlined.png similarity index 100% rename from docs/static/screenshots/text-input-outlined.png rename to docs/public/screenshots/text-input-outlined.png diff --git a/docs/static/screenshots/themingWithReactNavigationDarkLightSwitch.gif b/docs/public/screenshots/themingWithReactNavigationDarkLightSwitch.gif similarity index 100% rename from docs/static/screenshots/themingWithReactNavigationDarkLightSwitch.gif rename to docs/public/screenshots/themingWithReactNavigationDarkLightSwitch.gif diff --git a/docs/static/screenshots/toggle-button-group.gif b/docs/public/screenshots/toggle-button-group.gif similarity index 100% rename from docs/static/screenshots/toggle-button-group.gif rename to docs/public/screenshots/toggle-button-group.gif diff --git a/docs/static/screenshots/toggle-button-row.gif b/docs/public/screenshots/toggle-button-row.gif similarity index 100% rename from docs/static/screenshots/toggle-button-row.gif rename to docs/public/screenshots/toggle-button-row.gif diff --git a/docs/static/screenshots/toggle-button.png b/docs/public/screenshots/toggle-button.png similarity index 100% rename from docs/static/screenshots/toggle-button.png rename to docs/public/screenshots/toggle-button.png diff --git a/docs/static/screenshots/tooltip.png b/docs/public/screenshots/tooltip.png similarity index 100% rename from docs/static/screenshots/tooltip.png rename to docs/public/screenshots/tooltip.png diff --git a/docs/static/screenshots/tooltips.gif b/docs/public/screenshots/tooltips.gif similarity index 100% rename from docs/static/screenshots/tooltips.gif rename to docs/public/screenshots/tooltips.gif diff --git a/docs/static/screenshots/touchable-ripple.gif b/docs/public/screenshots/touchable-ripple.gif similarity index 100% rename from docs/static/screenshots/touchable-ripple.gif rename to docs/public/screenshots/touchable-ripple.gif diff --git a/docs/static/screenshots/typography.png b/docs/public/screenshots/typography.png similarity index 100% rename from docs/static/screenshots/typography.png rename to docs/public/screenshots/typography.png diff --git a/docs/static/showcase/appngo.png b/docs/public/showcase/appngo.png similarity index 100% rename from docs/static/showcase/appngo.png rename to docs/public/showcase/appngo.png diff --git a/docs/static/showcase/astrale.png b/docs/public/showcase/astrale.png similarity index 100% rename from docs/static/showcase/astrale.png rename to docs/public/showcase/astrale.png diff --git a/docs/static/showcase/bluebirding.png b/docs/public/showcase/bluebirding.png similarity index 100% rename from docs/static/showcase/bluebirding.png rename to docs/public/showcase/bluebirding.png diff --git a/docs/static/showcase/clutch.png b/docs/public/showcase/clutch.png similarity index 100% rename from docs/static/showcase/clutch.png rename to docs/public/showcase/clutch.png diff --git a/docs/static/showcase/crazythursday.jpg b/docs/public/showcase/crazythursday.jpg similarity index 100% rename from docs/static/showcase/crazythursday.jpg rename to docs/public/showcase/crazythursday.jpg diff --git a/docs/static/showcase/darkhackernews.png b/docs/public/showcase/darkhackernews.png similarity index 100% rename from docs/static/showcase/darkhackernews.png rename to docs/public/showcase/darkhackernews.png diff --git a/docs/static/showcase/groovy.png b/docs/public/showcase/groovy.png similarity index 100% rename from docs/static/showcase/groovy.png rename to docs/public/showcase/groovy.png diff --git a/docs/static/showcase/homeworkhelper.png b/docs/public/showcase/homeworkhelper.png similarity index 100% rename from docs/static/showcase/homeworkhelper.png rename to docs/public/showcase/homeworkhelper.png diff --git a/docs/static/showcase/lyracollect.png b/docs/public/showcase/lyracollect.png similarity index 100% rename from docs/static/showcase/lyracollect.png rename to docs/public/showcase/lyracollect.png diff --git a/docs/static/showcase/moonmeet.png b/docs/public/showcase/moonmeet.png similarity index 100% rename from docs/static/showcase/moonmeet.png rename to docs/public/showcase/moonmeet.png diff --git a/docs/static/showcase/pandadeals.png b/docs/public/showcase/pandadeals.png similarity index 100% rename from docs/static/showcase/pandadeals.png rename to docs/public/showcase/pandadeals.png diff --git a/docs/static/showcase/paper.png b/docs/public/showcase/paper.png similarity index 100% rename from docs/static/showcase/paper.png rename to docs/public/showcase/paper.png diff --git a/docs/static/showcase/quakemap.png b/docs/public/showcase/quakemap.png similarity index 100% rename from docs/static/showcase/quakemap.png rename to docs/public/showcase/quakemap.png diff --git a/docs/static/showcase/racecalendar.png b/docs/public/showcase/racecalendar.png similarity index 100% rename from docs/static/showcase/racecalendar.png rename to docs/public/showcase/racecalendar.png diff --git a/docs/static/showcase/rozy.png b/docs/public/showcase/rozy.png similarity index 100% rename from docs/static/showcase/rozy.png rename to docs/public/showcase/rozy.png diff --git a/docs/static/showcase/showman.png b/docs/public/showcase/showman.png similarity index 100% rename from docs/static/showcase/showman.png rename to docs/public/showcase/showman.png diff --git a/docs/static/showcase/tracksnz.png b/docs/public/showcase/tracksnz.png similarity index 100% rename from docs/static/showcase/tracksnz.png rename to docs/public/showcase/tracksnz.png diff --git a/docs/static/showcase/unicore.png b/docs/public/showcase/unicore.png similarity index 100% rename from docs/static/showcase/unicore.png rename to docs/public/showcase/unicore.png diff --git a/docs/static/showcase/vibemap.png b/docs/public/showcase/vibemap.png similarity index 100% rename from docs/static/showcase/vibemap.png rename to docs/public/showcase/vibemap.png diff --git a/docs/static/showcase/weatherforecast.png b/docs/public/showcase/weatherforecast.png similarity index 100% rename from docs/static/showcase/weatherforecast.png rename to docs/public/showcase/weatherforecast.png diff --git a/docs/static/showcase/yummeals.png b/docs/public/showcase/yummeals.png similarity index 100% rename from docs/static/showcase/yummeals.png rename to docs/public/showcase/yummeals.png diff --git a/docs/static/showcase/zoomapto.png b/docs/public/showcase/zoomapto.png similarity index 100% rename from docs/static/showcase/zoomapto.png rename to docs/public/showcase/zoomapto.png diff --git a/docs/rspress.config.ts b/docs/rspress.config.ts new file mode 100644 index 0000000000..c7c5195c42 --- /dev/null +++ b/docs/rspress.config.ts @@ -0,0 +1,172 @@ +import { withCallstackPreset } from '@callstack/rspress-preset'; +import { defineConfig } from '@rspress/core'; +import { createRequire } from 'node:module'; +import path from 'node:path'; + +const REPO_NAME = 'react-native-paper'; +const REPO_ORG = 'callstack'; + +const src = path.resolve(__dirname, 'src'); + +const require = createRequire(import.meta.url); +const resolve = (packageName: string) => + path.dirname(require.resolve(`${packageName}/package.json`)); + +const paper = path.resolve(__dirname, '..', 'src'); + +export default withCallstackPreset( + { + context: __dirname, + docs: { + title: 'React Native Paper', + description: 'Material Design for React Native', + editUrl: `https://github.com/${REPO_ORG}/${REPO_NAME}/edit/main/docs`, + rootUrl: `https://${REPO_ORG}.github.io/${REPO_NAME}`, + icon: 'public/images/favicon.ico', + logoLight: `/${REPO_NAME}/images/sidebar-logo.svg`, + logoDark: `/${REPO_NAME}/images/sidebar-logo.svg`, + rootDir: '.', + socials: { + github: `https://github.com/${REPO_ORG}/${REPO_NAME}`, + discord: 'https://discord.gg/zwR2Cdh', + x: 'https://twitter.com/rn_paper', + }, + }, + theme: { + content: { + docFooterCTAHeadline: '', + docFooterCTAButtonText: '', + homeBannerHeadline: '', + homeBannerDescription: '', + homeBannerButtonText: '', + outlineCTAHeadline: '', + outlineCTADescription: '', + outlineCTAButtonText: '', + }, + links: { + docFooterCTA: '', + homeBanner: '', + homeFooter: 'https://www.callstack.com', + outlineCTA: '', + }, + }, + vercelAnalytics: false, + }, + defineConfig({ + base: '/react-native-paper/', + outDir: 'build', + globalStyles: path.join(src, 'css', 'custom.css'), + multiVersion: { + default: '5.x', + versions: ['5.x', '6.x'], + }, + route: { + cleanUrls: true, + extensions: ['.md', '.mdx'], + include: ['5.x/**/*.{md,mdx}', '6.x/**/*.{md,mdx}'], + exclude: [ + 'src/**', + 'scripts/**', + 'public/**', + 'build/**', + 'test-results/**', + 'tests/**', + 'visual/**', + 'component-docs-plugin/**', + 'README.md', + ], + }, + search: { + versioned: true, + codeBlocks: true, + }, + i18nSource: (source) => ({ + ...source, + outlineTitle: { + ...source.outlineTitle, + en: 'Contents', + }, + searchNoResultsText: { + ...source.searchNoResultsText, + en: 'No results found, try something different than', + }, + searchSuggestedQueryText: { + ...source.searchSuggestedQueryText, + en: '', + }, + 'overview.filterNameText': { + ...source['overview.filterNameText'], + en: '', + }, + }), + builderConfig: { + source: { + exclude: [paper], + }, + resolve: { + extensions: [ + '.web.tsx', + '.web.ts', + '.web.jsx', + '.web.js', + '.tsx', + '.ts', + '.jsx', + '.js', + '.json', + ], + alias: { + '@docs': src, + react: resolve('react'), + 'react-dom': resolve('react-dom'), + 'react-native$': resolve('react-native-web'), + 'react-native-web/dist/exports/StyleSheet/compiler/createReactDOMStyle': + path.resolve( + resolve('react-native-web'), + 'dist/exports/StyleSheet/compiler/createReactDOMStyle.js' + ), + 'react-native-web/dist/exports/StyleSheet/preprocess': path.resolve( + resolve('react-native-web'), + 'dist/exports/StyleSheet/preprocess.js' + ), + 'react-native-paper': paper, + }, + }, + tools: { + rspack(config, { isDev, rspack }) { + config.module.rules = [ + ...(config.module.rules ?? []), + { + test: /\.[jt]sx?$/, + include: [paper], + use: { + loader: require.resolve('babel-loader'), + options: { + babelrc: false, + configFile: false, + presets: [require.resolve('@react-native/babel-preset')], + plugins: [require.resolve('react-native-reanimated/plugin')], + }, + }, + }, + ]; + + config.plugins.push( + new rspack.DefinePlugin({ + __DEV__: JSON.stringify(isDev), + 'process.env.JEST_WORKER_ID': 'undefined', + 'process.env.NODE_ENV': JSON.stringify( + isDev ? 'development' : 'production' + ), + }) + ); + + config.ignoreWarnings = [ + ...(config.ignoreWarnings ?? []), + /Can't resolve '@react-native-vector-icons\/get-image'/, + ]; + }, + }, + }, + }) +); diff --git a/docs/scripts/generate-current-component-docs.ts b/docs/scripts/generate-current-component-docs.ts new file mode 100644 index 0000000000..c36d2e4ed1 --- /dev/null +++ b/docs/scripts/generate-current-component-docs.ts @@ -0,0 +1,173 @@ +import componentDocsParser from 'component-docs/dist/parsers/component.js'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import type { ComponentDoc } from '../component-docs-plugin/generatePageMDX.ts'; +import generatePageMDX from '../component-docs-plugin/generatePageMDX.ts'; +import componentDocsConfig from '../component-docs.config.ts'; + +const parseComponentDocs = componentDocsParser.default; + +const rootDir = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..' +); +const repoRootDir = path.resolve(rootDir, '..'); +const outputDir = componentDocsConfig.docsRootDir; +const metadataPath = path.join(rootDir, 'src', 'data', 'componentDocs6x.json'); + +const isRecord = (value: unknown): value is Record => + typeof value === 'object' && value !== null; + +const normalizeDocs = (value: unknown, sourceDir: string): unknown => { + if (Array.isArray(value)) { + return value.map((item) => normalizeDocs(item, sourceDir)); + } + + if (!isRecord(value)) { + return value; + } + + return Object.fromEntries( + Object.entries(value).map(([key, item]) => { + if (key !== 'dependencies' || !Array.isArray(item)) { + return [key, normalizeDocs(item, sourceDir)]; + } + + return [ + key, + item.map((dependency) => { + if (typeof dependency !== 'string' || !path.isAbsolute(dependency)) { + return dependency; + } + + const realDependency = fs.existsSync(dependency) + ? fs.realpathSync(dependency) + : dependency; + const relativeDependency = path.relative(sourceDir, realDependency); + + if ( + relativeDependency.startsWith('..') || + path.isAbsolute(relativeDependency) + ) { + return dependency; + } + + return relativeDependency.split(path.sep).join('/'); + }), + ]; + }) + ); +}; + +const ensureDir = (dirPath: string) => { + fs.mkdirSync(dirPath, { recursive: true }); +}; + +const writeJson = (filePath: string, value: unknown) => { + ensureDir(path.dirname(filePath)); + fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`); +}; + +const writeDocPage = ( + targetPath: string, + sourcePath: string, + docs: Record +) => { + const doc = parseComponentDocs(sourcePath, { + root: componentDocsConfig.libsRootDir, + }); + + ensureDir(path.dirname(targetPath)); + fs.writeFileSync( + targetPath, + generatePageMDX( + doc, + path + .relative(componentDocsConfig.libsRootDir, sourcePath) + .replace(/\.tsx$/, '') + .split(path.sep) + .join('/') + ) + ); + + docs[ + path + .relative(componentDocsConfig.libsRootDir, sourcePath) + .replace(/\.tsx$/, '') + .split(path.sep) + .join('/') + ] = doc; +}; + +const writeMetaFiles = () => { + writeJson( + path.join(outputDir, '_meta.json'), + Object.entries(componentDocsConfig.pages).map(([entryName, pageValue]) => { + if (typeof pageValue === 'string') { + return entryName; + } + + return { + type: 'dir', + name: entryName, + label: entryName, + collapsible: true, + collapsed: false, + }; + }) + ); + + for (const [entryName, pageValue] of Object.entries( + componentDocsConfig.pages + )) { + if (typeof pageValue === 'string') { + continue; + } + + writeJson( + path.join(outputDir, entryName, '_meta.json'), + Object.keys(pageValue) + ); + } +}; + +const main = () => { + fs.rmSync(outputDir, { recursive: true, force: true }); + ensureDir(outputDir); + + const docs: Record = {}; + + for (const [entryName, pageValue] of Object.entries( + componentDocsConfig.pages + )) { + if (typeof pageValue === 'string') { + writeDocPage( + path.join(outputDir, `${entryName}.mdx`), + path.join(componentDocsConfig.libsRootDir, `${pageValue}.tsx`), + docs + ); + continue; + } + + const groupDir = path.join(outputDir, entryName); + + for (const [subitem, source] of Object.entries(pageValue)) { + writeDocPage( + path.join(groupDir, `${subitem}.mdx`), + path.join(componentDocsConfig.libsRootDir, `${source}.tsx`), + docs + ); + } + } + + ensureDir(path.dirname(metadataPath)); + writeJson(metadataPath, { + docs: normalizeDocs(docs, fs.realpathSync(repoRootDir)), + }); + + writeMetaFiles(); +}; + +main(); diff --git a/docs/scripts/generate-rspress-content.ts b/docs/scripts/generate-rspress-content.ts new file mode 100644 index 0000000000..1c9accd93a --- /dev/null +++ b/docs/scripts/generate-rspress-content.ts @@ -0,0 +1,274 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import componentDocsConfig from '../component-docs.config.ts'; + +type MetaEntry = + | string + | { + type: string; + name: string; + label: string; + collapsible?: boolean; + collapsed?: boolean; + }; + +const rootDir = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..' +); +const sourcePagesDir = path.join(rootDir, 'src', 'pages'); +const publicDir = path.join(rootDir, 'public'); +const basePublicDir = path.join(publicDir, 'react-native-paper'); +const docsOrder = ['5.x', '6.x']; + +const NUMERIC_PREFIX = /^\d+-/; + +const ensureDir = (dirPath: string) => { + fs.mkdirSync(dirPath, { recursive: true }); +}; + +const readFile = (filePath: string) => fs.readFileSync(filePath, 'utf8'); + +const writeJson = (filePath: string, value: unknown) => { + ensureDir(path.dirname(filePath)); + fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`); +}; + +const readStringArrayJson = (filePath: string) => { + const value: unknown = JSON.parse(readFile(filePath)); + + if ( + !Array.isArray(value) || + !value.every((item) => typeof item === 'string') + ) { + throw new Error(`${filePath} must contain a string array`); + } + + return value; +}; + +const stripExtension = (fileName: string) => + fileName.replace(/\.(md|mdx)$/, ''); + +const stripNumericPrefix = (fileName: string) => + stripExtension(fileName).replace(NUMERIC_PREFIX, ''); + +const transformIndexContent = (content: string) => + content + .replace( + '![paperLogo](../../static/images/sidebar-logo.svg)', + 'React Native Paper' + ) + .replace( + "import GetStartedButtons from '../components/GetStartedButtons';", + "import GetStartedButtons from '@docs/components/GetStartedButtons';" + ) + .replace("import {Button} from 'react-native-paper';\n", '') + .replace( + "import BannerExample from '../components/BannerExample';", + "import BannerExample from '@docs/components/BannerExample';" + ) + .replace(/src="\.\/*gallery\//g, 'src="/react-native-paper/gallery/'); + +const copyFile = ( + sourcePath: string, + targetPath: string, + transform: (content: string) => string +) => { + ensureDir(path.dirname(targetPath)); + const source = readFile(sourcePath); + fs.writeFileSync(targetPath, transform(source)); +}; + +const getVersionDocsDir = (version: string) => + path.join(rootDir, version, 'docs'); + +const createNav = (version: string) => { + const activePrefix = version === '6.x' ? '^/(?:6\\.x/)?docs' : '^/docs'; + + return [ + { + text: 'Guides', + link: '/docs/guides/getting-started', + activeMatch: `${activePrefix}/guides/`, + }, + { + text: 'Components', + link: '/docs/components/ActivityIndicator', + activeMatch: `${activePrefix}/components/`, + }, + { + text: 'Showcase', + link: '/docs/showcase', + activeMatch: `${activePrefix}/showcase$`, + }, + ]; +}; + +const getVersionComponentOrder = (version: string): MetaEntry[] => { + const fromConfig = Object.entries(componentDocsConfig.pages).map( + ([entryName, value]) => { + if (typeof value === 'string') { + return entryName; + } + + return { + type: 'dir', + name: entryName, + label: entryName, + collapsible: true, + collapsed: false, + }; + } + ); + + if (version !== '5.x') { + return fromConfig; + } + + return [ + ...fromConfig, + { + type: 'dir', + name: 'HelperText', + label: 'HelperText', + collapsible: true, + collapsed: false, + }, + ]; +}; + +const getComponentChildOrder = (version: string, dirName: string) => { + const configEntry = componentDocsConfig.pages[dirName]; + + if (version !== '5.x' && configEntry && typeof configEntry !== 'string') { + return Object.keys(configEntry); + } + + const dirPath = path.join(getVersionDocsDir(version), 'components', dirName); + + return fs + .readdirSync(dirPath, { withFileTypes: true }) + .filter((entry) => entry.isFile() && /\.(md|mdx)$/.test(entry.name)) + .map((entry) => stripExtension(entry.name)); +}; + +const createGuidesMeta = (version: string) => { + const guidesDir = path.join(getVersionDocsDir(version), 'guides'); + const metaPath = path.join(guidesDir, '_meta.json'); + const existingOrder = fs.existsSync(metaPath) + ? readStringArrayJson(metaPath) + : []; + + const currentEntries = fs + .readdirSync(guidesDir, { withFileTypes: true }) + .filter((entry) => entry.isFile() && /\.(md|mdx)$/.test(entry.name)) + .map((entry) => stripNumericPrefix(entry.name)); + + const knownEntries = existingOrder.filter((entry) => + currentEntries.includes(entry) + ); + const newEntries = currentEntries.filter( + (entry) => !existingOrder.includes(entry) + ); + + return [...knownEntries, ...newEntries]; +}; + +const createComponentMeta = (version: string) => { + return getVersionComponentOrder(version); +}; + +const writeMetaFiles = (version: string, targetVersionDir: string) => { + const docsDir = path.join(targetVersionDir, 'docs'); + const componentsDir = path.join(docsDir, 'components'); + + writeJson(path.join(docsDir, '_meta.json'), [ + { + type: 'dir-section-header', + name: 'guides', + label: 'Guides', + }, + { + type: 'dir-section-header', + name: 'components', + label: 'Components', + }, + { + type: 'file', + name: 'showcase', + label: 'Showcase', + }, + ]); + + writeJson( + path.join(docsDir, 'guides', '_meta.json'), + createGuidesMeta(version) + ); + writeJson( + path.join(componentsDir, '_meta.json'), + createComponentMeta(version) + ); + + for (const entry of fs.readdirSync(componentsDir, { withFileTypes: true })) { + if (!entry.isDirectory()) { + continue; + } + + writeJson( + path.join(componentsDir, entry.name, '_meta.json'), + getComponentChildOrder(version, entry.name) + ); + } +}; + +const writeIndexPage = (targetVersionDir: string) => { + copyFile( + path.join(sourcePagesDir, 'index.mdx'), + path.join(targetVersionDir, 'index.mdx'), + transformIndexContent + ); +}; + +const copyBasePublicAssets = () => { + fs.rmSync(basePublicDir, { recursive: true, force: true }); + ensureDir(basePublicDir); + + for (const entry of fs.readdirSync(publicDir, { withFileTypes: true })) { + if (entry.name === 'react-native-paper') { + continue; + } + + fs.cpSync( + path.join(publicDir, entry.name), + path.join(basePublicDir, entry.name), + { + recursive: true, + force: true, + } + ); + } +}; + +const prepareVersionDir = (version: string) => { + const targetVersionDir = path.join(rootDir, version); + ensureDir(targetVersionDir); + + writeJson(path.join(targetVersionDir, '_nav.json'), createNav(version)); + writeIndexPage(targetVersionDir); + writeMetaFiles(version, targetVersionDir); +}; + +const main = () => { + copyBasePublicAssets(); + fs.rmSync(path.join(rootDir, 'docs'), { recursive: true, force: true }); + fs.rmSync(path.join(rootDir, 'index.mdx'), { force: true }); + + for (const version of docsOrder) { + prepareVersionDir(version); + } +}; + +main(); diff --git a/docs/sidebars.js b/docs/sidebars.js deleted file mode 100644 index 072e565759..0000000000 --- a/docs/sidebars.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Creating a sidebar enables you to: - - create an ordered group of docs - - render a sidebar for each doc of that group - - provide next/previous navigation - - The sidebars can be generated from the filesystem, or explicitly defined here. - - Create as many sidebars as you want. - */ - -// @ts-check - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebars = { - tutorialSidebar: [{ type: 'autogenerated', dirName: 'guides' }], - components: [{ type: 'autogenerated', dirName: 'components' }], -}; - -module.exports = sidebars; diff --git a/docs/src/components/Admonition.tsx b/docs/src/components/Admonition.tsx new file mode 100644 index 0000000000..099782a878 --- /dev/null +++ b/docs/src/components/Admonition.tsx @@ -0,0 +1,31 @@ +import type { ReactNode } from 'react'; + +type AdmonitionProps = { + children: ReactNode; + title?: ReactNode; + type?: 'note' | 'tip' | 'info' | 'caution' | 'warning' | 'danger'; +}; + +const typeClassName = { + caution: 'warning', + danger: 'danger', + info: 'info', + note: 'note', + tip: 'tip', + warning: 'warning', +}; + +export default function Admonition({ + children, + title, + type = 'note', +}: AdmonitionProps) { + const normalizedType = typeClassName[type] ?? 'note'; + + return ( +
    + {title ?

    {title}

    : null} +
    {children}
    +
    + ); +} diff --git a/docs/src/components/BannerExample.tsx b/docs/src/components/BannerExample.tsx index fcb3e0a8e2..56bd19f194 100644 --- a/docs/src/components/BannerExample.tsx +++ b/docs/src/components/BannerExample.tsx @@ -1,18 +1,16 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; +import type { StyleProp, ViewStyle } from 'react-native'; -//@ts-ignore -import BrowserOnly from '@docusaurus/BrowserOnly'; -//@ts-ignore -import { useColorMode } from '@docusaurus/theme-common'; +import { BrowserOnly } from '@rspress/core/runtime'; import { Avatar, Button, - FAB, DarkTheme, + FAB, LightTheme, ProgressBar, - PaperProvider, + Provider, RadioButton, Switch, Text, @@ -20,6 +18,10 @@ import { useTheme, } from 'react-native-paper'; +import { useColorMode } from './theme-common'; + +const shimmerBorderColor = 'rgba(125, 82, 96, 0.4)'; + const styles = StyleSheet.create({ container: { padding: 24, @@ -33,15 +35,33 @@ const styles = StyleSheet.create({ alignItems: 'flex-start', flexWrap: 'wrap', }, + shimmer: { + alignContent: 'center', + borderColor: shimmerBorderColor, + borderRadius: 16, + borderStyle: 'solid', + borderWidth: 1, + display: 'flex', + justifyContent: 'center', + marginBottom: 32, + marginTop: 36, + minHeight: 500, + padding: 30, + }, }); -const Stack: React.FC< - React.PropsWithChildren<{ - direction?: 'row' | 'column'; - spacing?: number; - style?: any; - }> -> = ({ direction = 'column', spacing = 0, style, children }) => { +type StackProps = React.PropsWithChildren<{ + direction?: 'row' | 'column'; + spacing?: number; + style?: StyleProp; +}>; + +const Stack = ({ + direction = 'column', + spacing = 0, + style, + children, +}: StackProps) => { return ( {React.Children.map(children, (child, index) => ( @@ -109,13 +129,13 @@ const BannerExample = () => { setText(text)} + onChangeText={(value) => setText(value)} /> setText(text)} + onChangeText={(value) => setText(value)} /> @@ -142,34 +162,16 @@ const BannerExample = () => { }; const Shimmer = () => { - /* Docusaurus won't call StyleSheet.create() server-side */ - /* eslint-disable react-native/no-inline-styles, react-native/no-color-literals */ - - return ( - - ); + return ; }; const ThemedBannerExample = () => { const isDarkTheme = useColorMode().colorMode === 'dark'; + return ( - + - + ); }; diff --git a/docs/src/components/CodeBlock.tsx b/docs/src/components/CodeBlock.tsx new file mode 100644 index 0000000000..5e58ba8768 --- /dev/null +++ b/docs/src/components/CodeBlock.tsx @@ -0,0 +1,16 @@ +import type { ReactNode } from 'react'; + +type CodeBlockProps = { + children: ReactNode; + language?: string; +}; + +export default function CodeBlock({ children, language }: CodeBlockProps) { + return ( +
    +      
    +        {children}
    +      
    +    
    + ); +} diff --git a/docs/src/components/ExtendedExample.tsx b/docs/src/components/ExtendedExample.tsx index ec6a3363b4..2773d528e3 100644 --- a/docs/src/components/ExtendedExample.tsx +++ b/docs/src/components/ExtendedExample.tsx @@ -1,9 +1,6 @@ -//@ts-ignore -import CodeBlock from '@theme/CodeBlock'; -//@ts-ignore -import TabItem from '@theme/TabItem'; -//@ts-ignore -import Tabs from '@theme/Tabs'; +import CodeBlock from './CodeBlock'; +import TabItem from './TabItem'; +import Tabs from './Tabs'; interface ExtendedExampleProps { extendedExamplesData: { diff --git a/docs/src/components/GetStartedButtons.tsx b/docs/src/components/GetStartedButtons.tsx index 76a126600f..3b5378b194 100644 --- a/docs/src/components/GetStartedButtons.tsx +++ b/docs/src/components/GetStartedButtons.tsx @@ -1,109 +1,20 @@ -import { StyleSheet, View } from 'react-native'; - -//@ts-ignore -import BrowserOnly from '@docusaurus/BrowserOnly'; -//@ts-ignore -import Link from '@docusaurus/Link'; -//@ts-ignore -import { useColorMode } from '@docusaurus/theme-common'; -import { - Button, - DarkTheme, - LightTheme, - PaperProvider, -} from 'react-native-paper'; - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - justifyContent: 'center', - paddingBottom: 16, - }, - button: { - marginRight: 16, - }, -}); - -const noTextDecoration = { - textDecoration: 'none', -}; - -// Needed for ripple effect when pressing `Button`, however navigation is handled by `Link` -const noop = () => {}; - -const GetStartedButton = () => { +export default function GetStartedButtons() { return ( - - - - - - - ); -}; - -const Shimmer = () => { - /* Docusaurus won't call StyleSheet.create() server-side */ - /* eslint-disable react-native/no-inline-styles, react-native/no-color-literals */ - - return ( - - - - - ); -}; - -const ThemedGetStarted = () => { - const isDarkTheme = useColorMode().colorMode === 'dark'; - return ( - - - - ); -}; - -export default function ClientSideGetStarted() { - return ( - }> - {() => } - + + ); } diff --git a/docs/src/components/IconsList.tsx b/docs/src/components/IconsList.tsx index 791265b5b6..76fc8bef1b 100644 --- a/docs/src/components/IconsList.tsx +++ b/docs/src/components/IconsList.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; const latestIcons: { [key in string]: number } = { - ...require('../../node_modules/@react-native-vector-icons/material-design-icons/glyphmaps/MaterialDesignIcons.json'), + ...require('@react-native-vector-icons/material-design-icons/glyphmaps/MaterialDesignIcons.json'), }; const oldIcons: { [key in string]: number } = { diff --git a/docs/src/components/Link.tsx b/docs/src/components/Link.tsx new file mode 100644 index 0000000000..92f82456d2 --- /dev/null +++ b/docs/src/components/Link.tsx @@ -0,0 +1,32 @@ +import type { AnchorHTMLAttributes, ReactNode } from 'react'; + +import { useSite } from '@rspress/core/dist/runtime/index.js'; + +type LinkProps = AnchorHTMLAttributes & { + children?: ReactNode; + to?: string; +}; + +const withBase = (href: string, base: string) => { + if (/^(https?:)?\/\//.test(href) || href.startsWith('#')) { + return href; + } + + if (!href.startsWith('/')) { + return href; + } + + const normalizedBase = base.endsWith('/') ? base.slice(0, -1) : base; + return `${normalizedBase}${href}`; +}; + +export default function Link({ children, href, to, ...rest }: LinkProps) { + const { site } = useSite(); + const targetHref = to ?? href ?? '#'; + + return ( + + {children} + + ); +} diff --git a/docs/src/components/PaperHomeShowcase.tsx b/docs/src/components/PaperHomeShowcase.tsx new file mode 100644 index 0000000000..2b9db2a775 --- /dev/null +++ b/docs/src/components/PaperHomeShowcase.tsx @@ -0,0 +1,82 @@ +import BannerExample from './BannerExample'; + +const lightImages = [ + 'button.png', + 'input.png', + 'card.png', + 'searchbar.png', + 'appbar.png', + 'snackbar.png', + 'chip.png', + 'list.png', + 'list-accordion.png', + 'typography.png', + 'bottom-navigation.png', + 'fab.png', +]; + +const darkImages = [ + 'button-dark.png', + 'input-dark.png', + 'card-dark.png', + 'searchbar-dark.png', + 'appbar-dark.png', + 'snackbar-dark.png', + 'chip-dark.png', + 'list-dark.png', + 'list-accordion-dark.png', + 'typography-dark.png', + 'bottom-navigation-dark.png', + 'fab-dark.png', +]; + +const renderGallery = (images: string[]) => + images.map((imageName) => ( + + )); + +export default function PaperHomeShowcase() { + return ( +
    +
    +
    +

    In practice

    +

    + Material components, typography, and controls +

    +
    +

    + Browse the component surface the way it appears in real product UI, + not as generic documentation chrome. +

    +
    + +
    + Or check the demo app on{' '} + + iOS + {' '} + or{' '} + + Android + + . +
    + + + +
    + {renderGallery(lightImages)} +
    + +
    + {renderGallery(darkImages)} +
    +
    + ); +} diff --git a/docs/src/components/PaperVersionSelector.tsx b/docs/src/components/PaperVersionSelector.tsx new file mode 100644 index 0000000000..1c2de87152 --- /dev/null +++ b/docs/src/components/PaperVersionSelector.tsx @@ -0,0 +1,102 @@ +import { useEffect, useMemo, useRef } from 'react'; + +import { + usePage, + useVersion, + withBase, +} from '@rspress/core/dist/runtime/index.js'; + +import { getNextRoute, getStableRoute } from '../utils/versionRoutes'; + +const legacyVersions = [ + { label: '4.x', href: '/4.0/' }, + { label: '3.x', href: '/3.0/' }, + { label: '2.x', href: '/2.0/' }, + { label: '1.x', href: '/1.0/' }, +]; + +type VersionLink = { + active: boolean; + external?: boolean; + href: string; + label: string; +}; + +export default function PaperVersionSelector() { + const { page } = usePage(); + const version = useVersion() || '5.x'; + const detailsRef = useRef(null); + + const links = useMemo(() => { + const routePath = page.routePath ?? '/'; + const stableRoute = getStableRoute(routePath); + const nextRoute = getNextRoute(routePath); + + const primaryLink = + version === '6.x' + ? { active: true, href: withBase(nextRoute), label: '6.x' } + : { active: true, href: withBase(stableRoute), label: '5.x' }; + const secondaryLink = + version === '6.x' + ? { active: false, href: withBase(stableRoute), label: '5.x' } + : { active: false, href: withBase(nextRoute), label: '6.x' }; + + return [ + primaryLink, + secondaryLink, + ...legacyVersions.map((item) => ({ + active: false, + external: true, + href: withBase(item.href), + label: item.label, + })), + ]; + }, [page.routePath, version]); + + useEffect(() => { + detailsRef.current?.removeAttribute('open'); + }, [page.routePath]); + + return ( +
    + + {version} + + + +
    + ); +} diff --git a/docs/src/components/PrereleaseNotice.tsx b/docs/src/components/PrereleaseNotice.tsx new file mode 100644 index 0000000000..5e32804c8e --- /dev/null +++ b/docs/src/components/PrereleaseNotice.tsx @@ -0,0 +1,35 @@ +import Link from './Link'; + +type PrereleaseNoticeProps = { + stableHref: string; + stableLabel: string; + version: string; +}; + +export default function PrereleaseNotice({ + stableHref, + stableLabel, + version, +}: PrereleaseNoticeProps) { + return ( + + ); +} diff --git a/docs/src/components/PropTable.tsx b/docs/src/components/PropTable.tsx index 9e073b14fe..54e52d8f3b 100644 --- a/docs/src/components/PropTable.tsx +++ b/docs/src/components/PropTable.tsx @@ -1,19 +1,19 @@ -// @ts-ignore -import useDoc from '@site/component-docs-plugin/useDocs'; +import { useVersion } from '@rspress/core/dist/runtime/index.js'; import Markdown from './Markdown'; +import useDoc from '../../component-docs-plugin/useDocs'; const typeDefinitions = { IconSource: - 'https://github.com/callstack/react-native-paper/blob/main/src/components/Icon.tsx#L16', + 'https://github.com/callstack/react-native-paper/blob/main/components/Icon.tsx#L16', ThemeProp: 'https://callstack.github.io/react-native-paper/docs/guides/theming#theme-properties', '(props: TextInputAccessoryProps) => React.ReactNode': - 'https://github.com/callstack/react-native-paper/blob/main/src/components/TextInput/TextInputIcon.tsx#L11', + 'https://github.com/callstack/react-native-paper/blob/main/components/TextInput/TextInputIcon.tsx#L11', '(props: TextInputRenderProps) => React.ReactNode': - 'https://github.com/callstack/react-native-paper/blob/main/src/components/TextInput/TextInput.tsx#L168', + 'https://github.com/callstack/react-native-paper/blob/main/components/TextInput/TextInput.tsx#L168', 'React.Ref': - 'https://github.com/callstack/react-native-paper/blob/main/src/components/TextInput/TextInput.tsx#L172', + 'https://github.com/callstack/react-native-paper/blob/main/components/TextInput/TextInput.tsx#L172', AccessibilityState: 'https://reactnative.dev/docs/accessibility#accessibilitystate', 'StyleProp': 'https://reactnative.dev/docs/view-style-props', @@ -39,7 +39,8 @@ export default function PropTable({ componentLink: string; prop: string; }) { - const doc = useDoc(componentLink); + const version = useVersion() || '5.x'; + const doc = useDoc(componentLink, version); if (!doc?.data?.props) { return null; diff --git a/docs/src/components/ScreenshotTabs.tsx b/docs/src/components/ScreenshotTabs.tsx index 9eb9241480..6a524c3d21 100644 --- a/docs/src/components/ScreenshotTabs.tsx +++ b/docs/src/components/ScreenshotTabs.tsx @@ -1,16 +1,13 @@ -import React from 'react'; import type { ReactNode } from 'react'; -//@ts-ignore -import TabItem from '@theme/TabItem'; -//@ts-ignore -import Tabs from '@theme/Tabs'; +import { withBase } from '@rspress/core/runtime'; +import TabItem from './TabItem'; +import Tabs from './Tabs'; import type { DataObject } from '../utils/themeColors'; type ScreenshotTabsProps = { screenshotData: DataObject | string; - baseUrl: string; }; const getClassName = (value: string) => @@ -18,12 +15,9 @@ const getClassName = (value: string) => ? 'gifScreenshot' : `tabScreenshot${value.includes('full-width') ? 'full-width' : ''}`; -const ScreenshotTabs: React.FC = ({ - screenshotData, - baseUrl, -}) => { +const ScreenshotTabs = ({ screenshotData }: ScreenshotTabsProps) => { const renderScreenhot = (src: string): ReactNode => ( - + ); if (typeof screenshotData === 'string') { @@ -32,7 +26,7 @@ const ScreenshotTabs: React.FC = ({ const screenshots = Object.entries(screenshotData).map(([key, value]) => ( - {renderScreenhot(value as string)} + {typeof value === 'string' ? renderScreenhot(value) : null} )); diff --git a/docs/src/components/Showcase.tsx b/docs/src/components/Showcase.tsx index 40b95be11f..56b942ac70 100644 --- a/docs/src/components/Showcase.tsx +++ b/docs/src/components/Showcase.tsx @@ -182,8 +182,8 @@ export default function Showcase() { {data.map((item) => { const tintColor = color(item.color).isLight() ? '#000000' : '#FFFFFF'; return ( -
    -
    +
    +
    {children}; +} diff --git a/docs/src/components/Tabs.tsx b/docs/src/components/Tabs.tsx new file mode 100644 index 0000000000..e8b4d3f023 --- /dev/null +++ b/docs/src/components/Tabs.tsx @@ -0,0 +1,11 @@ +import type { ReactNode } from 'react'; + +import { Tabs as RspressTabs } from '@rspress/core/dist/theme/index.js'; + +type TabsProps = { + children: ReactNode; +}; + +export default function Tabs({ children }: TabsProps) { + return {children}; +} diff --git a/docs/src/components/ThemeColorsTable.tsx b/docs/src/components/ThemeColorsTable.tsx index cf98c3e2df..d9c5cec724 100644 --- a/docs/src/components/ThemeColorsTable.tsx +++ b/docs/src/components/ThemeColorsTable.tsx @@ -1,12 +1,8 @@ import type { ReactNode } from 'react'; -//@ts-ignore -import Admonition from '@theme/Admonition'; -//@ts-ignore -import TabItem from '@theme/TabItem'; -//@ts-ignore -import Tabs from '@theme/Tabs'; - +import Admonition from './Admonition'; +import TabItem from './TabItem'; +import Tabs from './Tabs'; import { getMaxNestedLevel, getUniqueNestedKeys } from '../utils/themeColors'; import type { DataObject } from '../utils/themeColors'; @@ -24,6 +20,9 @@ const getTableCell = (keys: string[], modes: DataObject): ReactNode[] => { }); }; +const isDataObject = (value: DataObject[string]): value is DataObject => + typeof value === 'object'; + const FlatTable = ({ themeColorsData, uniqueKeys, @@ -32,10 +31,12 @@ const FlatTable = ({ uniqueKeys: string[]; }): ReactNode => { const rows = Object.keys(themeColorsData).map((mode) => { + const value = themeColorsData[mode]; + return ( {mode} - {getTableCell(uniqueKeys, themeColorsData[mode] as DataObject)} + {isDataObject(value) ? getTableCell(uniqueKeys, value) : null} ); }); @@ -66,30 +67,37 @@ const TabbedTable = ({ themeColorsData: DataObject; uniqueKeys: string[]; }): ReactNode => { - const tabTableContent = Object.keys(themeColorsData).map((key) => { - const modes = themeColorsData[key] as DataObject; - const rows = Object.keys(modes).map((mode) => { + const tabTableContent = Object.entries(themeColorsData).map( + ([key, modes]) => { + if (!isDataObject(modes)) { + return null; + } + + const rows = Object.keys(modes).map((mode) => { + const value = modes[mode]; + + return ( + + {mode} + {isDataObject(value) ? getTableCell(uniqueKeys, value) : null} + + ); + }); return ( - - {mode} - {getTableCell(uniqueKeys, modes[mode] as DataObject)} - + + + + + + {getTableHeader(uniqueKeys)} + + + {rows} +
    mode
    +
    ); - }); - return ( - - - - - - {getTableHeader(uniqueKeys)} - - - {rows} -
    mode
    -
    - ); - }); + } + ); return ( <> diff --git a/docs/src/components/VersionedPrereleaseNotice.tsx b/docs/src/components/VersionedPrereleaseNotice.tsx new file mode 100644 index 0000000000..9c36931191 --- /dev/null +++ b/docs/src/components/VersionedPrereleaseNotice.tsx @@ -0,0 +1,30 @@ +import { usePage, useVersion } from '@rspress/core/dist/runtime/index.js'; + +import PrereleaseNotice from './PrereleaseNotice'; +import { getStableRoute, hasSameStableRoute } from '../utils/versionRoutes'; + +const DOCS_PREFIX = '/6.x/docs/'; + +export default function VersionedPrereleaseNotice() { + const version = useVersion() || '5.x'; + const { page } = usePage(); + const routePath = page.routePath ?? '/'; + + if (version !== '6.x' || !routePath.startsWith(DOCS_PREFIX)) { + return null; + } + + const stableHref = getStableRoute(routePath); + const stableLabel = + hasSameStableRoute(routePath) && page.title + ? `${page.title} (5.x)` + : 'Latest stable docs (5.x)'; + + return ( + + ); +} diff --git a/docs/src/components/theme-common.ts b/docs/src/components/theme-common.ts new file mode 100644 index 0000000000..335ce8eaa1 --- /dev/null +++ b/docs/src/components/theme-common.ts @@ -0,0 +1,9 @@ +import { useDark } from '@rspress/core/dist/runtime/index.js'; + +export const useColorMode = () => { + const isDark = useDark(); + + return { + colorMode: isDark ? 'dark' : 'light', + }; +}; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index a382d6268e..03288b6bcd 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -1,32 +1,11 @@ -/** - * Any CSS included here will be global. The classic template - * bundles Infima by default. Infima is a CSS framework designed to - * work well for content-centric websites. - */ - -/* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #21005d; - --ifm-color-primary-dark: #1e0054; - --ifm-color-primary-darker: #1c004f; - --ifm-color-primary-darkest: #170041; - --ifm-color-primary-light: #240066; - --ifm-color-primary-lighter: #26006b; - --ifm-color-primary-lightest: #2b0079; - --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); -} - -/* For readability concerns, you should choose a lighter palette in dark mode. */ + --paper-nav-link-hover: var(--rp-c-bg-soft, rgba(148, 163, 184, 0.16)); + --paper-nav-link-active: var(--rp-c-bg-mute, rgba(148, 163, 184, 0.24)); +} + [data-theme='dark'] { - --ifm-color-primary: #cfbcff; - --ifm-color-primary-dark: #af90ff; - --ifm-color-primary-darker: #9f7aff; - --ifm-color-primary-darkest: #7037ff; - --ifm-color-primary-light: #efe8ff; - --ifm-color-primary-lighter: #fffeff; - --ifm-color-primary-lightest: #ffffff; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + --paper-nav-link-hover: var(--rp-c-bg-soft, rgba(255, 255, 255, 0.12)); + --paper-nav-link-active: var(--rp-c-bg-mute, rgba(255, 255, 255, 0.18)); } .nav-link { @@ -43,15 +22,15 @@ border-radius: 50%; background-color: transparent; z-index: -1; - transition: background-color var(--ifm-transition-fast); + transition: background-color 150ms ease; } .nav-link:hover::before, .nav-link:active::before, .nav-link:focus::before { - background-color: var(--ifm-color-emphasis-200); + background-color: var(--paper-nav-link-hover); } .nav-link:active::before { - background-color: var(--ifm-menu-color-background-active); + background-color: var(--paper-nav-link-active); } .nav-link::after { @@ -79,7 +58,6 @@ html[data-theme='dark'] .nav-link::after { } .screenshots img, - .tabScreenshot { max-width: 45%; } @@ -88,7 +66,7 @@ html[data-theme='dark'] .nav-link::after { max-width: 40%; } -.screenshots figure>img { +.screenshots figure > img { max-width: 100%; } @@ -102,7 +80,6 @@ html[data-theme='dark'] .nav-link::after { width: 33%; } - html[data-theme='dark'] .gallery-dark, html[data-theme='light'] .gallery-light { display: block; @@ -186,7 +163,8 @@ html[data-theme='light'] .gallery-dark { @font-face { font-family: 'MaterialDesignIcons'; - src: url('../../node_modules/@react-native-vector-icons/material-design-icons/fonts/MaterialDesignIcons.ttf') format('truetype'); + src: url('@react-native-vector-icons/material-design-icons/fonts/MaterialDesignIcons.ttf') + format('truetype'); } .badge:is(a .badge) { @@ -215,7 +193,7 @@ a .badge .badge-text { } [data-theme='dark'] .badge { - color: white + color: white; } .badge-supported { @@ -247,7 +225,10 @@ a .badge .badge-text { display: flex; flex-direction: row; flex-wrap: wrap; + gap: 32px; min-width: 0; + align-items: flex-start; + overflow: visible; } .color-picker { @@ -263,7 +244,7 @@ a .badge .badge-text { left: 0; right: 0; bottom: 0; - background: rgba(0,0,0,.2); + background: rgba(0, 0, 0, 0.2); z-index: 1; } @@ -364,7 +345,6 @@ a .badge .badge-text { flex-direction: row; gap: 20px; margin-top: 20px; - } .color-picker-themes > div { flex: 1 0 300px; @@ -390,66 +370,76 @@ a .badge .badge-text { .color-picker-preview-box { padding: 6px 6px 18px 6px; font-size: 12px; - display:block; + display: block; } -.color-picker-button:hover, .color-picker-button:focus, -.color-picker-button-additional:hover, .color-picker-button-additional:focus, -.color-picker-button-cancel:hover, .color-picker-button-cancel:focus{ +.color-picker-button:hover, +.color-picker-button:focus, +.color-picker-button-additional:hover, +.color-picker-button-additional:focus, +.color-picker-button-cancel:hover, +.color-picker-button-cancel:focus { opacity: 0.8; } - @media (max-width: 680px) { .showcase-gallery { justify-content: center; } } +.showcase-card { + flex: 0 0 auto; + overflow: visible; +} + .showcase-image-container { + position: relative; overflow: hidden; - margin: 16px; + width: 270px; + height: 480px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); + background: #ffffff; + isolation: isolate; } -@media (min-width: 420px) { - .showcase-image-container { - height: 528px; - } - - .showcase-image-container:hover .showcase-info, - .showcase-image-container:focus .showcase-info { - transform: translateY(-48px); - } +.showcase-image-container:hover, +.showcase-image-container:focus-within { + z-index: 2; + box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18); } +.showcase-image-container:hover .showcase-info, +.showcase-image-container:focus-within .showcase-info { + transform: translateY(0); +} .showcase-image { display: block; - max-height: 480px; - width: auto; -} - -@media (min-width: 420px) { - .showcase-image { - height: 480px; - width: 270px; - } + width: 100%; + height: 100%; + object-fit: cover; } .showcase-info { - height: 96px; - padding: 12px; - transform: translateY(0); - transition: 150ms; -} - -.showcase-app-name { - font-size: 16px; - margin-top: 0; + position: absolute; + left: 0; + right: 0; + bottom: 0; + min-height: 136px; + padding: 18px 24px 20px; + transform: translateY(calc(100% - 68px)); + transition: transform 180ms ease; + box-shadow: 0 -16px 32px rgba(15, 23, 42, 0.18); +} + +.showcase-info .showcase-app-name { + font-size: 17px; + line-height: 1.2; + margin-block-start: 0; + margin-block-end: 12px; margin-bottom: 12px; - - width: 246px; + width: auto; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -458,14 +448,36 @@ a .badge .badge-text { .showcase-badge-container { display: flex; flex-direction: row; + align-items: center; margin-top: 16px; - padding-left: 3px; + padding-left: 1px; } .showcase-separation { margin: 0 10px; } +@media (max-width: 420px) { + .showcase-gallery { + gap: 24px; + } + + .showcase-image-container { + width: min(270px, 100%); + height: auto; + } + + .showcase-image { + height: auto; + aspect-ratio: 9 / 16; + } + + .showcase-info { + min-height: 124px; + transform: translateY(calc(100% - 60px)); + } +} + /* Switch.tsx */ .react-switch-checkbox { @@ -484,7 +496,7 @@ a .badge .badge-text { background: grey; border-radius: 100px; position: relative; - transition: background-color .2s; + transition: background-color 0.2s; } .react-switch-label .react-switch-button { @@ -500,7 +512,7 @@ a .badge .badge-text { box-shadow: 0 0 2px 0 rgba(10, 10, 10, 0.29); } -.react-switch-checkbox:checked+.react-switch-label .react-switch-button { +.react-switch-checkbox:checked + .react-switch-label .react-switch-button { left: calc(100% - 2px); transform: translateX(-100%); } @@ -534,4 +546,743 @@ a .badge .badge-text { .extends { margin-bottom: 20px; -} \ No newline at end of file +} + +.paper-version-selector { + position: relative; + z-index: 10; + min-width: 88px; + border: 1px solid var(--rp-c-divider, rgba(148, 163, 184, 0.24)); + border-radius: 999px; + background: var(--rp-c-bg-soft, rgba(18, 18, 20, 0.96)); + color: var(--rp-c-text-1, #fff); + backdrop-filter: blur(12px); +} + +.paper-version-selector summary { + list-style: none; + cursor: pointer; + padding: 8px 14px; + font-size: 13px; + font-weight: 600; + text-align: center; + white-space: nowrap; +} + +.paper-version-selector summary::-webkit-details-marker { + display: none; +} + +.paper-version-selector[open] { + border-radius: 18px; +} + +.paper-version-selector-menu { + position: absolute; + top: calc(100% + 8px); + right: 0; + display: grid; + min-width: 88px; + padding: 8px; + gap: 4px; + border: 1px solid var(--rp-c-divider, rgba(148, 163, 184, 0.24)); + border-radius: 18px; + background: inherit; + box-shadow: 0 18px 40px rgba(15, 23, 42, 0.18); +} + +.paper-version-selector-link { + display: inline-flex; + align-items: center; + justify-content: space-between; + border-radius: 12px; + padding: 8px 10px; + color: inherit; + text-decoration: none; + gap: 6px; +} + +.paper-version-selector-link:hover, +.paper-version-selector-link.active { + background: rgba(104, 88, 196, 0.22); +} + +html:not(.dark) .paper-version-selector { + background: rgba(255, 255, 255, 0.92); + color: #1c1b1f; +} + +html:not(.dark) .paper-version-selector-menu { + box-shadow: 0 18px 40px rgba(15, 23, 42, 0.08); +} + +@media (max-width: 960px) { + .paper-version-selector { + min-width: 76px; + } + + .paper-version-selector summary { + padding-inline: 12px; + } +} + +.rp-nav__others > .rp-nav-menu__item, +.rp-nav-hamburger__md__hover-group > .rp-nav-menu__item { + display: none; +} + +.rp-doc-layout__outline:empty, +.rp-doc-layout__outline:not(:has(.rp-toc-item)) { + display: none; +} + +.rp-outline__action-row:has(.rp-llms-copy-button), +.rp-outline__action-row:has(.rp-llms-view-options__trigger) { + display: none; +} + +.rp-outline__bottom:not(:has(.rp-edit-link, .rp-scroll-to-top)) { + display: none; +} + +.rp-outline__bottom:not(:has(.rp-edit-link, .rp-scroll-to-top)) + + .rp-outline__divider, +.rp-outline__divider:has( + + .rp-outline__bottom:not(:has(.rp-edit-link, .rp-scroll-to-top)) +) { + display: none; +} + +.rp-llms-container { + display: none !important; +} + +.paper-home-showcase { + margin: 56px auto 0; + box-sizing: border-box; + width: min(72rem, calc(100% - 16px)); + padding: 0 40px 56px; +} + +.paper-home-showcase__intro { + display: grid; + gap: 16px; + margin-bottom: 18px; +} + +.paper-home-showcase__eyebrow { + margin: 0 0 12px; + color: #7c4dff; + font-size: 14px; + font-weight: 600; + letter-spacing: 0; + text-transform: uppercase; +} + +.paper-home-showcase__title { + margin: 0; + font-size: clamp(32px, 4vw, 54px); + line-height: 1.04; +} + +.paper-home-showcase__copy, +.paper-home-showcase__links { + color: var(--rp-c-text-2); + font-size: 18px; + line-height: 1.55; +} + +.paper-home-showcase__copy { + margin: 0; + max-width: 760px; +} + +.paper-home-showcase__links { + margin: 0 0 24px; +} + +.paper-home-showcase__links a { + color: inherit; +} + +.paper-get-started-buttons { + display: flex; + flex-wrap: wrap; + gap: 1rem; + justify-content: center; + padding-bottom: 1rem; +} + +.paper-get-started-button { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 2.75rem; + padding: 0 1.25rem; + border: 1px solid; + border-radius: 999px; + font-size: 0.95rem; + font-weight: 600; + text-decoration: none; + transition: + transform 120ms ease, + background-color 120ms ease; +} + +.paper-get-started-button:hover { + transform: translateY(-1px); + text-decoration: none; +} + +.paper-get-started-button--primary { + border-color: #6750a4; + background-color: #6750a4; + color: #ffffff; +} + +.paper-get-started-button--secondary { + border-color: #79747e; + background-color: transparent; + color: #49454f; +} + +[data-theme='dark'] .paper-get-started-button--primary { + border-color: #d0bcff; + background-color: #d0bcff; + color: #1d192b; +} + +[data-theme='dark'] .paper-get-started-button--secondary { + border-color: #938f99; + color: #e6e1e5; +} + +.paper-home-showcase__gallery { + display: grid; + gap: 16px; + grid-template-columns: repeat(3, minmax(0, 1fr)); + margin-top: 24px; +} + +.paper-home-showcase__gallery-image { + width: min(100%, 348px); + margin: 0 auto; + border-radius: 20px; + border: 1px solid rgba(124, 77, 255, 0.12); + background: rgba(124, 77, 255, 0.03); +} + +.paper-prerelease-notice { + display: grid; + grid-template-columns: auto 1fr; + gap: 20px; + margin: 0 0 32px; + padding: 20px 24px; + border: 1px solid rgba(245, 158, 11, 0.28); + border-left: 4px solid #f59e0b; + border-radius: 20px; + background: rgba(255, 251, 235, 0.82); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06); +} + +.paper-prerelease-notice__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border: 2px solid #f59e0b; + border-radius: 12px; + color: #f59e0b; + font-size: 24px; + font-weight: 700; + line-height: 1; +} + +.paper-prerelease-notice__content { + min-width: 0; +} + +.paper-prerelease-notice__title, +.paper-prerelease-notice__body { + margin: 0; +} + +.paper-prerelease-notice__title { + color: #1f2937; + font-size: 18px; + font-weight: 600; +} + +.paper-prerelease-notice__body { + margin-top: 10px; + color: #374151; + font-size: 16px; + line-height: 1.6; +} + +.paper-prerelease-notice__body a { + color: #7c4dff; + font-weight: 600; +} + +.paper-banner-example { + margin: 0; + padding: 28px; + border-radius: 32px; + border: 1px solid var(--paper-example-border); + background: linear-gradient( + 180deg, + var(--paper-example-panel-alt) 0%, + var(--paper-example-panel) 100% + ); + color: var(--paper-example-text); + box-shadow: var(--paper-example-shadow); +} + +.paper-banner-example__toolbar { + display: flex; + flex-wrap: wrap; + gap: 12px; + align-items: center; + margin-bottom: 18px; +} + +.paper-banner-example__button, +.paper-banner-example__avatar, +.paper-banner-example__fab, +.paper-banner-example__folder-button { + border: 1px solid transparent; + font: inherit; + letter-spacing: 0; + transition: + transform 120ms ease, + box-shadow 120ms ease, + background-color 120ms ease, + border-color 120ms ease; +} + +.paper-banner-example__button:hover, +.paper-banner-example__avatar:hover, +.paper-banner-example__fab:hover, +.paper-banner-example__folder-button:hover { + transform: translateY(-1px); +} + +.paper-banner-example__button { + display: inline-flex; + align-items: center; + gap: 12px; + min-height: 48px; + padding: 0 24px; + border-radius: 999px; + font-size: 16px; + font-weight: 600; +} + +.paper-banner-example__button-status { + width: 10px; + height: 10px; + border-radius: 999px; + background: color-mix( + in srgb, + var(--paper-example-accent) 36%, + var(--paper-example-panel-soft) + ); +} + +.paper-banner-example__button-status--busy { + background: var(--paper-example-accent); + box-shadow: 0 0 0 6px rgba(103, 80, 164, 0.12); +} + +.paper-banner-example__button--outlined, +.paper-banner-example__button--tonal, +.paper-banner-example__avatar, +.paper-banner-example__folder-button { + border-color: var(--paper-example-border); + background: var(--paper-example-panel-soft); + color: var(--paper-example-text); +} + +.paper-banner-example__button--tonal { + box-shadow: 0 12px 24px rgba(103, 80, 164, 0.08); +} + +.paper-banner-example__button--selected { + border-color: color-mix( + in srgb, + var(--paper-example-accent) 40%, + var(--paper-example-border) + ); +} + +.paper-banner-example__camera-icon { + position: relative; + width: 18px; + height: 12px; + border: 2px solid currentColor; + border-radius: 4px; +} + +.paper-banner-example__camera-icon::before { + content: ''; + position: absolute; + top: -5px; + left: 3px; + width: 8px; + height: 4px; + border-radius: 2px 2px 0 0; + background: currentColor; +} + +.paper-banner-example__button--contained, +.paper-banner-example__fab, +.paper-banner-example__avatar { + background: var(--paper-example-accent); + color: var(--paper-example-accent-text); + box-shadow: 0 10px 18px rgba(103, 80, 164, 0.22); +} + +.paper-banner-example__fab-group { + display: flex; + gap: 14px; + align-items: center; +} + +.paper-banner-example__fab, +.paper-banner-example__avatar, +.paper-banner-example__folder-button { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 24px; + font-weight: 600; +} + +.paper-banner-example__fab { + width: 64px; + height: 64px; + border-radius: 24px; +} + +.paper-banner-example__fab--small { + width: 54px; + height: 54px; + border-radius: 20px; +} + +.paper-banner-example__fab--medium { + width: 78px; + height: 78px; + border-radius: 28px; +} + +.paper-banner-example__fab--large { + width: 108px; + height: 108px; + border-radius: 34px; + font-size: 34px; +} + +.paper-banner-example__avatar { + width: 84px; + height: 84px; + border-radius: 999px; + font-size: 40px; + margin-left: auto; +} + +.paper-banner-example__hero-row { + display: flex; + margin-bottom: 16px; +} + +.paper-banner-example__folder-button { + width: 84px; + height: 84px; + border-radius: 999px; + box-shadow: 0 14px 26px rgba(103, 80, 164, 0.12); +} + +.paper-banner-example__folder-button--raised { + box-shadow: 0 18px 32px rgba(103, 80, 164, 0.2); +} + +.paper-banner-example__folder-icon { + position: relative; + width: 34px; + height: 24px; + border-radius: 6px; + background: currentColor; +} + +.paper-banner-example__folder-icon::before { + content: ''; + position: absolute; + top: -5px; + left: 3px; + width: 14px; + height: 8px; + border-radius: 4px 4px 0 0; + background: currentColor; +} + +.paper-banner-example__progress { + height: 8px; + border-radius: 999px; + overflow: hidden; + background: var(--paper-example-panel-soft); + margin-bottom: 28px; +} + +.paper-banner-example__progress-bar { + width: 64%; + height: 100%; + background: linear-gradient( + 90deg, + var(--paper-example-accent) 0%, + var(--paper-example-accent-strong) 100% + ); +} + +.paper-banner-example__headline-group { + margin-bottom: 24px; +} + +.paper-banner-example__display-large, +.paper-banner-example__display-medium { + margin: 0; + letter-spacing: 0; +} + +.paper-banner-example__display-large { + font-size: clamp(44px, 6vw, 72px); + line-height: 0.98; +} + +.paper-banner-example__display-medium { + margin-top: 14px; + font-size: clamp(28px, 4vw, 44px); + line-height: 1.08; +} + +.paper-banner-example__body { + margin: 20px 0 0; + max-width: 780px; + color: var(--paper-example-muted); + font-size: 18px; + line-height: 1.65; +} + +.paper-banner-example__switches, +.paper-banner-example__radios { + display: flex; + flex-wrap: wrap; + gap: 20px; +} + +.paper-banner-example__switches { + margin-bottom: 24px; +} + +.paper-banner-example__switch, +.paper-banner-example__radio { + display: inline-flex; + align-items: center; + gap: 10px; + font-size: 18px; +} + +.paper-banner-example__switch input, +.paper-banner-example__radio input { + position: absolute; + opacity: 0; + pointer-events: none; +} + +.paper-banner-example__switch-control { + position: relative; + display: inline-flex; + align-items: center; + width: 54px; + height: 32px; +} + +.paper-banner-example__switch-track { + width: 54px; + height: 32px; + border-radius: 999px; + border: 2px solid var(--paper-example-muted); + background: transparent; + transition: + background-color 120ms ease, + border-color 120ms ease; +} + +.paper-banner-example__switch-thumb { + position: absolute; + top: 7px; + left: 7px; + width: 14px; + height: 14px; + border-radius: 999px; + background: var(--paper-example-muted); + transition: + transform 120ms ease, + background-color 120ms ease; +} + +.paper-banner-example__switch + input:checked + + .paper-banner-example__switch-control + .paper-banner-example__switch-track { + border-color: var(--paper-example-accent); + background: color-mix(in srgb, var(--paper-example-accent) 28%, transparent); +} + +.paper-banner-example__switch + input:checked + + .paper-banner-example__switch-control + .paper-banner-example__switch-thumb { + transform: translateX(22px); + background: var(--paper-example-accent); +} + +.paper-banner-example__fields { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 24px; + margin-bottom: 24px; +} + +.paper-banner-example__field { + display: grid; + gap: 10px; + color: var(--paper-example-muted); + font-size: 15px; +} + +.paper-banner-example__field input { + min-height: 64px; + padding: 0 18px; + border: 1px solid var(--paper-example-border); + border-radius: 20px; + background: var(--paper-example-panel-soft); + color: var(--paper-example-text); + font: inherit; +} + +.paper-banner-example__field input::placeholder { + color: var(--paper-example-muted); +} + +.paper-banner-example__field-input--outlined { + background: transparent !important; + border-color: var(--paper-example-accent) !important; +} + +.paper-banner-example__radio-control { + width: 28px; + height: 28px; + border: 2px solid var(--paper-example-muted); + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.paper-banner-example__radio-dot { + width: 12px; + height: 12px; + border-radius: 999px; + background: transparent; +} + +.paper-banner-example__radio + input:checked + + .paper-banner-example__radio-control { + border-color: var(--paper-example-accent); +} + +.paper-banner-example__radio + input:checked + + .paper-banner-example__radio-control + .paper-banner-example__radio-dot { + background: var(--paper-example-accent); +} + +@media (min-width: 997px) { + .paper-home-showcase__intro { + grid-template-columns: minmax(0, 1.25fr) minmax(320px, 0.8fr); + align-items: end; + } +} + +@media (max-width: 996px) { + .paper-home-showcase { + width: min(100%, calc(100% - 16px)); + padding: 0 16px 40px; + margin-top: 44px; + } + + .paper-home-showcase__gallery { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .paper-banner-example { + padding: 22px; + border-radius: 28px; + } + + .paper-banner-example__avatar { + margin-left: 0; + } + + .paper-banner-example__fields { + grid-template-columns: 1fr; + gap: 18px; + } + + .paper-prerelease-notice { + padding: 20px 22px; + } +} + +@media (max-width: 640px) { + .paper-home-showcase__gallery { + grid-template-columns: 1fr; + } + + .paper-home-showcase__gallery-image { + width: 100%; + } + + .paper-banner-example__switch, + .paper-banner-example__radio { + font-size: 16px; + } + + .paper-banner-example__fab-group { + width: 100%; + justify-content: space-between; + } + + .paper-banner-example__fab--large { + width: 88px; + height: 88px; + } + + .paper-banner-example__avatar { + width: 72px; + height: 72px; + font-size: 30px; + } + + .paper-prerelease-notice { + grid-template-columns: 1fr; + gap: 16px; + margin-bottom: 24px; + padding: 18px; + } +} diff --git a/docs/src/data/componentDocs5x.json b/docs/src/data/componentDocs5x.json index 5aa299568c..0984e441a0 100644 --- a/docs/src/data/componentDocs5x.json +++ b/docs/src/data/componentDocs5x.json @@ -88,9 +88,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/ActivityIndicator.tsx" - ] + "dependencies": ["src/components/ActivityIndicator.tsx"] }, "Appbar/Appbar": { "filepath": "Appbar/Appbar.tsx", @@ -225,9 +223,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Appbar/Appbar.tsx" - ] + "dependencies": ["src/components/Appbar/Appbar.tsx"] }, "Appbar/AppbarAction": { "filepath": "Appbar/AppbarAction.tsx", @@ -350,9 +346,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarAction.tsx" - ], + "dependencies": ["src/components/Appbar/AppbarAction.tsx"], "group": "Appbar" }, "Appbar/AppbarBackAction": { @@ -455,9 +449,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarBackAction.tsx" - ], + "dependencies": ["src/components/Appbar/AppbarBackAction.tsx"], "group": "Appbar" }, "Appbar/AppbarContent": { @@ -633,9 +625,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarContent.tsx" - ], + "dependencies": ["src/components/Appbar/AppbarContent.tsx"], "group": "Appbar" }, "Appbar/AppbarHeader": { @@ -749,9 +739,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarHeader.tsx" - ], + "dependencies": ["src/components/Appbar/AppbarHeader.tsx"], "group": "Appbar" }, "Avatar/AvatarIcon": { @@ -813,9 +801,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarIcon.tsx" - ], + "dependencies": ["src/components/Avatar/AvatarIcon.tsx"], "group": "Avatar" }, "Avatar/AvatarImage": { @@ -927,9 +913,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarImage.tsx" - ], + "dependencies": ["src/components/Avatar/AvatarImage.tsx"], "group": "Avatar" }, "Avatar/AvatarText": { @@ -1011,9 +995,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarText.tsx" - ], + "dependencies": ["src/components/Avatar/AvatarText.tsx"], "group": "Avatar" }, "Badge": { @@ -1083,9 +1065,7 @@ "tsType": { "name": "ReactRefObject", "raw": "React.RefObject", - "elements": [ - {} - ] + "elements": [{}] }, "description": "" }, @@ -1099,9 +1079,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Badge.tsx" - ] + "dependencies": ["src/components/Badge.tsx"] }, "Banner": { "filepath": "Banner.tsx", @@ -1306,9 +1284,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Banner.tsx" - ] + "dependencies": ["src/components/Banner.tsx"] }, "BottomNavigation/BottomNavigation": { "filepath": "BottomNavigation/BottomNavigation.tsx", @@ -1322,9 +1298,7 @@ { "name": "SceneMap", "docblock": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance.", - "modifiers": [ - "static" - ], + "modifiers": ["static"], "params": [ { "name": "scenes", @@ -2318,9 +2292,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/BottomNavigation/BottomNavigation.tsx" - ] + "dependencies": ["src/components/BottomNavigation/BottomNavigation.tsx"] }, "BottomNavigation/BottomNavigationBar": { "filepath": "BottomNavigation/BottomNavigationBar.tsx", @@ -3445,9 +3417,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Button/Button.tsx" - ] + "dependencies": ["src/components/Button/Button.tsx"] }, "Card/Card": { "filepath": "Card/Card.tsx", @@ -3690,9 +3660,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Card/Card.tsx" - ] + "dependencies": ["src/components/Card/Card.tsx"] }, "Card/CardActions": { "filepath": "Card/CardActions.tsx", @@ -3736,9 +3704,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Card/CardActions.tsx" - ], + "dependencies": ["src/components/Card/CardActions.tsx"], "group": "Card" }, "Card/CardContent": { @@ -3803,9 +3769,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Card/CardContent.tsx" - ], + "dependencies": ["src/components/Card/CardContent.tsx"], "group": "Card" }, "Card/CardCover": { @@ -3856,9 +3820,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Card/CardCover.tsx" - ], + "dependencies": ["src/components/Card/CardCover.tsx"], "group": "Card" }, "Card/CardTitle": { @@ -4107,9 +4069,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Card/CardTitle.tsx" - ], + "dependencies": ["src/components/Card/CardTitle.tsx"], "group": "Card" }, "Checkbox/Checkbox": { @@ -4205,9 +4165,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Checkbox/Checkbox.tsx" - ] + "dependencies": ["src/components/Checkbox/Checkbox.tsx"] }, "Checkbox/CheckboxAndroid": { "filepath": "Checkbox/CheckboxAndroid.tsx", @@ -4302,9 +4260,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Checkbox/CheckboxAndroid.tsx" - ], + "dependencies": ["src/components/Checkbox/CheckboxAndroid.tsx"], "group": "Checkbox" }, "Checkbox/CheckboxIOS": { @@ -4393,9 +4349,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Checkbox/CheckboxIOS.tsx" - ], + "dependencies": ["src/components/Checkbox/CheckboxIOS.tsx"], "group": "Checkbox" }, "Checkbox/CheckboxItem": { @@ -4641,9 +4595,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Checkbox/CheckboxItem.tsx" - ], + "dependencies": ["src/components/Checkbox/CheckboxItem.tsx"], "group": "Checkbox" }, "Chip/Chip": { @@ -4996,9 +4948,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Chip/Chip.tsx" - ] + "dependencies": ["src/components/Chip/Chip.tsx"] }, "DataTable/DataTable": { "filepath": "DataTable/DataTable.tsx", @@ -5035,9 +4985,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/DataTable/DataTable.tsx" - ] + "dependencies": ["src/components/DataTable/DataTable.tsx"] }, "DataTable/DataTableCell": { "filepath": "DataTable/DataTableCell.tsx", @@ -5130,9 +5078,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableCell.tsx" - ], + "dependencies": ["src/components/DataTable/DataTableCell.tsx"], "group": "DataTable" }, "DataTable/DataTableHeader": { @@ -5177,9 +5123,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableHeader.tsx" - ], + "dependencies": ["src/components/DataTable/DataTableHeader.tsx"], "group": "DataTable" }, "DataTable/DataTablePagination": { @@ -5356,9 +5300,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/DataTable/DataTablePagination.tsx" - ], + "dependencies": ["src/components/DataTable/DataTablePagination.tsx"], "group": "DataTable" }, "DataTable/DataTableRow": { @@ -5433,9 +5375,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableRow.tsx" - ], + "dependencies": ["src/components/DataTable/DataTableRow.tsx"], "group": "DataTable" }, "DataTable/DataTableTitle": { @@ -5558,9 +5498,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableTitle.tsx" - ], + "dependencies": ["src/components/DataTable/DataTableTitle.tsx"], "group": "DataTable" }, "Dialog/Dialog": { @@ -5666,9 +5604,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Dialog/Dialog.tsx" - ] + "dependencies": ["src/components/Dialog/Dialog.tsx"] }, "Dialog/DialogActions": { "filepath": "Dialog/DialogActions.tsx", @@ -5712,9 +5648,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Dialog/DialogActions.tsx" - ], + "dependencies": ["src/components/Dialog/DialogActions.tsx"], "group": "Dialog" }, "Dialog/DialogContent": { @@ -5752,9 +5686,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Dialog/DialogContent.tsx" - ], + "dependencies": ["src/components/Dialog/DialogContent.tsx"], "group": "Dialog" }, "Dialog/DialogIcon": { @@ -5803,9 +5735,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Dialog/DialogIcon.tsx" - ], + "dependencies": ["src/components/Dialog/DialogIcon.tsx"], "group": "Dialog" }, "Dialog/DialogScrollArea": { @@ -5850,9 +5780,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Dialog/DialogScrollArea.tsx" - ], + "dependencies": ["src/components/Dialog/DialogScrollArea.tsx"], "group": "Dialog" }, "Dialog/DialogTitle": { @@ -5897,9 +5825,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Dialog/DialogTitle.tsx" - ], + "dependencies": ["src/components/Dialog/DialogTitle.tsx"], "group": "Dialog" }, "Divider": { @@ -5965,9 +5891,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Divider.tsx" - ] + "dependencies": ["src/components/Divider.tsx"] }, "Drawer/DrawerCollapsedItem": { "filepath": "Drawer/DrawerCollapsedItem.tsx", @@ -6108,9 +6032,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerCollapsedItem.tsx" - ], + "dependencies": ["src/components/Drawer/DrawerCollapsedItem.tsx"], "group": "Drawer" }, "Drawer/DrawerItem": { @@ -6269,9 +6191,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerItem.tsx" - ], + "dependencies": ["src/components/Drawer/DrawerItem.tsx"], "group": "Drawer" }, "Drawer/DrawerSection": { @@ -6341,9 +6261,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerSection.tsx" - ], + "dependencies": ["src/components/Drawer/DrawerSection.tsx"], "group": "Drawer" }, "FAB/FAB": { @@ -6656,9 +6574,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/FAB/FAB.tsx" - ] + "dependencies": ["src/components/FAB/FAB.tsx"] }, "FAB/AnimatedFAB": { "filepath": "FAB/AnimatedFAB.tsx", @@ -6940,9 +6856,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/FAB/AnimatedFAB.tsx" - ] + "dependencies": ["src/components/FAB/AnimatedFAB.tsx"] }, "FAB/FABGroup": { "filepath": "FAB/FABGroup.tsx", @@ -7384,9 +7298,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/FAB/FABGroup.tsx" - ], + "dependencies": ["src/components/FAB/FABGroup.tsx"], "group": "FAB" }, "HelperText/HelperText": { @@ -7500,9 +7412,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/HelperText/HelperText.tsx" - ] + "dependencies": ["src/components/HelperText/HelperText.tsx"] }, "IconButton/IconButton": { "filepath": "IconButton/IconButton.tsx", @@ -7711,9 +7621,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/IconButton/IconButton.tsx" - ] + "dependencies": ["src/components/IconButton/IconButton.tsx"] }, "Icon": { "filepath": "Icon.tsx", @@ -7771,9 +7679,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Icon.tsx" - ] + "dependencies": ["src/components/Icon.tsx"] }, "List/ListAccordion": { "filepath": "List/ListAccordion.tsx", @@ -8122,9 +8028,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/List/ListAccordion.tsx" - ], + "dependencies": ["src/components/List/ListAccordion.tsx"], "group": "List" }, "List/ListAccordionGroup": { @@ -8196,9 +8100,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/List/ListAccordionGroup.tsx" - ], + "dependencies": ["src/components/List/ListAccordionGroup.tsx"], "group": "List" }, "List/ListIcon": { @@ -8249,9 +8151,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/List/ListIcon.tsx" - ], + "dependencies": ["src/components/List/ListIcon.tsx"], "group": "List" }, "List/ListItem": { @@ -8539,9 +8439,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/List/ListItem.tsx" - ], + "dependencies": ["src/components/List/ListItem.tsx"], "group": "List" }, "List/ListSection": { @@ -8606,9 +8504,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/List/ListSection.tsx" - ], + "dependencies": ["src/components/List/ListSection.tsx"], "group": "List" }, "List/ListSubheader": { @@ -8652,9 +8548,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/List/ListSubheader.tsx" - ], + "dependencies": ["src/components/List/ListSubheader.tsx"], "group": "List" }, "Menu/Menu": { @@ -8865,9 +8759,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Menu/Menu.tsx" - ] + "dependencies": ["src/components/Menu/Menu.tsx"] }, "Menu/MenuItem": { "filepath": "Menu/MenuItem.tsx", @@ -9058,9 +8950,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Menu/MenuItem.tsx" - ], + "dependencies": ["src/components/Menu/MenuItem.tsx"], "group": "Menu" }, "Modal": { @@ -9198,9 +9088,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Modal.tsx" - ] + "dependencies": ["src/components/Modal.tsx"] }, "Portal/Portal": { "filepath": "Portal/Portal.tsx", @@ -9241,9 +9129,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Portal/Portal.tsx" - ] + "dependencies": ["src/components/Portal/Portal.tsx"] }, "Portal/PortalHost": { "filepath": "Portal/PortalHost.tsx", @@ -9267,9 +9153,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Portal/PortalHost.tsx" - ], + "dependencies": ["src/components/Portal/PortalHost.tsx"], "group": "Portal" }, "ProgressBar": { @@ -9379,9 +9263,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/ProgressBar.tsx" - ] + "dependencies": ["src/components/ProgressBar.tsx"] }, "RadioButton/RadioButton": { "filepath": "RadioButton/RadioButton.tsx", @@ -9479,9 +9361,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButton.tsx" - ] + "dependencies": ["src/components/RadioButton/RadioButton.tsx"] }, "RadioButton/RadioButtonAndroid": { "filepath": "RadioButton/RadioButtonAndroid.tsx", @@ -9579,9 +9459,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonAndroid.tsx" - ], + "dependencies": ["src/components/RadioButton/RadioButtonAndroid.tsx"], "group": "RadioButton" }, "RadioButton/RadioButtonGroup": { @@ -9635,9 +9513,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonGroup.tsx" - ], + "dependencies": ["src/components/RadioButton/RadioButtonGroup.tsx"], "group": "RadioButton" }, "RadioButton/RadioButtonIOS": { @@ -9729,9 +9605,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonIOS.tsx" - ], + "dependencies": ["src/components/RadioButton/RadioButtonIOS.tsx"], "group": "RadioButton" }, "RadioButton/RadioButtonItem": { @@ -9976,9 +9850,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonItem.tsx" - ], + "dependencies": ["src/components/RadioButton/RadioButtonItem.tsx"], "group": "RadioButton" }, "Searchbar": { @@ -10360,9 +10232,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Searchbar.tsx" - ] + "dependencies": ["src/components/Searchbar.tsx"] }, "SegmentedButtons/SegmentedButtons": { "filepath": "SegmentedButtons/SegmentedButtons.tsx", @@ -10554,9 +10424,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/SegmentedButtons/SegmentedButtons.tsx" - ] + "dependencies": ["src/components/SegmentedButtons/SegmentedButtons.tsx"] }, "Snackbar": { "filepath": "Snackbar.tsx", @@ -10808,9 +10676,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Snackbar.tsx" - ] + "dependencies": ["src/components/Snackbar.tsx"] }, "Surface": { "filepath": "Surface.tsx", @@ -10954,9 +10820,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Surface.tsx" - ] + "dependencies": ["src/components/Surface.tsx"] }, "Switch/Switch": { "filepath": "Switch/Switch.tsx", @@ -11020,9 +10884,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Switch/Switch.tsx" - ] + "dependencies": ["src/components/Switch/Switch.tsx"] }, "TextInput/TextInput": { "filepath": "TextInput/TextInput.tsx", @@ -11349,9 +11211,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/TextInput/TextInput.tsx" - ] + "dependencies": ["src/components/TextInput/TextInput.tsx"] }, "TextInput/Adornment/TextInputAffix": { "filepath": "TextInput/Adornment/TextInputAffix.tsx", @@ -11449,9 +11309,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/TextInput/Adornment/TextInputAffix.tsx" - ], + "dependencies": ["src/components/TextInput/Adornment/TextInputAffix.tsx"], "group": "TextInput" }, "TextInput/Adornment/TextInputIcon": { @@ -11551,9 +11409,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/TextInput/Adornment/TextInputIcon.tsx" - ], + "dependencies": ["src/components/TextInput/Adornment/TextInputIcon.tsx"], "group": "TextInput" }, "ToggleButton/ToggleButton": { @@ -11714,9 +11570,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButton.tsx" - ] + "dependencies": ["src/components/ToggleButton/ToggleButton.tsx"] }, "ToggleButton/ToggleButtonGroup": { "filepath": "ToggleButton/ToggleButtonGroup.tsx", @@ -11778,9 +11632,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButtonGroup.tsx" - ], + "dependencies": ["src/components/ToggleButton/ToggleButtonGroup.tsx"], "group": "ToggleButton" }, "ToggleButton/ToggleButtonRow": { @@ -11847,9 +11699,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButtonRow.tsx" - ], + "dependencies": ["src/components/ToggleButton/ToggleButtonRow.tsx"], "group": "ToggleButton" }, "Tooltip/Tooltip": { @@ -11917,9 +11767,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Tooltip/Tooltip.tsx" - ] + "dependencies": ["src/components/Tooltip/Tooltip.tsx"] }, "TouchableRipple/TouchableRipple": { "filepath": "TouchableRipple/TouchableRipple.tsx", @@ -12118,9 +11966,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/TouchableRipple/TouchableRipple.tsx" - ] + "dependencies": ["src/components/TouchableRipple/TouchableRipple.tsx"] }, "Typography/Text": { "filepath": "Typography/Text.tsx", @@ -12177,9 +12023,7 @@ } }, "type": "component", - "dependencies": [ - "src/components/Typography/Text.tsx" - ] + "dependencies": ["src/components/Typography/Text.tsx"] } } } diff --git a/docs/src/data/componentDocs6x.json b/docs/src/data/componentDocs6x.json new file mode 100644 index 0000000000..dcacb1f99f --- /dev/null +++ b/docs/src/data/componentDocs6x.json @@ -0,0 +1,12256 @@ +{ + "docs": { + "ActivityIndicator": { + "filepath": "ActivityIndicator.tsx", + "title": "ActivityIndicator", + "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "activity-indicator", + "data": { + "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "ActivityIndicator", + "methods": [], + "statics": [], + "props": { + "animating": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show the indicator or hide it.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "The color of the spinner." + }, + "size": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'large' | number", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "number" + } + ] + }, + "description": "Size of the indicator.", + "defaultValue": { + "value": "'small'", + "computed": false + } + }, + "hidesWhenStopped": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the indicator should hide when not animating.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ActivityIndicator.tsx" + ] + }, + "Appbar/Appbar": { + "filepath": "Appbar/Appbar.tsx", + "title": "Appbar", + "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", + "link": "appbar", + "data": { + "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "Appbar", + "methods": [], + "statics": [], + "props": { + "dark": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the background color is a dark color. A dark appbar will render light text and vice-versa." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Appbar`." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'medium' | 'large' | 'center-aligned'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "literal", + "value": "'center-aligned'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", + "defaultValue": { + "value": "'small'", + "computed": false + } + }, + "elevated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment." + }, + "safeAreaInsets": { + "required": false, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n bottom?: number;\n top?: number;\n left?: number;\n right?: number;\n}", + "signature": { + "properties": [ + { + "key": "bottom", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "top", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "left", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "right", + "value": { + "name": "number", + "required": false + } + } + ] + } + }, + "description": "Safe area insets for the Appbar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/Appbar.tsx" + ] + }, + "Appbar/AppbarAction": { + "filepath": "Appbar/AppbarAction.tsx", + "title": "Appbar.Action", + "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", + "link": "appbar-action", + "data": { + "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Appbar.Action", + "methods": [], + "statics": [], + "props": { + "color": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom color for action icon." + }, + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Name of the icon to show." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Optional icon size.", + "defaultValue": { + "value": "24", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "isLeading": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n\nWhether it's the leading button. Note: If `Appbar.BackAction` is present, it will be rendered before any `isLeading` icons." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarAction.tsx" + ], + "group": "Appbar" + }, + "Appbar/AppbarBackAction": { + "filepath": "Appbar/AppbarBackAction.tsx", + "title": "Appbar.BackAction", + "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "link": "appbar-back-action", + "data": { + "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Appbar.BackAction", + "methods": [], + "statics": [], + "props": { + "color": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom color for back icon." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Optional icon size." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'Back'", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarBackAction.tsx" + ], + "group": "Appbar" + }, + "Appbar/AppbarContent": { + "filepath": "Appbar/AppbarContent.tsx", + "title": "Appbar.Content", + "description": "A component used to display a title in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "appbar-content", + "data": { + "description": "A component used to display a title in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Appbar.Content", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text or component for the title." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the title, if `title` is a string." + }, + "titleRef": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "TextRef" + } + ] + }, + "description": "Reference for the title." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "If true, disable all interactions for this component." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the text." + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'medium' | 'large' | 'center-aligned'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "literal", + "value": "'center-aligned'" + } + ] + }, + "description": "@internal", + "defaultValue": { + "value": "'small'", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'appbar-content'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarContent.tsx" + ], + "group": "Appbar" + }, + "Appbar/AppbarHeader": { + "filepath": "Appbar/AppbarHeader.tsx", + "title": "Appbar.Header", + "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "appbar-header", + "data": { + "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Appbar.Header", + "methods": [], + "statics": [], + "props": { + "dark": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the background color is a dark color. A dark header will render light text and vice-versa." + }, + "statusBarHeight": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Extra padding to add at the top of header to account for translucent status bar.\nThis is automatically handled on iOS >= 11 including iPhone X using `SafeAreaView`.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to disable the default behaviour, and customize the height." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the header." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'small' | 'medium' | 'large' | 'center-aligned'", + "elements": [ + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'large'" + }, + { + "name": "literal", + "value": "'center-aligned'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", + "defaultValue": { + "value": "Platform.OS === 'ios' ? 'center-aligned' : 'small'", + "computed": false + } + }, + "elevated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "testID": { + "defaultValue": { + "value": "'appbar-header'", + "computed": false + }, + "required": false, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Appbar/AppbarHeader.tsx" + ], + "group": "Appbar" + }, + "Avatar/AvatarIcon": { + "filepath": "Avatar/AvatarIcon.tsx", + "title": "Avatar.Icon", + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "link": "avatar-icon", + "data": { + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "displayName": "Avatar.Icon", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Avatar`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the avatar.", + "defaultValue": { + "value": "64", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the icon." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Avatar/AvatarIcon.tsx" + ], + "group": "Avatar" + }, + "Avatar/AvatarImage": { + "filepath": "Avatar/AvatarImage.tsx", + "title": "Avatar.Image", + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", + "link": "avatar-image", + "data": { + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", + "displayName": "Avatar.Image", + "methods": [], + "statics": [], + "props": { + "source": { + "required": true, + "tsType": { + "name": "union", + "raw": "| ImageSourcePropType\n| ((props: { size: number }) => React.ReactNode)", + "elements": [ + { + "name": "ImageSourcePropType" + }, + { + "name": "unknown" + } + ] + }, + "description": "Image to display for the `Avatar`.\nIt accepts a standard React Native Image `source` prop\nOr a function that returns an `Image`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the avatar.", + "defaultValue": { + "value": "64", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "onError": { + "required": false, + "tsType": { + "name": "ImageProps['onError']", + "raw": "ImageProps['onError']" + }, + "description": "Invoked on load error." + }, + "onLayout": { + "required": false, + "tsType": { + "name": "ImageProps['onLayout']", + "raw": "ImageProps['onLayout']" + }, + "description": "Invoked on mount and on layout changes." + }, + "onLoad": { + "required": false, + "tsType": { + "name": "ImageProps['onLoad']", + "raw": "ImageProps['onLoad']" + }, + "description": "Invoked when load completes successfully." + }, + "onLoadEnd": { + "required": false, + "tsType": { + "name": "ImageProps['onLoadEnd']", + "raw": "ImageProps['onLoadEnd']" + }, + "description": "Invoked when load either succeeds or fails." + }, + "onLoadStart": { + "required": false, + "tsType": { + "name": "ImageProps['onLoadStart']", + "raw": "ImageProps['onLoadStart']" + }, + "description": "Invoked on load start." + }, + "onProgress": { + "required": false, + "tsType": { + "name": "ImageProps['onProgress']", + "raw": "ImageProps['onProgress']" + }, + "description": "Invoked on download progress." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Avatar/AvatarImage.tsx" + ], + "group": "Avatar" + }, + "Avatar/AvatarText": { + "filepath": "Avatar/AvatarText.tsx", + "title": "Avatar.Text", + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "link": "avatar-text", + "data": { + "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", + "displayName": "Avatar.Text", + "methods": [], + "statics": [], + "props": { + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Initials to show as the text in the `Avatar`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the avatar.", + "defaultValue": { + "value": "64", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for the text." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for text container" + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the title." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Avatar/AvatarText.tsx" + ], + "group": "Avatar" + }, + "Badge": { + "filepath": "Badge.tsx", + "title": "Badge", + "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", + "link": "badge", + "data": { + "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", + "displayName": "Badge", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the badge is visible", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "children": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + }, + "description": "Content of the `Badge`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the `Badge`.", + "defaultValue": { + "value": "20", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + {} + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Badge.tsx" + ] + }, + "Banner": { + "filepath": "Banner.tsx", + "title": "Banner", + "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "banner", + "data": { + "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Banner", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether banner is currently visible." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content that will be displayed inside banner." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Banner`. Can be an image." + }, + "actions": { + "required": false, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "intersection", + "raw": "{\n label: string;\n} & $RemoveChildren", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n label: string;\n}", + "signature": { + "properties": [ + { + "key": "label", + "value": { + "name": "string", + "required": true + } + } + ] + } + }, + { + "name": "$RemoveChildren", + "elements": [ + { + "name": "Button" + } + ], + "raw": "$RemoveChildren" + } + ] + } + ], + "raw": "Array<\n {\n label: string;\n } & $RemoveChildren\n>" + }, + "description": "Action items to shown in the banner.\nAn action item should contain the following properties:\n\n- `label`: label of the action button (required)\n- `onPress`: callback that is called when button is pressed (required)\n\nTo customize button you can pass other props that button component takes.", + "defaultValue": { + "value": "[]", + "computed": false + } + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of banner's inner content.\nUse this prop to apply custom width for wide layouts." + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges Banner shadow and background on iOS and Android.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "onShowAnimationFinished": { + "required": false, + "tsType": { + "name": "Animated.EndCallback" + }, + "description": "\nOptional callback that will be called after the opening animation finished running normally", + "defaultValue": { + "value": "() => {}", + "computed": false + } + }, + "onHideAnimationFinished": { + "required": false, + "tsType": { + "name": "Animated.EndCallback" + }, + "description": "\nOptional callback that will be called after the closing animation finished running normally", + "defaultValue": { + "value": "() => {}", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Banner.tsx" + ] + }, + "BottomNavigation/BottomNavigation": { + "filepath": "BottomNavigation/BottomNavigation.tsx", + "title": "BottomNavigation", + "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "bottom-navigation", + "data": { + "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "BottomNavigation", + "methods": [ + { + "name": "SceneMap", + "docblock": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance.", + "modifiers": [ + "static" + ], + "params": [ + { + "name": "scenes", + "optional": false, + "type": { + "name": "signature", + "type": "object", + "raw": "{\n [key: string]: React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n }>;\n}", + "signature": { + "properties": [ + { + "key": { + "name": "string" + }, + "value": { + "name": "ReactComponentType", + "raw": "React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n}>", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "jumpTo", + "value": { + "name": "signature", + "type": "function", + "raw": "(key: string) => void", + "signature": { + "arguments": [ + { + "name": "key", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + } + ] + } + } + ], + "required": true + } + } + ] + } + } + } + ], + "returns": null, + "description": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance." + } + ], + "statics": [], + "props": { + "shifting": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." + }, + "labeled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether tabs should be spread across the entire width." + }, + "navigationState": { + "required": true, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n index: number;\n routes: Route[];\n}", + "signature": { + "properties": [ + { + "key": "index", + "value": { + "name": "number", + "required": true + } + }, + { + "key": "routes", + "value": { + "name": "Array", + "elements": [ + { + "name": "Route" + } + ], + "raw": "Route[]", + "required": true + } + } + ] + } + }, + "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation` is a controlled component, which means the `index` needs to be updated via the `onIndexChange` callback." + }, + "onIndexChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(index: number) => void", + "signature": { + "arguments": [ + { + "name": "index", + "type": { + "name": "number" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback which is called on tab change, receives the index of the new tab as argument.\nThe navigation state needs to be updated when it's called, otherwise the change is dropped." + }, + "renderScene": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n jumpTo: (key: string) => void;\n}) => React.ReactNode | null", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "jumpTo", + "value": { + "name": "signature", + "type": "function", + "raw": "(key: string) => void", + "signature": { + "arguments": [ + { + "name": "key", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "React.ReactNode | null", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "null" + } + ] + } + } + }, + "description": "Callback which returns a react element to render as the page for the tab. Receives an object containing the route as the argument:\n\n```js\nrenderScene = ({ route, jumpTo }) => {\n switch (route.key) {\n case 'music':\n return ;\n case 'albums':\n return ;\n }\n}\n```\n\nPages are lazily rendered, which means that a page will be rendered the first time you navigate to it.\nAfter initial render, all the pages stay rendered to preserve their state.\n\nYou need to make sure that your individual routes implement a `shouldComponentUpdate` to improve the performance.\nTo make it easier to specify the components, you can use the `SceneMap` helper:\n\n```js\nrenderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n});\n```\n\nSpecifying the components this way is easier and takes care of implementing a `shouldComponentUpdate` method.\nEach component will receive the current route and a `jumpTo` method as it's props.\nThe `jumpTo` method can be used to navigate to other tabs programmatically:\n\n```js\nthis.props.jumpTo('albums')\n```" + }, + "renderIcon": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React Element to be used as tab icon." + }, + "renderLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which React Element to be used as tab label." + }, + "renderTouchable": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: TouchableProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "elements": [ + { + "name": "TouchableRippleProps" + }, + { + "name": "signature", + "type": "object", + "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "key", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "children", + "value": { + "name": "ReactReactNode", + "raw": "React.ReactNode", + "required": true + } + }, + { + "key": "borderless", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "centered", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "rippleColor", + "value": { + "name": "ColorValue", + "required": false + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS." + }, + "getAccessibilityLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default." + }, + "getBadge": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => boolean | number | string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "boolean | number | string | undefined", + "elements": [ + { + "name": "boolean" + }, + { + "name": "number" + }, + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get badge for the tab, uses `route.badge` by default." + }, + "getLabelText": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component." + }, + "getLazy": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => boolean | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "boolean | undefined", + "elements": [ + { + "name": "boolean" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get lazy for the current screen. Uses true by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.lazy", + "computed": false + } + }, + "getTestID": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get the id to locate this tab button in tests, uses `route.testID` by default." + }, + "onTabPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab press. It receives the route for the pressed tab, useful for things like scroll to top." + }, + "onTabLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab long press. It receives the route for the pressed tab, useful for things like custom action when longed pressed." + }, + "activeColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the active tab." + }, + "inactiveColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the inactive tab." + }, + "sceneAnimationEnabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether animation is enabled for scenes transitions in `shifting` mode.\nBy default, the scenes cross-fade during tab change when `shifting` is enabled.\nSpecify `sceneAnimationEnabled` as `false` to disable the animation.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "sceneAnimationType": { + "required": false, + "tsType": { + "name": "union", + "raw": "'opacity' | 'shifting'", + "elements": [ + { + "name": "literal", + "value": "'opacity'" + }, + { + "name": "literal", + "value": "'shifting'" + } + ] + }, + "description": "The scene animation effect. Specify `'shifting'` for a different effect.\nBy default, 'opacity' will be used.", + "defaultValue": { + "value": "'opacity'", + "computed": false + } + }, + "sceneAnimationEasing": { + "required": false, + "tsType": { + "name": "union", + "raw": "EasingFunction | undefined", + "elements": [ + { + "name": "EasingFunction" + }, + { + "name": "undefined" + } + ] + }, + "description": "The scene animation Easing." + }, + "keyboardHidesNavigationBar": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", + "defaultValue": { + "value": "Platform.OS === 'android'", + "computed": false + } + }, + "safeAreaInsets": { + "required": false, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", + "signature": { + "properties": [ + { + "key": "top", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "right", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "bottom", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "left", + "value": { + "name": "number", + "required": false + } + } + ] + } + }, + "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." + }, + "barStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style for the bottom navigation bar. You can pass a custom background color here:\n\n```js\nbarStyle={{ backgroundColor: '#694fad' }}\n```" + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "activeIndicatorStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'bottom-navigation'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/BottomNavigation/BottomNavigation.tsx" + ] + }, + "BottomNavigation/BottomNavigationBar": { + "filepath": "BottomNavigation/BottomNavigationBar.tsx", + "title": "BottomNavigation.Bar", + "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", + "link": "bottom-navigation-bar", + "data": { + "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", + "displayName": "BottomNavigation.Bar", + "methods": [], + "statics": [], + "props": { + "shifting": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." + }, + "labeled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether tabs should be spread across the entire width." + }, + "navigationState": { + "required": true, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n index: number;\n routes: Route[];\n}", + "signature": { + "properties": [ + { + "key": "index", + "value": { + "name": "number", + "required": true + } + }, + { + "key": "routes", + "value": { + "name": "Array", + "elements": [ + { + "name": "Route" + } + ], + "raw": "Route[]", + "required": true + } + } + ] + } + }, + "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation.Bar` is a controlled component, which means the `index` needs to be updated via the `onTabPress` callback." + }, + "renderIcon": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React Element to be used as tab icon." + }, + "renderLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "focused", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which React Element to be used as tab label." + }, + "renderTouchable": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: TouchableProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "elements": [ + { + "name": "TouchableRippleProps" + }, + { + "name": "signature", + "type": "object", + "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", + "signature": { + "properties": [ + { + "key": "key", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + }, + { + "key": "children", + "value": { + "name": "ReactReactNode", + "raw": "React.ReactNode", + "required": true + } + }, + { + "key": "borderless", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "centered", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "rippleColor", + "value": { + "name": "ColorValue", + "required": false + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS.", + "defaultValue": { + "value": "({ key, ...props }: TouchableProps) => (\n \n)", + "computed": false + } + }, + "getAccessibilityLabel": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) =>\nroute.accessibilityLabel", + "computed": false + } + }, + "getBadge": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => boolean | number | string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "boolean | number | string | undefined", + "elements": [ + { + "name": "boolean" + }, + { + "name": "number" + }, + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get badge for the tab, uses `route.badge` by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.badge", + "computed": false + } + }, + "getLabelText": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.title", + "computed": false + } + }, + "getTestID": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route }) => string | undefined", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "union", + "raw": "string | undefined", + "elements": [ + { + "name": "string" + }, + { + "name": "undefined" + } + ] + } + } + }, + "description": "Get the id to locate this tab button in tests, uses `route.testID` by default.", + "defaultValue": { + "value": "({ route }: { route: Route }) => route.testID", + "computed": false + } + }, + "onTabPress": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab press. It receives the route for the pressed tab. Use this to update the navigation state." + }, + "onTabLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { route: Route } & TabPressEvent) => void", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "{ route: Route } & TabPressEvent", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{ route: Route }", + "signature": { + "properties": [ + { + "key": "route", + "value": { + "name": "Route", + "required": true + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", + "signature": { + "properties": [ + { + "key": "defaultPrevented", + "value": { + "name": "boolean", + "required": true + } + }, + { + "key": "preventDefault", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on tab long press. It receives the route for the pressed tab" + }, + "activeColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the active tab." + }, + "inactiveColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for icon and label in the inactive tab." + }, + "animationEasing": { + "required": false, + "tsType": { + "name": "union", + "raw": "EasingFunction | undefined", + "elements": [ + { + "name": "EasingFunction" + }, + { + "name": "undefined" + } + ] + }, + "description": "The scene animation Easing." + }, + "keyboardHidesNavigationBar": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", + "defaultValue": { + "value": "Platform.OS === 'android'", + "computed": false + } + }, + "safeAreaInsets": { + "required": false, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", + "signature": { + "properties": [ + { + "key": "top", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "right", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "bottom", + "value": { + "name": "number", + "required": false + } + }, + { + "key": "left", + "value": { + "name": "number", + "required": false + } + } + ] + } + }, + "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "activeIndicatorStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'bottom-navigation-bar'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/BottomNavigation/BottomNavigationBar.tsx" + ], + "group": "BottomNavigation" + }, + "Button/Button": { + "filepath": "Button/Button.tsx", + "title": "Button", + "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "button", + "data": { + "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Button", + "methods": [], + "statics": [], + "props": { + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal'", + "elements": [ + { + "name": "literal", + "value": "'text'" + }, + { + "name": "literal", + "value": "'outlined'" + }, + { + "name": "literal", + "value": "'contained'" + }, + { + "name": "literal", + "value": "'elevated'" + }, + { + "name": "literal", + "value": "'contained-tonal'" + } + ] + }, + "description": "Mode of the button. You can change the mode to adjust the styling to give it desired emphasis.\n- `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options.\n- `outlined` - button with an outline without background, typically used for important, but not primary action – represents medium emphasis.\n- `contained` - button with a background color, used for important action, have the most visual impact and high emphasis.\n- `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background. @supported Available in v5.x with theme version 3\n- `contained-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. @supported Available in v5.x with theme version 3", + "defaultValue": { + "value": "'text'", + "computed": false + } + }, + "dark": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the color is a dark color. A dark button will render light text and vice-versa. Only applicable for:\n * `contained` mode for theme version 2\n * `contained`, `contained-tonal` and `elevated` modes for theme version 3." + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Use a compact look, useful for `text` buttons in a row." + }, + "buttonColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom button's background color." + }, + "textColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom button's text color." + }, + "loading": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show a loading indicator." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Button`." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Label text of the button." + }, + "uppercase": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Make the label text uppercased. Note that this won't work if you pass React elements as children." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "accessibilityHint": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility hint for the button. This is read by the screen reader when the user taps the button." + }, + "accessibilityRole": { + "required": false, + "tsType": { + "name": "AccessibilityRole" + }, + "description": "Accessibility role for the button. The \"button\" role is set by default.", + "defaultValue": { + "value": "'button'", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touch is released even before onPress." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of button's inner content.\nUse this prop to apply custom height and width, to set a custom padding or to set the icon on the right with `flexDirection: 'row-reverse'`." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the button text." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "touchableRef": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "Reference for the touchable" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'button'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Button/Button.tsx" + ] + }, + "Card/Card": { + "filepath": "Card/Card.tsx", + "title": "Card", + "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "card", + "data": { + "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Card", + "methods": [], + "statics": [], + "props": { + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'elevated' | 'outlined' | 'contained'", + "elements": [ + { + "name": "literal", + "value": "'elevated'" + }, + { + "name": "literal", + "value": "'outlined'" + }, + { + "name": "literal", + "value": "'contained'" + } + ] + }, + "description": "Mode of the Card.\n- `elevated` - Card with elevation.\n- `contained` - Card without outline and elevation @supported Available in v5.x with theme version 3\n- `outlined` - Card with an outline.", + "defaultValue": { + "value": "'elevated'", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Card`." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touch is released even before onPress." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "If true, disable all interactions for this component." + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "Changes Card shadow and background on iOS and Android.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of card's inner content." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Pass down testID from card props to touchable", + "defaultValue": { + "value": "'card'", + "computed": false + } + }, + "accessible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Pass down accessible from card props to touchable" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/Card.tsx" + ] + }, + "Card/CardActions": { + "filepath": "Card/CardActions.tsx", + "title": "Card.Actions", + "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "card-actions", + "data": { + "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Card.Actions", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Items inside the `CardActions`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardActions.tsx" + ], + "group": "Card" + }, + "Card/CardContent": { + "filepath": "Card/CardContent.tsx", + "title": "Card.Content", + "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", + "link": "card-content", + "data": { + "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Card.Content", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Items inside the `Card.Content`." + }, + "index": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "total": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "siblings": { + "required": false, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "string" + } + ], + "raw": "Array" + }, + "description": "@internal" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardContent.tsx" + ], + "group": "Card" + }, + "Card/CardCover": { + "filepath": "Card/CardCover.tsx", + "title": "Card.Cover", + "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", + "link": "card-cover", + "data": { + "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", + "displayName": "Card.Cover", + "methods": [], + "statics": [], + "props": { + "index": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "total": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardCover.tsx" + ], + "group": "Card" + }, + "Card/CardTitle": { + "filepath": "Card/CardTitle.tsx", + "title": "Card.Title", + "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", + "link": "card-title", + "data": { + "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", + "displayName": "Card.Title", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text for the title. Note that this will only accept a string or ``-based node." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the title." + }, + "titleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Number of lines for the title.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "titleVariant": { + "required": false, + "tsType": { + "name": "TypescaleKey" + }, + "description": "@supported Available in v5.x with theme version 3\n\nTitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyLarge'", + "computed": false + } + }, + "subtitle": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text for the subtitle. Note that this will only accept a string or ``-based node." + }, + "subtitleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the subtitle." + }, + "subtitleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Number of lines for the subtitle.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "subtitleVariant": { + "required": false, + "tsType": { + "name": "TypescaleKey" + }, + "description": "@supported Available in v5.x with theme version 3\n\nSubtitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyMedium'", + "computed": false + } + }, + "left": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { size: number }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ size: number }", + "signature": { + "properties": [ + { + "key": "size", + "value": { + "name": "number", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the left side." + }, + "leftStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the left element wrapper." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { size: number }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ size: number }", + "signature": { + "properties": [ + { + "key": "size", + "value": { + "name": "number", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side." + }, + "rightStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the right element wrapper." + }, + "index": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "total": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "@internal" + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "subtitleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a subtitle font can reach." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Card/CardTitle.tsx" + ], + "group": "Card" + }, + "Checkbox/Checkbox": { + "filepath": "Checkbox/Checkbox.tsx", + "title": "Checkbox", + "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", + "link": "checkbox", + "data": { + "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Checkbox", + "methods": [], + "statics": [], + "props": { + "status": { + "required": true, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked' | 'indeterminate'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + }, + { + "name": "literal", + "value": "'indeterminate'" + } + ] + }, + "description": "Status of checkbox." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether checkbox is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom color for unchecked checkbox." + }, + "color": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom color for checkbox." + }, + "error": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the checkbox is in an error state. When true, the outline\n(unchecked) and container (selected) use `theme.colors.error`.\n`disabled` and explicit `color`/`uncheckedColor` overrides take\nprecedence." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Custom style to override the default tap target. Passed through to\nthe underlying `TouchableRipple`." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Checkbox/Checkbox.tsx" + ] + }, + "Checkbox/CheckboxItem": { + "filepath": "Checkbox/CheckboxItem.tsx", + "title": "Checkbox.Item", + "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "checkbox-item", + "data": { + "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Checkbox.Item", + "methods": [], + "statics": [], + "props": { + "status": { + "required": true, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked' | 'indeterminate'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + }, + { + "name": "literal", + "value": "'indeterminate'" + } + ] + }, + "description": "Status of checkbox." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether checkbox is disabled." + }, + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Label to be displayed on the item." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", + "defaultValue": { + "value": "label", + "computed": true + } + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked checkbox." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for checkbox." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Additional styles for container View." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach.", + "defaultValue": { + "value": "1.5", + "computed": false + } + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Label element." + }, + "labelVariant": { + "required": false, + "tsType": { + "name": "TypescaleKey" + }, + "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyLarge'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + }, + "position": { + "required": false, + "tsType": { + "name": "union", + "raw": "'leading' | 'trailing'", + "elements": [ + { + "name": "literal", + "value": "'leading'" + }, + { + "name": "literal", + "value": "'trailing'" + } + ] + }, + "description": "Checkbox control position.", + "defaultValue": { + "value": "'trailing'", + "computed": false + } + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Checkbox/CheckboxItem.tsx" + ], + "group": "Checkbox" + }, + "Chip/Chip": { + "filepath": "Chip/Chip.tsx", + "title": "Chip", + "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n
      \n
    • Present multiple options
    • \n
    • Represent attributes active or chosen
    • \n
    • Present filter options
    • \n
    • Trigger actions related to primary content
    • \n
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", + "link": "chip", + "data": { + "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n
      \n
    • Present multiple options
    • \n
    • Represent attributes active or chosen
    • \n
    • Present filter options
    • \n
    • Trigger actions related to primary content
    • \n
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", + "displayName": "Chip", + "methods": [], + "statics": [], + "props": { + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'outlined'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'outlined'" + } + ] + }, + "description": "Mode of the chip.\n- `flat` - flat chip without outline.\n- `outlined` - chip with an outline.", + "defaultValue": { + "value": "'flat'", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text content of the `Chip`." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `Chip`. Both icon and avatar cannot be specified." + }, + "avatar": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Avatar to display for the `Chip`. Both icon and avatar cannot be specified." + }, + "closeIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display as the close button for the `Chip`. The icon appears only when the onClose prop is specified." + }, + "selected": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether chip is selected.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "selectedColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Whether to style the chip color as selected.\nNote: With theme version 3 `selectedColor` doesn't apply to the `icon`.\n If you want specify custom color for the `icon`, render your own `Icon` component." + }, + "showSelectedOverlay": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether to display overlay on selected chip" + }, + "showSelectedCheck": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to display default check icon on selected chip.\nNote: Check will not be shown if `icon` is specified. If specified, `icon` will be shown regardless of `selected`.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the chip is disabled. A disabled chip is greyed out and `onPress` is not called on touch.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the chip. This is read by the screen reader when the user taps the chip." + }, + "closeIconAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon.", + "defaultValue": { + "value": "'Close'", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute as soon as the touch is released even before onPress." + }, + "onClose": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on close button press. The close button appears only when this prop is specified." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "compact": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nSets smaller horizontal paddings `12dp` around label, when there is only label." + }, + "elevated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether chip should have the elevation.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "textStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of chip's text" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Pass down testID from chip props to touchable for Detox tests.", + "defaultValue": { + "value": "'chip'", + "computed": false + } + }, + "ellipsizeMode": { + "required": false, + "tsType": { + "name": "EllipsizeProp" + }, + "description": "Ellipsize Mode for the children text" + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "accessibilityRole": { + "defaultValue": { + "value": "'button'", + "computed": false + }, + "required": false, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Chip/Chip.tsx" + ] + }, + "DataTable/DataTable": { + "filepath": "DataTable/DataTable.tsx", + "title": "DataTable", + "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "data-table", + "data": { + "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "DataTable", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTable`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTable.tsx" + ] + }, + "DataTable/DataTableCell": { + "filepath": "DataTable/DataTableCell.tsx", + "title": "DataTable.Cell", + "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "data-table-cell", + "data": { + "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "DataTable.Cell", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTableCell`." + }, + "numeric": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Align the text to the right. Generally monetary or number fields are aligned to right." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "textStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Text content style of the `DataTableCell`." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableCell.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTableHeader": { + "filepath": "DataTable/DataTableHeader.tsx", + "title": "DataTable.Header", + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "link": "data-table-header", + "data": { + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "DataTable.Header", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTableHeader`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableHeader.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTablePagination": { + "filepath": "DataTable/DataTablePagination.tsx", + "title": "DataTable.Pagination", + "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "data-table-pagination", + "data": { + "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "DataTable.Pagination", + "methods": [], + "statics": [], + "props": { + "page": { + "required": true, + "tsType": { + "name": "number" + }, + "description": "The currently visible page (starting with 0)." + }, + "numberOfPages": { + "required": true, + "tsType": { + "name": "number" + }, + "description": "The total number of pages." + }, + "onPageChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(page: number) => void", + "signature": { + "arguments": [ + { + "name": "page", + "type": { + "name": "number" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on page change." + }, + "showFastPaginationControls": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show fast forward and fast rewind buttons in pagination. False by default.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "numberOfItemsPerPage": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The current number of rows per page." + }, + "numberOfItemsPerPageList": { + "required": false, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "number" + } + ], + "raw": "Array" + }, + "description": "Options for a number of rows per page to choose from." + }, + "onItemsPerPageChange": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(numberOfItemsPerPage: number) => void", + "signature": { + "arguments": [ + { + "name": "numberOfItemsPerPage", + "type": { + "name": "number" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "The function to set the number of rows per page." + }, + "selectPageDropdownLabel": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Label text for select page dropdown to display." + }, + "selectPageDropdownAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "AccessibilityLabel for `selectPageDropdownLabel`." + }, + "label": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Label text to display which indicates current pagination." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "AccessibilityLabel for `label`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTablePagination.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTableRow": { + "filepath": "DataTable/DataTableRow.tsx", + "title": "DataTable.Row", + "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "data-table-row", + "data": { + "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "DataTable.Row", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DataTableRow`." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "pointerEvents": { + "required": false, + "tsType": { + "name": "ViewProps['pointerEvents']", + "raw": "ViewProps['pointerEvents']" + }, + "description": "`pointerEvents` passed to the `View` container, which is wrapping children within `TouchableRipple`." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableRow.tsx" + ], + "group": "DataTable" + }, + "DataTable/DataTableTitle": { + "filepath": "DataTable/DataTableTitle.tsx", + "title": "DataTable.Title", + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "link": "data-table-title", + "data": { + "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "DataTable.Title", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text content of the `DataTableTitle`." + }, + "numeric": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Align the text to the right. Generally monetary or number fields are aligned to right." + }, + "sortDirection": { + "required": false, + "tsType": { + "name": "union", + "raw": "'ascending' | 'descending'", + "elements": [ + { + "name": "literal", + "value": "'ascending'" + }, + { + "name": "literal", + "value": "'descending'" + } + ] + }, + "description": "Direction of sorting. An arrow indicating the direction is displayed when this is given." + }, + "numberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of lines to show.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "textStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Text content style of the `DataTableTitle`." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/DataTable/DataTableTitle.tsx" + ], + "group": "DataTable" + }, + "Dialog/Dialog": { + "filepath": "Dialog/Dialog.tsx", + "title": "Dialog", + "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog", + "data": { + "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog", + "methods": [], + "statics": [], + "props": { + "dismissable": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking outside the dialog dismiss it.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "dismissableBackButton": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking Android hardware back button dismiss dialog.", + "defaultValue": { + "value": "dismissable", + "computed": true + } + }, + "onDismiss": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the user dismisses the dialog." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines Whether the dialog is visible.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Dialog`." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/Dialog.tsx" + ] + }, + "Dialog/DialogActions": { + "filepath": "Dialog/DialogActions.tsx", + "title": "Dialog.Actions", + "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-actions", + "data": { + "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Actions", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DialogActions`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogActions.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogContent": { + "filepath": "Dialog/DialogContent.tsx", + "title": "Dialog.Content", + "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-content", + "data": { + "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Content", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DialogContent`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogContent.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogIcon": { + "filepath": "Dialog/DialogIcon.tsx", + "title": "Dialog.Icon", + "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", + "link": "dialog-icon", + "data": { + "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Icon", + "methods": [], + "statics": [], + "props": { + "color": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom color for action icon." + }, + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Name of the icon to show." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Optional icon size.", + "defaultValue": { + "value": "24", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogIcon.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogScrollArea": { + "filepath": "Dialog/DialogScrollArea.tsx", + "title": "Dialog.ScrollArea", + "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-scroll-area", + "data": { + "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.ScrollArea", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `DialogScrollArea`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogScrollArea.tsx" + ], + "group": "Dialog" + }, + "Dialog/DialogTitle": { + "filepath": "Dialog/DialogTitle.tsx", + "title": "Dialog.Title", + "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "dialog-title", + "data": { + "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Dialog.Title", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Title text for the `DialogTitle`." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Dialog/DialogTitle.tsx" + ], + "group": "Dialog" + }, + "Divider": { + "filepath": "Divider.tsx", + "title": "Divider", + "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", + "link": "divider", + "data": { + "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "Divider", + "methods": [], + "statics": [], + "props": { + "leftInset": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@renamed Renamed from 'inset' to 'leftInset` in v5.x\nWhether divider has a left inset." + }, + "horizontalInset": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n Whether divider has a horizontal inset on both sides.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "bold": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n Whether divider should be bolded.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Divider.tsx" + ] + }, + "Drawer/DrawerCollapsedItem": { + "filepath": "Drawer/DrawerCollapsedItem.tsx", + "title": "Drawer.CollapsedItem", + "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "drawer-collapsed-item", + "data": { + "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Drawer.CollapsedItem", + "methods": [], + "statics": [], + "props": { + "label": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "The label text of the item." + }, + "badge": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number | boolean", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + }, + { + "name": "boolean" + } + ] + }, + "description": "Badge to show on the icon, can be `true` to show a dot, `string` or `number` to show text.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the item is disabled." + }, + "focusedIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the focused destination icon, can be a string, an image source or a react component" + }, + "unfocusedIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the unfocused destination icon, can be a string, an image source or a react component" + }, + "active": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to highlight the drawer item as active." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'drawer-collapsed-item'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Drawer/DrawerCollapsedItem.tsx" + ], + "group": "Drawer" + }, + "Drawer/DrawerItem": { + "filepath": "Drawer/DrawerItem.tsx", + "title": "Drawer.Item", + "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "drawer-item", + "data": { + "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Drawer.Item", + "methods": [], + "statics": [], + "props": { + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "The label text of the item." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `DrawerItem`." + }, + "active": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to highlight the drawer item as active." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the item is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: ColorValue }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: ColorValue }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side. For instance a Badge." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Drawer/DrawerItem.tsx" + ], + "group": "Drawer" + }, + "Drawer/DrawerSection": { + "filepath": "Drawer/DrawerSection.tsx", + "title": "Drawer.Section", + "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "drawer-section", + "data": { + "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Drawer.Section", + "methods": [], + "statics": [], + "props": { + "title": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Title to show as the header for the section." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Drawer.Section`." + }, + "showDivider": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show `Divider` at the end of the section. True by default.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Drawer/DrawerSection.tsx" + ], + "group": "Drawer" + }, + "FAB/FAB": { + "filepath": "FAB/FAB.tsx", + "title": "FAB", + "description": "A floating action button represents the primary action on a screen.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", + "link": "fab", + "data": { + "description": "A floating action button represents the primary action on a screen.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "FAB", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display inside the FAB." + }, + "variant": { + "required": false, + "tsType": { + "name": "Variant" + }, + "description": "Role-color preset. Defaults to `tonalPrimary`.", + "defaultValue": { + "value": "'tonalPrimary'", + "computed": false + } + }, + "containerColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Override the container (background) color." + }, + "contentColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Override the content (icon) color." + }, + "size": { + "required": false, + "tsType": { + "name": "Size" + }, + "description": "Spec size. Defaults to `default`.", + "defaultValue": { + "value": "'default'", + "computed": false + } + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the FAB is currently visible. Toggling animates the spec'd enter\nand exit (scale + alpha) on the FAB itself.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label. Falls back to nothing if unset." + }, + "accessibilityState": { + "required": false, + "tsType": { + "name": "AccessibilityState" + }, + "description": "Accessibility state forwarded to the underlying button." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawable to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for positioning the FAB. The visual treatment (size, shape, color)\nis driven by `variant` and `size`." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes.", + "defaultValue": { + "value": "'floating-action-button'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/FAB/FAB.tsx" + ] + }, + "FAB/Extended": { + "filepath": "FAB/Extended.tsx", + "title": "Extended", + "description": "An extended floating action button represents the primary action on a screen\nand shows a label next to the icon. Animates between expanded (icon + label)\nand collapsed (icon only) states.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n return (\n setExpanded((v) => !v)}\n style={styles.fab}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n left: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", + "link": "extended", + "data": { + "description": "An extended floating action button represents the primary action on a screen\nand shows a label next to the icon. Animates between expanded (icon + label)\nand collapsed (icon only) states.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n return (\n setExpanded((v) => !v)}\n style={styles.fab}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n left: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "Extended", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display inside the FAB." + }, + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Label rendered next to the icon when expanded." + }, + "variant": { + "required": false, + "tsType": { + "name": "Variant" + }, + "description": "Role-color preset. Defaults to `tonalPrimary`.", + "defaultValue": { + "value": "'tonalPrimary'", + "computed": false + } + }, + "containerColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Override the container (background) color. When set without `contentColor`,\nthe icon and label colors are derived automatically via `contentColorFor`." + }, + "contentColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Override the content (icon + label) color." + }, + "size": { + "required": false, + "tsType": { + "name": "Size" + }, + "description": "Spec size. Defaults to `default`.", + "defaultValue": { + "value": "'default'", + "computed": false + } + }, + "expanded": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether the FAB is expanded (icon + label) or collapsed (icon only). The\nwidth and label opacity animate per the MD3 Expressive spec on change." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the FAB is currently visible. Toggling animates the spec'd enter\nand exit (scale + alpha) on the FAB itself.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label. Falls back to `label` if unset.", + "defaultValue": { + "value": "label", + "computed": true + } + }, + "accessibilityState": { + "required": false, + "tsType": { + "name": "AccessibilityState" + }, + "description": "Accessibility state forwarded to the underlying button." + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawable to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for positioning the FAB. The visual treatment (size, shape, color)\nis driven by `variant` and `size`." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes.", + "defaultValue": { + "value": "'extended-floating-action-button'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/FAB/Extended.tsx" + ] + }, + "FAB/Menu": { + "filepath": "FAB/Menu.tsx", + "title": "Menu", + "description": "Floating action button menu. Wraps a trigger FAB; when `expanded` is true,\nitems appear stacked above and the trigger morphs into the spec'd close\nbutton (`shape: 'full'`, 56 dp, saturated role color).\n\nNo visual backdrop and no outside-tap dismiss — that matches the MD3 spec\nand lets the user keep interacting with the content underneath. Dismiss\nvia the close button or by tapping an item.\n\n## Usage\n```tsx\nconst [open, setOpen] = React.useState(false);\n\n\n setOpen(false)}\n trigger={{ icon: 'plus', variant: 'primary', onPress: () => setOpen(true) }}\n items={[\n { icon: 'email', label: 'Send', onPress: () => {} },\n { icon: 'bell', label: 'Remind', onPress: () => {} },\n ]}\n />\n\n```", + "link": "menu", + "data": { + "description": "Floating action button menu. Wraps a trigger FAB; when `expanded` is true,\nitems appear stacked above and the trigger morphs into the spec'd close\nbutton (`shape: 'full'`, 56 dp, saturated role color).\n\nNo visual backdrop and no outside-tap dismiss — that matches the MD3 spec\nand lets the user keep interacting with the content underneath. Dismiss\nvia the close button or by tapping an item.\n\n## Usage\n```tsx\nconst [open, setOpen] = React.useState(false);\n\n\n setOpen(false)}\n trigger={{ icon: 'plus', variant: 'primary', onPress: () => setOpen(true) }}\n items={[\n { icon: 'email', label: 'Send', onPress: () => {} },\n { icon: 'bell', label: 'Remind', onPress: () => {} },\n ]}\n />\n\n```", + "displayName": "Menu", + "methods": [], + "statics": [], + "props": { + "expanded": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether the menu is open." + }, + "onDismiss": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Called when the user taps the close button or taps an item." + }, + "trigger": { + "required": true, + "tsType": { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Icon displayed in the trigger FAB (and cross-faded to `closeIcon` when\n * the menu is open).\n */\n icon: IconSource;\n variant?: Variant;\n size?: Size;\n containerColor?: ColorValue;\n contentColor?: ColorValue;\n visible?: boolean;\n onPress?: (e: GestureResponderEvent) => void;\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": true + } + }, + { + "key": "variant", + "value": { + "name": "Variant", + "required": false + } + }, + { + "key": "size", + "value": { + "name": "Size", + "required": false + } + }, + { + "key": "containerColor", + "value": { + "name": "ColorValue", + "required": false + } + }, + { + "key": "contentColor", + "value": { + "name": "ColorValue", + "required": false + } + }, + { + "key": "visible", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": false + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + "description": "Trigger FAB configuration. The menu renders a morphing FAB that\nanimates between the trigger appearance and the spec'd close button." + }, + "alignment": { + "required": false, + "tsType": { + "name": "union", + "raw": "'start' | 'center' | 'end'", + "elements": [ + { + "name": "literal", + "value": "'start'" + }, + { + "name": "literal", + "value": "'center'" + }, + { + "name": "literal", + "value": "'end'" + } + ] + }, + "description": "Horizontal side the menu sits on. Default `'end'`.", + "defaultValue": { + "value": "'end'", + "computed": false + } + }, + "closeIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon used by the close button when the menu is expanded. Default\n`'close'`.", + "defaultValue": { + "value": "'close'", + "computed": false + } + }, + "items": { + "required": true, + "tsType": { + "name": "union", + "raw": "| [T, T]\n| [T, T, T]\n| [T, T, T, T]\n| [T, T, T, T, T]\n| [T, T, T, T, T, T]", + "elements": [ + { + "name": "tuple", + "raw": "[T, T]", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + } + ] + }, + { + "name": "tuple", + "raw": "[T, T, T]", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + } + ] + }, + { + "name": "tuple", + "raw": "[T, T, T, T]", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + } + ] + }, + { + "name": "tuple", + "raw": "[T, T, T, T, T]", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + } + ] + }, + { + "name": "tuple", + "raw": "[T, T, T, T, T, T]", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + }, + { + "name": "signature", + "type": "object", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": true + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": true + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + } + ] + } + ] + }, + "description": "Menu items. Spec calls for 2 to 6 items." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "", + "defaultValue": { + "value": "'floating-action-button-menu'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/FAB/Menu.tsx" + ] + }, + "IconButton/IconButton": { + "filepath": "IconButton/IconButton.tsx", + "title": "IconButton", + "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "icon-button", + "data": { + "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "IconButton", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'outlined' | 'contained' | 'contained-tonal'", + "elements": [ + { + "name": "literal", + "value": "'outlined'" + }, + { + "name": "literal", + "value": "'contained'" + }, + { + "name": "literal", + "value": "'contained-tonal'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nMode of the icon button. By default there is no specified mode - only pressable icon will be rendered." + }, + "iconColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "@renamed Renamed from 'color' to 'iconColor' in v5.x\nColor of the icon." + }, + "containerColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Background color of the icon container." + }, + "selected": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether icon button is selected. A selected button receives alternative combination of icon and container colors.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the icon.", + "defaultValue": { + "value": "24", + "computed": false + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." + }, + "animated": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether an icon change is animated.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style of button's inner content.\nUse this prop to apply custom height and width or to set a custom padding`." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'icon-button'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "loading": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show a loading indicator.", + "defaultValue": { + "value": "false", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/IconButton/IconButton.tsx" + ] + }, + "Icon": { + "filepath": "Icon.tsx", + "title": "Icon", + "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "icon", + "data": { + "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Icon", + "methods": [], + "statics": [], + "props": { + "size": { + "required": true, + "tsType": { + "name": "number" + }, + "description": "Size of icon." + }, + "allowFontScaling": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "" + }, + "source": { + "required": true, + "tsType": { + "name": "any" + }, + "description": "Icon to display." + }, + "color": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the icon." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Icon.tsx" + ] + }, + "List/ListAccordion": { + "filepath": "List/ListAccordion.tsx", + "title": "List.Accordion", + "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "list-accordion", + "data": { + "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "List.Accordion", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Title text for the list accordion." + }, + "description": { + "required": false, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Description text for the list accordion." + }, + "left": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: ColorValue; style: Style }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: ColorValue; style: Style }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the left side." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { isExpanded: boolean }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ isExpanded: boolean }", + "signature": { + "properties": [ + { + "key": "isExpanded", + "value": { + "name": "boolean", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side." + }, + "expanded": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the accordion is expanded\nIf this prop is provided, the accordion will behave as a \"controlled component\".\nYou'll need to update this prop when you want to toggle the component or on `onPress`." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "delayLongPress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the section." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the root TouchableRipple container." + }, + "containerStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the content container, which wraps the title and description." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Title element." + }, + "descriptionStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Description element." + }, + "titleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "descriptionNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "descriptionMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a description font can reach." + }, + "id": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + }, + "description": "Id is used for distinguishing specific accordion when using List.AccordionGroup. Property is required when using List.AccordionGroup and has no impact on behavior when using standalone List.Accordion." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the TouchableRipple. This is read by the screen reader when the user taps the touchable." + }, + "pointerEvents": { + "required": false, + "tsType": { + "name": "ViewProps['pointerEvents']", + "raw": "ViewProps['pointerEvents']" + }, + "description": "`pointerEvents` passed to the `View` container", + "defaultValue": { + "value": "'none'", + "computed": false + } + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Amount of space between the touchable area and the edge of the component.\nThis can be used to enlarge the touchable area beyond the visible component." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListAccordion.tsx" + ], + "group": "List" + }, + "List/ListAccordionGroup": { + "filepath": "List/ListAccordionGroup.tsx", + "title": "List.AccordionGroup", + "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "list-accordion-group", + "data": { + "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "List.AccordionGroup", + "methods": [], + "statics": [], + "props": { + "onAccordionPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(expandedId: string | number) => void", + "signature": { + "arguments": [ + { + "name": "expandedId", + "type": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "expandedId": { + "required": false, + "tsType": { + "name": "union", + "raw": "string | number", + "elements": [ + { + "name": "string" + }, + { + "name": "number" + } + ] + }, + "description": "Id of the currently expanded list accordion" + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing list accordions" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListAccordionGroup.tsx" + ], + "group": "List" + }, + "List/ListIcon": { + "filepath": "List/ListIcon.tsx", + "title": "List.Icon", + "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", + "link": "list-icon", + "data": { + "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", + "displayName": "List.Icon", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to show." + }, + "color": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color for the icon." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListIcon.tsx" + ], + "group": "List" + }, + "List/ListItem": { + "filepath": "List/ListItem.tsx", + "title": "List.Item", + "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "list-item", + "data": { + "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "List.Item", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "union", + "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: ColorValue;\n fontSize: number;\n }) => React.ReactNode)", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "unknown" + } + ] + }, + "description": "Title text for the list item." + }, + "description": { + "required": false, + "tsType": { + "name": "union", + "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: ColorValue;\n fontSize: number;\n }) => React.ReactNode)", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "unknown" + } + ] + }, + "description": "Description text for the list item or callback which returns a React element to display the description." + }, + "left": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: ColorValue; style: Style }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: ColorValue; style: Style }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the left side." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: { color: ColorValue; style?: Style }) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{ color: ColorValue; style?: Style }", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": false + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback which returns a React element to display on the right side." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the root TouchableRipple container." + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "containerStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the content container, which wraps the title and description." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Title element." + }, + "descriptionStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Description element." + }, + "titleNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "descriptionNumberOfLines": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "titleEllipsizeMode": { + "required": false, + "tsType": { + "name": "EllipsizeProp" + }, + "description": "Ellipsize Mode for the Title. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" + }, + "descriptionEllipsizeMode": { + "required": false, + "tsType": { + "name": "EllipsizeProp" + }, + "description": "Ellipsize Mode for the Description. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "descriptionMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a description font can reach." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListItem.tsx" + ], + "group": "List" + }, + "List/ListSection": { + "filepath": "List/ListSection.tsx", + "title": "List.Section", + "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", + "link": "list-section", + "data": { + "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "List.Section", + "methods": [], + "statics": [], + "props": { + "title": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Title text for the section." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the section." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Title element." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListSection.tsx" + ], + "group": "List" + }, + "List/ListSubheader": { + "filepath": "List/ListSubheader.tsx", + "title": "List.Subheader", + "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", + "link": "list-subheader", + "data": { + "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", + "displayName": "List.Subheader", + "methods": [], + "statics": [], + "props": { + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Text element." + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/List/ListSubheader.tsx" + ], + "group": "List" + }, + "Menu/Menu": { + "filepath": "Menu/Menu.tsx", + "title": "Menu", + "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n
    \n \n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", + "link": "menu", + "data": { + "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n
\n \n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", + "displayName": "Menu", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether the Menu is currently visible." + }, + "anchor": { + "required": true, + "tsType": { + "name": "union", + "raw": "React.ReactNode | { x: number; y: number }", + "elements": [ + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + { + "name": "signature", + "type": "object", + "raw": "{ x: number; y: number }", + "signature": { + "properties": [ + { + "key": "x", + "value": { + "name": "number", + "required": true + } + }, + { + "key": "y", + "value": { + "name": "number", + "required": true + } + } + ] + } + } + ] + }, + "description": "The anchor to open the menu from. In most cases, it will be a button that opens the menu." + }, + "anchorPosition": { + "required": false, + "tsType": { + "name": "union", + "raw": "'top' | 'bottom'", + "elements": [ + { + "name": "literal", + "value": "'top'" + }, + { + "name": "literal", + "value": "'bottom'" + } + ] + }, + "description": "Whether the menu should open at the top of the anchor or at its bottom.\nApplied only when anchor is a node, not an x/y position." + }, + "statusBarHeight": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Extra margin to add at the top of the menu to account for translucent status bar on Android.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to and customize it.\nThis is automatically handled on iOS." + }, + "onDismiss": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback called when Menu is dismissed. The `visible` prop needs to be updated when this is called." + }, + "overlayAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the menu.", + "defaultValue": { + "value": "'Close menu'", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Menu`." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style of menu's inner content." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "elevation": { + "required": false, + "tsType": { + "name": "Elevation" + }, + "description": "Elevation level of the menu's content. Shadow styles are calculated based on this value. Default `backgroundColor` is taken from the corresponding `theme.colors.elevation` property. By default equals `2`.\n@supported Available in v5.x with theme version 3", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'elevated'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'elevated'" + } + ] + }, + "description": "Mode of the menu's content.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.\n\n@supported Available in v5.x with theme version 3", + "defaultValue": { + "value": "'elevated'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "keyboardShouldPersistTaps": { + "required": false, + "tsType": { + "name": "ScrollViewProps['keyboardShouldPersistTaps']", + "raw": "ScrollViewProps['keyboardShouldPersistTaps']" + }, + "description": "Inner ScrollView prop" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'menu'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Menu/Menu.tsx" + ] + }, + "Menu/MenuItem": { + "filepath": "Menu/MenuItem.tsx", + "title": "Menu.Item", + "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", + "link": "menu-item", + "data": { + "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Menu.Item", + "methods": [], + "statics": [], + "props": { + "title": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Title text for the `MenuItem`." + }, + "leadingIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@renamed Renamed from 'icon' to 'leadingIcon' in v5.x\n\nLeading icon to display for the `MenuItem`." + }, + "trailingIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@supported Available in v5.x with theme version 3\n\nTrailing icon to display for the `MenuItem`." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the 'item' is disabled. A disabled 'item' is greyed out and `onPress` is not called on touch." + }, + "dense": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\n\nSets min height with densed layout." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach.", + "defaultValue": { + "value": "1.5", + "computed": false + } + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the root TouchableRipple container.\n" + }, + "containerStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the outermost container that wraps the entire content, including leading and trailing icons and title text." + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the content container, which wraps the title text." + }, + "titleStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to the Title element." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'menu-item'", + "computed": false + } + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the Touchable. This is read by the screen reader when the user taps the component." + }, + "accessibilityState": { + "required": false, + "tsType": { + "name": "AccessibilityState" + }, + "description": "Accessibility state for the Touchable. This is read by the screen reader when the user taps the component." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Menu/MenuItem.tsx" + ], + "group": "Menu" + }, + "Modal": { + "filepath": "Modal.tsx", + "title": "Modal", + "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "modal", + "data": { + "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "Modal", + "methods": [], + "statics": [], + "props": { + "dismissable": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking outside the modal dismisses it.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "dismissableBackButton": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines whether clicking Android hardware back button dismisses the dialog.", + "defaultValue": { + "value": "dismissable", + "computed": true + } + }, + "onDismiss": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the user dismisses the modal.", + "defaultValue": { + "value": "() => {}", + "computed": false + } + }, + "overlayAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal.", + "defaultValue": { + "value": "'Close modal'", + "computed": false + } + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Determines Whether the modal is visible.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Modal`." + }, + "contentContainerStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style for the content of the modal" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the wrapper of the modal.\nUse this prop to change the default wrapper style or to override safe area insets with marginTop and marginBottom." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'modal'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Modal.tsx" + ] + }, + "Portal/Portal": { + "filepath": "Portal/Portal.tsx", + "title": "Portal", + "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", + "link": "portal", + "data": { + "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Portal", + "methods": [], + "statics": [ + { + "name": "Host", + "docblock": null, + "type": { + "name": "static", + "raw": "static" + }, + "link": "portal-host.html" + } + ], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Portal`." + }, + "theme": { + "required": true, + "tsType": { + "name": "InternalTheme" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Portal/Portal.tsx" + ] + }, + "Portal/PortalHost": { + "filepath": "Portal/PortalHost.tsx", + "title": "Portal.Host", + "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", + "link": "portal-host", + "data": { + "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", + "displayName": "Portal.Host", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Portal/PortalHost.tsx" + ], + "group": "Portal" + }, + "ProgressBar": { + "filepath": "ProgressBar.tsx", + "title": "ProgressBar", + "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "link": "progress-bar", + "data": { + "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", + "displayName": "ProgressBar", + "methods": [], + "statics": [], + "props": { + "animatedValue": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Animated value (between 0 and 1). This tells the progress bar to rely on this value to animate it.\nNote: It should not be used in parallel with the `progress` prop." + }, + "progress": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Progress value (between 0 and 1).\nNote: It should not be used in parallel with the `animatedValue` prop.", + "defaultValue": { + "value": "0", + "computed": false + } + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Color of the progress bar. The background color will be calculated based on this but you can change it by passing `backgroundColor` to `style` prop." + }, + "indeterminate": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "If the progress bar will show indeterminate progress." + }, + "visible": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to show the ProgressBar (true, the default) or hide it (false).", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "fillStyle": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "Style of filled part of the ProgresBar." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests.", + "defaultValue": { + "value": "'progress-bar'", + "computed": false + } + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ProgressBar.tsx" + ] + }, + "RadioButton/RadioButton": { + "filepath": "RadioButton/RadioButton.tsx", + "title": "RadioButton", + "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "link": "radio-button", + "data": { + "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "RadioButton", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button" + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked radio." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButton.tsx" + ] + }, + "RadioButton/RadioButtonAndroid": { + "filepath": "RadioButton/RadioButtonAndroid.tsx", + "title": "RadioButton.Android", + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "radio-button-android", + "data": { + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "RadioButton.Android", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button" + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(param?: any) => void", + "signature": { + "arguments": [ + { + "name": "param", + "type": { + "name": "any" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked radio." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonAndroid.tsx" + ], + "group": "RadioButton" + }, + "RadioButton/RadioButtonGroup": { + "filepath": "RadioButton/RadioButtonGroup.tsx", + "title": "RadioButton.Group", + "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "radio-button-group", + "data": { + "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "RadioButton.Group", + "methods": [], + "statics": [], + "props": { + "onValueChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: string) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the currently selected radio button." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing radio buttons." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonGroup.tsx" + ], + "group": "RadioButton" + }, + "RadioButton/RadioButtonIOS": { + "filepath": "RadioButton/RadioButtonIOS.tsx", + "title": "RadioButton.IOS", + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "link": "radio-button-ios", + "data": { + "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", + "displayName": "RadioButton.IOS", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button" + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonIOS.tsx" + ], + "group": "RadioButton" + }, + "RadioButton/RadioButtonItem": { + "filepath": "RadioButton/RadioButtonItem.tsx", + "title": "RadioButton.Item", + "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "radio-button-item", + "data": { + "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "RadioButton.Item", + "methods": [], + "statics": [], + "props": { + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the radio button." + }, + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Label to be displayed on the item." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether radio is disabled." + }, + "background": { + "required": false, + "tsType": { + "name": "PressableAndroidRippleConfig" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", + "defaultValue": { + "value": "label", + "computed": true + } + }, + "uncheckedColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for unchecked radio." + }, + "color": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom color for radio." + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of radio button." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Additional styles for container View." + }, + "labelStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style that is passed to Label element." + }, + "labelVariant": { + "required": false, + "tsType": { + "name": "TypescaleKey" + }, + "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", + "defaultValue": { + "value": "'bodyLarge'", + "computed": false + } + }, + "labelMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a label font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'android' | 'ios'", + "elements": [ + { + "name": "literal", + "value": "'android'" + }, + { + "name": "literal", + "value": "'ios'" + } + ] + }, + "description": "Whether `` or `` should be used.\nLeft undefined `` will be used." + }, + "position": { + "required": false, + "tsType": { + "name": "union", + "raw": "'leading' | 'trailing'", + "elements": [ + { + "name": "literal", + "value": "'leading'" + }, + { + "name": "literal", + "value": "'trailing'" + } + ] + }, + "description": "Radio button control position.", + "defaultValue": { + "value": "'trailing'", + "computed": false + } + }, + "hitSlop": { + "required": false, + "tsType": { + "name": "TouchableRippleProps['hitSlop']", + "raw": "TouchableRippleProps['hitSlop']" + }, + "description": "Sets additional distance outside of element in which a press can be detected." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/RadioButton/RadioButtonItem.tsx" + ], + "group": "RadioButton" + }, + "Searchbar": { + "filepath": "Searchbar.tsx", + "title": "Searchbar", + "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", + "link": "searchbar", + "data": { + "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", + "displayName": "Searchbar", + "methods": [], + "statics": [], + "props": { + "placeholder": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Hint text shown when the input is empty." + }, + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "The value of the text input." + }, + "onChangeText": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(query: string) => void", + "signature": { + "arguments": [ + { + "name": "query", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback that is called when the text input's text changes." + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'bar' | 'view'", + "elements": [ + { + "name": "literal", + "value": "'bar'" + }, + { + "name": "literal", + "value": "'view'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nSearch layout mode, the default value is \"bar\".", + "defaultValue": { + "value": "'bar'", + "computed": false + } + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon name for the left icon button (see `onIconPress`)." + }, + "iconColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Custom color for icon, default will be derived from theme" + }, + "onIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback to execute if we want the left icon to act as button." + }, + "onClearIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback to execute if we want to add custom behaviour to close icon button." + }, + "searchAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'search'", + "computed": false + } + }, + "clearIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Custom icon for clear button, default will be icon close. It's visible when `loading` is set to `false`.\nIn v5.x with theme version 3, `clearIcon` is visible only if `right` prop is not defined." + }, + "clearAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'clear'", + "computed": false + } + }, + "traileringIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@supported Available in v5.x with theme version 3\nIcon name for the right trailering icon button.\nWorks only when `mode` is set to \"bar\". It won't be displayed if `loading` is set to `true`." + }, + "traileringIconColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "@supported Available in v5.x with theme version 3\nCustom color for the right trailering icon, default will be derived from theme" + }, + "onTraileringIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Callback to execute on the right trailering icon button press." + }, + "traileringIconAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the right trailering icon button. This is read by the screen reader when the user taps the button." + }, + "right": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: {\n color: ColorValue;\n style: Style;\n testID: string;\n}) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "signature", + "type": "object", + "raw": "{\n color: ColorValue;\n style: Style;\n testID: string;\n}", + "signature": { + "properties": [ + { + "key": "color", + "value": { + "name": "ColorValue", + "required": true + } + }, + { + "key": "style", + "value": { + "name": "Style", + "required": true + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "@supported Available in v5.x with theme version 3\nCallback which returns a React element to display on the right side.\nWorks only when `mode` is set to \"bar\"." + }, + "showDivider": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@supported Available in v5.x with theme version 3\nWhether to show `Divider` at the bottom of the search.\nWorks only when `mode` is set to \"view\". True by default.", + "defaultValue": { + "value": "true", + "computed": false + } + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges Searchbar shadow and background on iOS and Android.", + "defaultValue": { + "value": "0", + "computed": false + } + }, + "inputStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Set style of the TextInput component inside the searchbar" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "loading": { + "required": false, + "tsType": { + "name": "Boolean" + }, + "description": "Custom flag for replacing clear button with activity indicator.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'search-bar'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "Pick", + "elements": [ + { + "name": "TextInput" + }, + { + "name": "union", + "raw": "'setNativeProps' | 'isFocused' | 'clear' | 'blur' | 'focus' | 'setSelection'", + "elements": [ + { + "name": "literal", + "value": "'setNativeProps'" + }, + { + "name": "literal", + "value": "'isFocused'" + }, + { + "name": "literal", + "value": "'clear'" + }, + { + "name": "literal", + "value": "'blur'" + }, + { + "name": "literal", + "value": "'focus'" + }, + { + "name": "literal", + "value": "'setSelection'" + } + ] + } + ], + "raw": "Pick<\n TextInput,\n 'setNativeProps' | 'isFocused' | 'clear' | 'blur' | 'focus' | 'setSelection'\n>" + } + ] + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Searchbar.tsx" + ] + }, + "SegmentedButtons/SegmentedButtons": { + "filepath": "SegmentedButtons/SegmentedButtons.tsx", + "title": "SegmentedButtons", + "description": "Segmented buttons can be used to select options, switch views or sort elements.
\n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", + "link": "segmented-buttons", + "data": { + "description": "Segmented buttons can be used to select options, switch views or sort elements.
\n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "SegmentedButtons", + "methods": [], + "statics": [], + "props": { + "buttons": { + "required": true, + "tsType": { + "name": "Array", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}", + "signature": { + "properties": [ + { + "key": "value", + "value": { + "name": "T", + "required": true + } + }, + { + "key": "icon", + "value": { + "name": "IconSource", + "required": false + } + }, + { + "key": "disabled", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "accessibilityLabel", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "checkedColor", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "uncheckedColor", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "onPress", + "value": { + "name": "signature", + "type": "function", + "raw": "(event: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "event", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + }, + "required": false + } + }, + { + "key": "label", + "value": { + "name": "string", + "required": false + } + }, + { + "key": "showSelectedCheck", + "value": { + "name": "boolean", + "required": false + } + }, + { + "key": "style", + "value": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp", + "required": false + } + }, + { + "key": "labelStyle", + "value": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp", + "required": false + } + }, + { + "key": "testID", + "value": { + "name": "string", + "required": false + } + } + ] + } + } + ], + "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}[]" + }, + "description": "Buttons to display as options in toggle button.\nButton should contain the following properties:\n- `value`: value of button (required)\n- `icon`: icon to display for the item\n- `disabled`: whether the button is disabled\n- `accessibilityLabel`: acccessibility label for the button. This is read by the screen reader when the user taps the button.\n- `checkedColor`: custom color for checked Text and Icon\n- `uncheckedColor`: custom color for unchecked Text and Icon\n- `onPress`: callback that is called when button is pressed\n- `label`: label text of the button\n- `showSelectedCheck`: show optional check icon to indicate selected state\n- `style`: pass additional styles for the button\n- `testID`: testID to be used on tests" + }, + "density": { + "required": false, + "tsType": { + "name": "union", + "raw": "'regular' | 'small' | 'medium' | 'high'", + "elements": [ + { + "name": "literal", + "value": "'regular'" + }, + { + "name": "literal", + "value": "'small'" + }, + { + "name": "literal", + "value": "'medium'" + }, + { + "name": "literal", + "value": "'high'" + } + ] + }, + "description": "Density is applied to the height, to allow usage in denser UIs" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/SegmentedButtons/SegmentedButtons.tsx" + ] + }, + "Snackbar": { + "filepath": "Snackbar.tsx", + "title": "Snackbar", + "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", + "link": "snackbar", + "data": { + "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", + "displayName": "Snackbar", + "methods": [], + "statics": [], + "props": { + "visible": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "Whether the Snackbar is currently visible." + }, + "action": { + "required": false, + "tsType": { + "name": "intersection", + "raw": "$RemoveChildren & {\n label: string;\n}", + "elements": [ + { + "name": "$RemoveChildren", + "elements": [ + { + "name": "Button" + } + ], + "raw": "$RemoveChildren" + }, + { + "name": "signature", + "type": "object", + "raw": "{\n label: string;\n}", + "signature": { + "properties": [ + { + "key": "label", + "value": { + "name": "string", + "required": true + } + } + ] + } + } + ] + }, + "description": "Label and press callback for the action button. It should contain the following properties:\n- `label` - Label of the action button\n- `onPress` - Callback that is called when action button is pressed." + }, + "icon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "@supported Available in v5.x with theme version 3\nIcon to display when `onIconPress` is defined. Default will be `close` icon." + }, + "onIconPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on icon button press. The icon button appears only when this prop is specified." + }, + "iconAccessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "@supported Available in v5.x with theme version 3\nAccessibility label for the icon button. This is read by the screen reader when the user taps the button.", + "defaultValue": { + "value": "'Close icon'", + "computed": false + } + }, + "duration": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The duration for which the Snackbar is shown.", + "defaultValue": { + "value": "7000", + "computed": false + } + }, + "onDismiss": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "() => void", + "signature": { + "arguments": [], + "return": { + "name": "void" + } + } + }, + "description": "Callback called when Snackbar is dismissed. The `visible` prop needs to be updated when this is called." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Text content of the Snackbar." + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", + "elements": [ + { + "name": "literal", + "value": "0" + }, + { + "name": "literal", + "value": "1" + }, + { + "name": "literal", + "value": "2" + }, + { + "name": "literal", + "value": "3" + }, + { + "name": "literal", + "value": "4" + }, + { + "name": "literal", + "value": "5" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges Snackbar shadow and background on iOS and Android.", + "defaultValue": { + "value": "2", + "computed": false + } + }, + "maxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a text font can reach." + }, + "wrapperStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the wrapper of the snackbar" + }, + "contentStyle": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "Style for the content of the snackbar" + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Snackbar.tsx" + ] + }, + "Surface": { + "filepath": "Surface.tsx", + "title": "Surface", + "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", + "link": "surface", + "data": { + "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", + "displayName": "Surface", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Surface`." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "elevation": { + "required": false, + "tsType": { + "name": "union", + "raw": "Elevation | Animated.Value", + "elements": [ + { + "name": "Elevation" + }, + { + "name": "Animated.Value" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nChanges shadows and background on iOS and Android.\nUsed to create UI hierarchy between components.\n\nNote: If `mode` is set to `flat`, Surface doesn't have a shadow.\n\nNote: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`.\nIt's no longer supported with theme version 3 and you should use `elevation` property instead.", + "defaultValue": { + "value": "1", + "computed": false + } + }, + "mode": { + "required": false, + "tsType": { + "name": "union", + "raw": "'flat' | 'elevated'", + "elements": [ + { + "name": "literal", + "value": "'flat'" + }, + { + "name": "literal", + "value": "'elevated'" + } + ] + }, + "description": "@supported Available in v5.x with theme version 3\nMode of the Surface.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.", + "defaultValue": { + "value": "'elevated'", + "computed": false + } + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "TestID used for testing purposes", + "defaultValue": { + "value": "'surface'", + "computed": false + } + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "container": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "@internal" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Surface.tsx" + ] + }, + "Switch/Switch": { + "filepath": "Switch/Switch.tsx", + "title": "Switch", + "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track + icon / selected handle\n- `primaryContainer`: selected handle on hover, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", + "link": "switch", + "data": { + "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track + icon / selected handle\n- `primaryContainer`: selected handle on hover, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", + "displayName": "Switch", + "methods": [], + "statics": [], + "props": { + "value": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the switch is on." + }, + "onValueChange": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: boolean) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "boolean" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Called with the new value when the user toggles the switch." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Disables interaction and renders the disabled visual state." + }, + "checkedIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon shown inside the handle when checked" + }, + "uncheckedIcon": { + "required": false, + "tsType": { + "name": "IconSource" + }, + "description": "Icon shown inside the handle when unchecked." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Switch/Switch.tsx" + ] + }, + "TextInput/TextInput": { + "filepath": "TextInput/TextInput.tsx", + "title": "TextInput", + "description": "A text input lets users enter and edit text. It shows an optional floating label,\nsupports `filled` and `outlined` variants, optional supporting text (including\nerror state), and start/end accessories.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (accessoryProps) => (\n \n );\n\n const clearAccessory = ({ style, disabled }) => (\n setText('')}\n role=\"button\"\n aria-label=\"Clear text\"\n >\n \n \n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", + "link": "text-input", + "data": { + "description": "A text input lets users enter and edit text. It shows an optional floating label,\nsupports `filled` and `outlined` variants, optional supporting text (including\nerror state), and start/end accessories.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (accessoryProps) => (\n \n );\n\n const clearAccessory = ({ style, disabled }) => (\n setText('')}\n role=\"button\"\n aria-label=\"Clear text\"\n >\n \n \n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", + "displayName": "TextInput", + "methods": [], + "statics": [], + "props": { + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "Pick", + "elements": [ + { + "name": "NativeTextInput" + }, + { + "name": "union", + "raw": "'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps' | 'setSelection'", + "elements": [ + { + "name": "literal", + "value": "'focus'" + }, + { + "name": "literal", + "value": "'clear'" + }, + { + "name": "literal", + "value": "'blur'" + }, + { + "name": "literal", + "value": "'isFocused'" + }, + { + "name": "literal", + "value": "'setNativeProps'" + }, + { + "name": "literal", + "value": "'setSelection'" + } + ] + } + ], + "raw": "Pick<\n NativeTextInput,\n 'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps' | 'setSelection'\n>" + } + ] + }, + "description": "Imperative handle exposing a subset of native `TextInput` methods\nwith side-effect handling (e.g. `clear()` syncs internal state and animations)." + }, + "variant": { + "required": false, + "tsType": { + "name": "union", + "raw": "'filled' | 'outlined'", + "elements": [ + { + "name": "literal", + "value": "'filled'" + }, + { + "name": "literal", + "value": "'outlined'" + } + ] + }, + "description": "Determines the visual style of the text input.\n\n- `filled` — filled background with an animated underline; higher visual emphasis.\n- `outlined` — stroke outline only; lower visual emphasis." + }, + "error": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "When `true`, the field uses error styling and replaces the trailing accessory\nwith an error indicator when no `endAccessory` is provided." + }, + "label": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "The label text to display above the input." + }, + "supportingText": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Supporting text to display below the input (Material Design 3)." + }, + "counter": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "When `true`, displays a character counter below the input on the trailing\nside, showing `currentLength/maxLength`. Requires `maxLength` to be set." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "This is separate from `editable={false}`, which makes the text read-only while the\ninput can still be focused and text selected." + }, + "prefix": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "A short text string displayed at the start of the input (e.g. `\"$\"`)." + }, + "suffix": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "A short text string displayed at the end of the input (e.g. `\"/100\"`)." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "startAccessory": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: TextInputAccessoryProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "TextInputAccessoryProps" + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "An optional component to render on the start side of the input (leading in LTR).\nCan be a custom component or `TextInput.Icon`." + }, + "endAccessory": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: TextInputAccessoryProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "TextInputAccessoryProps" + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "An optional component to render on the end side of the input (trailing in LTR).\nCan be a custom component or `TextInput.Icon`." + }, + "render": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(props: TextInputRenderProps) => React.ReactNode", + "signature": { + "arguments": [ + { + "name": "props", + "type": { + "name": "intersection", + "raw": "React.ComponentPropsWithoutRef<\n typeof NativeTextInput\n> & {\n ref?: React.RefObject;\n}", + "elements": [ + { + "name": "ReactComponentPropsWithoutRef", + "raw": "React.ComponentPropsWithoutRef<\n typeof NativeTextInput\n>", + "elements": [ + { + "name": "NativeTextInput" + } + ] + }, + { + "name": "signature", + "type": "object", + "raw": "{\n ref?: React.RefObject;\n}", + "signature": { + "properties": [ + { + "key": "ref", + "value": { + "name": "ReactRefObject", + "raw": "React.RefObject", + "elements": [ + { + "name": "union", + "raw": "NativeTextInput | null", + "elements": [ + { + "name": "NativeTextInput" + }, + { + "name": "null" + } + ] + } + ], + "required": false + } + } + ] + } + } + ] + } + } + ], + "return": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + } + }, + "description": "Callback to render a custom input component in place of the native `TextInput`.\nReceives all props that would be passed to `TextInput`, allowing integration\nwith third-party inputs such as masked inputs." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/TextInput/TextInput.tsx" + ] + }, + "TextInput/TextInputIcon": { + "filepath": "TextInput/TextInputIcon.tsx", + "title": "TextInput.Icon", + "description": "A component to render a leading / trailing icon in the TextInput\n(return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as\n`TextInputAccessoryProps`, which TextInput passes into those render props.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (props) => (\n \n );\n\n const clearAccessory = (props) => (\n setText('')} />\n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends IconButton props https://callstack.github.io/react-native-paper/docs/components/IconButton", + "link": "text-input-icon", + "data": { + "description": "A component to render a leading / trailing icon in the TextInput\n(return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as\n`TextInputAccessoryProps`, which TextInput passes into those render props.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (props) => (\n \n );\n\n const clearAccessory = (props) => (\n setText('')} />\n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends IconButton props https://callstack.github.io/react-native-paper/docs/components/IconButton", + "displayName": "TextInput.Icon", + "methods": [], + "statics": [], + "props": { + "style": { + "required": true, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "multiline": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "" + }, + "disabled": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "" + }, + "error": { + "required": true, + "tsType": { + "name": "boolean" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/TextInput/TextInputIcon.tsx" + ], + "group": "TextInput" + }, + "ToggleButton/ToggleButton": { + "filepath": "ToggleButton/ToggleButton.tsx", + "title": "ToggleButton", + "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", + "link": "toggle-button", + "data": { + "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", + "displayName": "ToggleButton", + "methods": [], + "statics": [], + "props": { + "icon": { + "required": true, + "tsType": { + "name": "IconSource" + }, + "description": "Icon to display for the `ToggleButton`." + }, + "size": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Size of the icon." + }, + "iconColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Custom text color for button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether the button is disabled." + }, + "accessibilityLabel": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value?: GestureResponderEvent | string) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "union", + "raw": "GestureResponderEvent | string", + "elements": [ + { + "name": "GestureResponderEvent" + }, + { + "name": "string" + } + ] + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press." + }, + "value": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Value of button." + }, + "status": { + "required": false, + "tsType": { + "name": "union", + "raw": "'checked' | 'unchecked'", + "elements": [ + { + "name": "literal", + "value": "'checked'" + }, + { + "name": "literal", + "value": "'unchecked'" + } + ] + }, + "description": "Status of button." + }, + "style": { + "required": false, + "tsType": { + "name": "Animated.WithAnimatedValue", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + } + ], + "raw": "Animated.WithAnimatedValue>" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "testID": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "testID to be used on tests." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ToggleButton/ToggleButton.tsx" + ] + }, + "ToggleButton/ToggleButtonGroup": { + "filepath": "ToggleButton/ToggleButtonGroup.tsx", + "title": "ToggleButton.Group", + "description": "Toggle group allows to control a group of toggle buttons.
\nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "link": "toggle-button-group", + "data": { + "description": "Toggle group allows to control a group of toggle buttons.
\nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "displayName": "ToggleButton.Group", + "methods": [], + "statics": [], + "props": { + "onValueChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: Value) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "Value" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "value": { + "required": true, + "tsType": { + "name": "union", + "raw": "Value | null", + "elements": [ + { + "name": "Value" + }, + { + "name": "null" + } + ] + }, + "description": "Value of the currently selected toggle button." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing toggle buttons." + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ToggleButton/ToggleButtonGroup.tsx" + ], + "group": "ToggleButton" + }, + "ToggleButton/ToggleButtonRow": { + "filepath": "ToggleButton/ToggleButtonRow.tsx", + "title": "ToggleButton.Row", + "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", + "link": "toggle-button-row", + "data": { + "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", + "displayName": "ToggleButton.Row", + "methods": [], + "statics": [], + "props": { + "onValueChange": { + "required": true, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(value: string) => void", + "signature": { + "arguments": [ + { + "name": "value", + "type": { + "name": "string" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on selection change." + }, + "value": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Value of the currently selected toggle button." + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "React elements containing toggle buttons." + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/ToggleButton/ToggleButtonRow.tsx" + ], + "group": "ToggleButton" + }, + "Tooltip/Tooltip": { + "filepath": "Tooltip/Tooltip.tsx", + "title": "Tooltip", + "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "link": "tooltip", + "data": { + "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", + "displayName": "Tooltip", + "methods": [], + "statics": [], + "props": { + "children": { + "required": true, + "tsType": { + "name": "ReactReactElement", + "raw": "React.ReactElement" + }, + "description": "Tooltip reference element. Needs to be able to hold a ref." + }, + "enterTouchDelay": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds a user must touch the element before showing the tooltip.", + "defaultValue": { + "value": "500", + "computed": false + } + }, + "leaveTouchDelay": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "The number of milliseconds after the user stops touching an element before hiding the tooltip.", + "defaultValue": { + "value": "1500", + "computed": false + } + }, + "title": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Tooltip title" + }, + "titleMaxFontSizeMultiplier": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Specifies the largest possible scale a title font can reach." + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Tooltip/Tooltip.tsx" + ] + }, + "TouchableRipple/TouchableRipple": { + "filepath": "TouchableRipple/TouchableRipple.tsx", + "title": "TouchableRipple", + "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n \n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", + "link": "touchable-ripple", + "data": { + "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n \n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", + "displayName": "TouchableRipple", + "methods": [], + "statics": [], + "props": { + "borderless": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to render the ripple outside the view bounds.", + "defaultValue": { + "value": "false", + "computed": false + } + }, + "background": { + "required": false, + "tsType": { + "name": "Object" + }, + "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" + }, + "centered": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to start the ripple at the center (Web)." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Whether to prevent interaction with the touchable." + }, + "onPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on press. If not set, will cause the touchable to be disabled." + }, + "onLongPress": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute on long press." + }, + "onPressIn": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute immediately when a touch is engaged, before `onPressOut` and `onPress`." + }, + "onPressOut": { + "required": false, + "tsType": { + "name": "signature", + "type": "function", + "raw": "(e: GestureResponderEvent) => void", + "signature": { + "arguments": [ + { + "name": "e", + "type": { + "name": "GestureResponderEvent" + } + } + ], + "return": { + "name": "void" + } + } + }, + "description": "Function to execute when a touch is released." + }, + "rippleColor": { + "required": false, + "tsType": { + "name": "ColorValue" + }, + "description": "Color of the ripple effect (Android >= 5.0 and Web)." + }, + "underlayColor": { + "required": false, + "tsType": { + "name": "string" + }, + "description": "Color of the underlay for the highlight effect (Android < 5.0 and iOS)." + }, + "children": { + "required": true, + "tsType": { + "name": "union", + "raw": "| ((state: PressableStateCallbackType) => React.ReactNode)\n| React.ReactNode", + "elements": [ + { + "name": "unknown" + }, + { + "name": "ReactReactNode", + "raw": "React.ReactNode" + } + ] + }, + "description": "Content of the `TouchableRipple`." + }, + "style": { + "required": false, + "tsType": { + "name": "union", + "raw": "| StyleProp\n| ((state: PressableStateCallbackType) => StyleProp)\n| undefined", + "elements": [ + { + "name": "StyleProp", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "StyleProp" + }, + { + "name": "unknown" + }, + { + "name": "undefined" + } + ] + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/TouchableRipple/TouchableRipple.tsx" + ] + }, + "Typography/Text": { + "filepath": "Typography/Text.tsx", + "title": "Text", + "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", + "link": "text", + "data": { + "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", + "displayName": "Text", + "methods": [], + "statics": [], + "props": { + "variant": { + "required": false, + "tsType": { + "name": "VariantProp", + "elements": [ + { + "name": "T" + } + ], + "raw": "VariantProp" + }, + "description": "@supported Available in v5.x with theme version 3\n\nVariant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`" + }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "" + }, + "theme": { + "required": false, + "tsType": { + "name": "ThemeProp" + }, + "description": "" + }, + "style": { + "required": false, + "tsType": { + "name": "StyleProp", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "StyleProp" + }, + "description": "" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "signature", + "type": "object", + "raw": "{\n setNativeProps(args: Object): void;\n}", + "signature": { + "properties": [ + { + "key": "setNativeProps", + "value": { + "name": "void", + "required": true + } + } + ] + } + } + ] + }, + "description": "" + } + } + }, + "type": "component", + "dependencies": [ + "src/components/Typography/Text.tsx" + ] + } + } +} diff --git a/docs/src/data/extendedExamples.js b/docs/src/data/extendedExamples.ts similarity index 54% rename from docs/src/data/extendedExamples.js rename to docs/src/data/extendedExamples.ts index f7b196afff..20800e2005 100644 --- a/docs/src/data/extendedExamples.js +++ b/docs/src/data/extendedExamples.ts @@ -1,9 +1,9 @@ -const { - staticCode, +import { dynamicCode, -} = require('./extendedExamples/BottomNavigationBar'); + staticCode, +} from './extendedExamples/BottomNavigationBar.ts'; -const extendedExamples = { +export const extendedExamples = { 'BottomNavigation.Bar': { 'with React Navigation': { static: staticCode, @@ -11,7 +11,3 @@ const extendedExamples = { }, }, }; - -module.exports = { - extendedExamples, -}; diff --git a/docs/src/data/extendedExamples/BottomNavigationBar.js b/docs/src/data/extendedExamples/BottomNavigationBar.ts similarity index 96% rename from docs/src/data/extendedExamples/BottomNavigationBar.js rename to docs/src/data/extendedExamples/BottomNavigationBar.ts index 4a5cd4bf24..7a49db0bce 100644 --- a/docs/src/data/extendedExamples/BottomNavigationBar.js +++ b/docs/src/data/extendedExamples/BottomNavigationBar.ts @@ -1,4 +1,4 @@ -const staticCode = `import { Text, View } from 'react-native'; +export const staticCode = `import { Text, View } from 'react-native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Provider, BottomNavigation } from 'react-native-paper'; import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; @@ -99,7 +99,7 @@ export default function App() { ); }`; -const dynamicCode = `import { Text, View } from 'react-native'; +export const dynamicCode = `import { Text, View } from 'react-native'; import { NavigationContainer, CommonActions } from '@react-navigation/native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { Provider, BottomNavigation } from 'react-native-paper'; @@ -195,8 +195,3 @@ export default function App() { ); } `; - -module.exports = { - staticCode, - dynamicCode, -}; diff --git a/docs/src/data/screenshots.js b/docs/src/data/screenshots.ts similarity index 99% rename from docs/src/data/screenshots.js rename to docs/src/data/screenshots.ts index f25cef6294..92bf8f2788 100644 --- a/docs/src/data/screenshots.js +++ b/docs/src/data/screenshots.ts @@ -1,4 +1,4 @@ -const screenshots = { +export const screenshots = { ActivityIndicator: 'screenshots/activity-indicator.gif', Appbar: 'screenshots/appbar.png', 'Appbar.Action': 'screenshots/appbar-action-android.png', @@ -155,7 +155,3 @@ const screenshots = { Tooltip: 'screenshots/tooltip.png', TouchableRipple: 'screenshots/touchable-ripple.gif', }; - -module.exports = { - screenshots, -}; diff --git a/docs/src/data/themeColors.js b/docs/src/data/themeColors.ts similarity index 99% rename from docs/src/data/themeColors.js rename to docs/src/data/themeColors.ts index 2e97dd1bce..20f16962f7 100644 --- a/docs/src/data/themeColors.js +++ b/docs/src/data/themeColors.ts @@ -1,4 +1,4 @@ -const themeColors = { +export const themeColors = { ActivityIndicator: { '-': { borderColor: 'theme.colors.primary', @@ -327,7 +327,3 @@ const themeColors = { }, }, }; - -module.exports = { - themeColors, -}; diff --git a/docs/src/data/versionRouteFallbacks.json b/docs/src/data/versionRouteFallbacks.json new file mode 100644 index 0000000000..326741b5b5 --- /dev/null +++ b/docs/src/data/versionRouteFallbacks.json @@ -0,0 +1,17 @@ +{ + "next": { + "/docs/components/Checkbox/CheckboxAndroid": "/6.x/docs/components/Checkbox/Checkbox", + "/docs/components/Checkbox/CheckboxIOS": "/6.x/docs/components/Checkbox/Checkbox", + "/docs/components/FAB/AnimatedFAB": "/6.x/docs/components/FAB/FAB", + "/docs/components/FAB/FABGroup": "/6.x/docs/components/FAB/FAB", + "/docs/components/HelperText/HelperText": "/6.x/", + "/docs/components/TextInput/TextInputAffix": "/6.x/docs/components/TextInput/TextInput", + "/docs/guides/migration-guide-to-5.0": "/6.x/docs/guides/migration" + }, + "stable": { + "/6.x/docs/components/FAB/FABExtended": "/docs/components/FAB/FAB", + "/6.x/docs/components/FAB/FABMenu": "/docs/components/FAB/FAB", + "/6.x/docs/guides/migration": "/docs/guides/migration-guide-to-5.0", + "/6.x/docs/guides/rtl": "/" + } +} diff --git a/docs/src/pages/index.mdx b/docs/src/pages/index.mdx index 2870fda5b7..91f12670f9 100644 --- a/docs/src/pages/index.mdx +++ b/docs/src/pages/index.mdx @@ -1,63 +1,38 @@ -![paperLogo](../../static/images/sidebar-logo.svg) - -# Cross-platform Material Design for React Native - -import {Button} from 'react-native-paper'; -import GetStartedButtons from '../components/GetStartedButtons'; -import BannerExample from '../components/BannerExample'; - -Paper is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. - - - -Or check the demo app on [iOS](https://apps.apple.com/app/react-native-paper/id1548934513) or [Android](https://play.google.com/store/apps/details?id=com.callstack.reactnativepaperexample). - - - - - - - - - - - - - - - - - - +--- +pageType: home +hero: + name: Cross-platform Material Design for React Native + tagline: Paper is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. + actions: + - text: Get started + link: /docs/guides/getting-started + theme: brand + - text: Try on Snack + link: https://snack.expo.dev/@react-native-paper/react-native-paper-example_v5 + theme: alt +features: + - title: Production-ready components + details: Buttons, inputs, lists, cards, menus, dialogs, navigation, and feedback patterns built for day-to-day product work. + icon: /react-native-paper/images/home/components.svg + span: 4 + - title: Material You theming + details: Adopt Material 3 color, typography, elevation, and state layers with a theme system designed for real apps. + icon: /react-native-paper/images/home/palette.svg + span: 4 + - title: Accessible by default + details: Common interactions, contrast, and state handling are already baked into the component surface instead of left to each screen. + icon: /react-native-paper/images/home/accessibility.svg + span: 4 + - title: Cross-platform parity + details: Keep design language and component behavior aligned across iOS, Android, and React Native Web from one codebase. + icon: /react-native-paper/images/home/devices.svg + span: 4 + - title: Dark mode support + details: Ship light and dark experiences with the same primitives, including adaptive surfaces and semantic color roles. + icon: /react-native-paper/images/home/moon.svg + span: 4 + - title: Migration-friendly docs + details: Browse stable 5.x, preview 6.x, and legacy static docs from one version selector without splitting the docs experience. + icon: /react-native-paper/images/home/versions.svg + span: 4 +--- diff --git a/docs/src/utils/themeColors.tsx b/docs/src/utils/themeColors.tsx index dcc099ec58..befc487d80 100644 --- a/docs/src/utils/themeColors.tsx +++ b/docs/src/utils/themeColors.tsx @@ -23,9 +23,9 @@ export const getUniqueNestedKeys = (data: DataObject): string[] => { export const getMaxNestedLevel = (obj: DataObject): number => { let maxNestedLevel = 0; - for (const key in obj) { - if (typeof obj[key] === 'object') { - const nestedLevel = getMaxNestedLevel(obj[key] as DataObject) + 1; + for (const value of Object.values(obj)) { + if (typeof value === 'object') { + const nestedLevel = getMaxNestedLevel(value) + 1; maxNestedLevel = Math.max(maxNestedLevel, nestedLevel); } } diff --git a/docs/src/utils/themes.tsx b/docs/src/utils/themes.tsx index 1430d38830..182298a21d 100644 --- a/docs/src/utils/themes.tsx +++ b/docs/src/utils/themes.tsx @@ -6,7 +6,6 @@ import camelCase from 'camelcase'; import Color from 'color'; type ARGBTheme = ReturnType; -type CSSTheme = Record; type RGBColorList = { primary: string; @@ -46,22 +45,22 @@ const argbThemeFromColor = ( return themeFromSourceColor(argbFromHex(color)).schemes[type].toJSON(); }; +const hexFromArgb = (value: number) => + `#${(value & 0xffffff).toString(16).padStart(6, '0')}`; + const argbThemeToHexTheme = (theme: ARGBTheme) => { return Object.fromEntries( - //@ts-ignore - Object.entries(theme).map(([key, value]) => [key, Color(value).hex()]) - ) as CSSTheme; + Object.entries(theme).map(([key, value]) => [key, hexFromArgb(value)]) + ); }; const argbThemeToRgbTheme = (theme: ARGBTheme) => { return Object.fromEntries( - //@ts-ignore Object.entries(theme).map(([key, value]) => [ key, - //@ts-ignore - Color(value).rgb().string(), + Color(hexFromArgb(value)).rgb().string(), ]) - ) as CSSTheme; + ); }; export const hexThemeFromColor = ( diff --git a/docs/src/utils/versionRoutes.ts b/docs/src/utils/versionRoutes.ts new file mode 100644 index 0000000000..a20e6fe8e0 --- /dev/null +++ b/docs/src/utils/versionRoutes.ts @@ -0,0 +1,40 @@ +import routeFallbacks from '../data/versionRouteFallbacks.json'; + +const NEXT_VERSION_PREFIX = '/6.x'; +const NEXT_HOME_ROUTE = '/6.x/'; +const STABLE_HOME_ROUTE = '/'; +const stableFallbacks: Record = routeFallbacks.stable; +const nextFallbacks: Record = routeFallbacks.next; + +const stripNextVersionPrefix = (routePath: string) => { + const stableRoute = routePath.replace(/^\/6\.x(?=\/|$)/, ''); + + return stableRoute === '' ? STABLE_HOME_ROUTE : stableRoute; +}; + +export const getStableRoute = (routePath: string) => { + if (!routePath.startsWith(NEXT_VERSION_PREFIX)) { + return routePath || STABLE_HOME_ROUTE; + } + + if (routePath === NEXT_VERSION_PREFIX || routePath === NEXT_HOME_ROUTE) { + return STABLE_HOME_ROUTE; + } + + return stableFallbacks[routePath] ?? stripNextVersionPrefix(routePath); +}; + +export const getNextRoute = (routePath: string) => { + if (routePath.startsWith(NEXT_VERSION_PREFIX)) { + return routePath; + } + + if (routePath === STABLE_HOME_ROUTE || routePath === '') { + return NEXT_HOME_ROUTE; + } + + return nextFallbacks[routePath] ?? `${NEXT_VERSION_PREFIX}${routePath}`; +}; + +export const hasSameStableRoute = (routePath: string) => + getStableRoute(routePath) === stripNextVersionPrefix(routePath); diff --git a/docs/static/llms.txt b/docs/static/llms.txt deleted file mode 100644 index 3244326f67..0000000000 --- a/docs/static/llms.txt +++ /dev/null @@ -1,79 +0,0 @@ -# React Native Paper - -> Material Design components for React Native. -> See the full documentation at: https://callstack.github.io/react-native-paper/ - -## Getting Started - -- Install: npm install react-native-paper react-native-safe-area-context -- Wrap your app with PaperProvider -- Default theme is Material You (MD3) -- Requires React Native 0.72+ and React 18+ - -## Guides - -- Getting Started: https://callstack.github.io/react-native-paper/docs/guides/getting-started -- Theming: https://callstack.github.io/react-native-paper/docs/guides/theming -- Icons: https://callstack.github.io/react-native-paper/docs/guides/icons -- Fonts: https://callstack.github.io/react-native-paper/docs/guides/fonts -- React Native Web: https://callstack.github.io/react-native-paper/docs/guides/react-native-web -- Recommended Libraries: https://callstack.github.io/react-native-paper/docs/guides/recommended-libraries -- Contributing: https://callstack.github.io/react-native-paper/docs/guides/contributing -- Bottom Navigation: https://callstack.github.io/react-native-paper/docs/guides/bottom-navigation -- Ripple Effect: https://callstack.github.io/react-native-paper/docs/guides/ripple-effect - -## Components - -- ActivityIndicator: https://callstack.github.io/react-native-paper/docs/components/ActivityIndicator -- Appbar: https://callstack.github.io/react-native-paper/docs/components/Appbar -- Avatar: https://callstack.github.io/react-native-paper/docs/components/Avatar -- Badge: https://callstack.github.io/react-native-paper/docs/components/Badge -- Banner: https://callstack.github.io/react-native-paper/docs/components/Banner -- BottomNavigation: https://callstack.github.io/react-native-paper/docs/components/BottomNavigation -- Button: https://callstack.github.io/react-native-paper/docs/components/Button -- Card: https://callstack.github.io/react-native-paper/docs/components/Card -- Checkbox: https://callstack.github.io/react-native-paper/docs/components/Checkbox -- Chip: https://callstack.github.io/react-native-paper/docs/components/Chip -- DataTable: https://callstack.github.io/react-native-paper/docs/components/DataTable -- Dialog: https://callstack.github.io/react-native-paper/docs/components/Dialog -- Divider: https://callstack.github.io/react-native-paper/docs/components/Divider -- Drawer: https://callstack.github.io/react-native-paper/docs/components/Drawer -- FAB: https://callstack.github.io/react-native-paper/docs/components/FAB -- HelperText: https://callstack.github.io/react-native-paper/docs/components/HelperText -- IconButton: https://callstack.github.io/react-native-paper/docs/components/IconButton -- Icon: https://callstack.github.io/react-native-paper/docs/components/Icon -- List: https://callstack.github.io/react-native-paper/docs/components/List -- Menu: https://callstack.github.io/react-native-paper/docs/components/Menu -- Modal: https://callstack.github.io/react-native-paper/docs/components/Modal -- Portal: https://callstack.github.io/react-native-paper/docs/components/Portal -- ProgressBar: https://callstack.github.io/react-native-paper/docs/components/ProgressBar -- RadioButton: https://callstack.github.io/react-native-paper/docs/components/RadioButton -- Searchbar: https://callstack.github.io/react-native-paper/docs/components/Searchbar -- SegmentedButtons: https://callstack.github.io/react-native-paper/docs/components/SegmentedButtons -- Snackbar: https://callstack.github.io/react-native-paper/docs/components/Snackbar -- Surface: https://callstack.github.io/react-native-paper/docs/components/Surface -- Switch: https://callstack.github.io/react-native-paper/docs/components/Switch -- Text: https://callstack.github.io/react-native-paper/docs/components/Text -- TextInput: https://callstack.github.io/react-native-paper/docs/components/TextInput -- TouchableRipple: https://callstack.github.io/react-native-paper/docs/components/TouchableRipple -- Tooltip: https://callstack.github.io/react-native-paper/docs/components/Tooltip - -## Theming - -- Theme object accepts colors, dark, roundness, fonts, animation, and version properties -- Use useTheme() hook or ThemeProvider for nested theme overrides -- MD3 (Material You) is the only supported design system; extend MD3LightTheme / MD3DarkTheme -- See theming guide: https://callstack.github.io/react-native-paper/docs/guides/theming - -## Provider Pattern - -- Wrap root component with PaperProvider (required) -- PaperProvider accepts an optional theme prop -- Supports theming with React Navigation via react-native-paper theming integration - -## Important Notes - -- This is a cross-platform library (iOS, Android, web) -- Built with TypeScript -- MIT licensed -- Maintained by Callstack diff --git a/docs/theme/Outline.tsx b/docs/theme/Outline.tsx new file mode 100644 index 0000000000..ded2f49549 --- /dev/null +++ b/docs/theme/Outline.tsx @@ -0,0 +1,38 @@ +import { useI18n } from '@rspress/core/dist/runtime/index.js'; +import { + EditLink, + ReadPercent, + Toc, + useDynamicToc, + useEditLink, +} from '@rspress/core/dist/theme/index.js'; + +export default function Outline() { + const t = useI18n(); + const headers = useDynamicToc(); + const editLink = useEditLink(); + + if (headers.length === 0) { + return null; + } + + return ( +
+
+ {t('outlineTitle')} + +
+ + {editLink ? ( + <> +
+
+ +
+ + ) : null} +
+ ); +} diff --git a/docs/theme/index.tsx b/docs/theme/index.tsx new file mode 100644 index 0000000000..3cd7557c48 --- /dev/null +++ b/docs/theme/index.tsx @@ -0,0 +1,34 @@ +import { Fragment, type ComponentProps } from 'react'; + +import { HomeBanner } from '@callstack/rspress-theme'; +import { Layout as BasicLayout } from '@rspress/core/dist/theme/index.js'; + +import Outline from './Outline'; +import PaperHomeShowcase from '../src/components/PaperHomeShowcase'; +import PaperVersionSelector from '../src/components/PaperVersionSelector'; +import VersionedPrereleaseNotice from '../src/components/VersionedPrereleaseNotice'; + +type LayoutProps = ComponentProps; + +const Layout = (props: LayoutProps) => ( + + + + + } + afterNavMenu={} + afterOutline={false} + beforeDocContent={} + /> +); + +export * from '@rspress/core/dist/theme/index.js'; +export { Layout, Outline }; diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 56f834939e..3b8c64a185 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -2,7 +2,9 @@ "extends": "../tsconfig.json", "compilerOptions": { "paths": { - "react-native-paper": ["./src"] + "@docs/*": ["./src/*"], + "react-native-paper": ["../src/index"] } - } + }, + "include": ["**/*.ts", "**/*.tsx"] } diff --git a/docs/versioned_docs/version-5.x/components/Badge.mdx b/docs/versioned_docs/version-5.x/components/Badge.mdx deleted file mode 100644 index 564777604c..0000000000 --- a/docs/versioned_docs/version-5.x/components/Badge.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Badge ---- - -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; - -Badges are small status descriptors for UI elements. -A badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Badge } from 'react-native-paper'; - -const MyComponent = () => ( - 3 -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### visible - -
- - - -
- -### children - -
- - - -
- -### size - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigationBar.mdx b/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigationBar.mdx deleted file mode 100644 index 8859b3cfec..0000000000 --- a/docs/versioned_docs/version-5.x/components/BottomNavigation/BottomNavigationBar.mdx +++ /dev/null @@ -1,293 +0,0 @@ ---- -title: BottomNavigation.Bar ---- - -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; - -A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/). - - - - - - - - - ## Usage -### without React Navigation -```js -import React from 'react'; -import { useState } from 'react'; -import { View } from 'react-native'; -import { BottomNavigation, Text, Provider } from 'react-native-paper'; -import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'; - -function HomeScreen() { - return ( - - Home! - - ); -} - -function SettingsScreen() { - return ( - - Settings! - - ); -} - -export default function MyComponent() { - const [index, setIndex] = useState(0); - - const routes = [ - { key: 'home', title: 'Home', icon: 'home' }, - { key: 'settings', title: 'Settings', icon: 'cog' }, - ]; - - const renderScene = ({ route }) => { - switch (route.key) { - case 'home': - return ; - case 'settings': - return ; - default: - return null; - } - }; - - return ( - - {renderScene({ route: routes[index] })} - { - const newIndex = routes.findIndex((r) => r.key === route.key); - if (newIndex !== -1) { - setIndex(newIndex); - } - }} - renderIcon={({ route, color }) => ( - - )} - getLabelText={({ route }) => route.title} - /> - - ); -} -``` - - ### with React Navigation - \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst MyTabs = createBottomTabNavigator({\n screenOptions: {\n animation: 'shift',\n },\n tabBar: ({ navigation, state, descriptors, insets }) => (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n ),\n screens: {\n Home: {\n screen: HomeScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n Settings: {\n screen: SettingsScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n },\n});\n\nconst Navigation = createStaticNavigation(MyTabs);\n\nexport default function App() {\n return (\n \n \n \n \n \n );\n}","dynamic":"import { Text, View } from 'react-native';\nimport { NavigationContainer, CommonActions } from '@react-navigation/native';\nimport { createBottomTabNavigator } from '@react-navigation/bottom-tabs';\nimport { Provider, BottomNavigation } from 'react-native-paper';\nimport MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst Tab = createBottomTabNavigator();\n\nexport default function App() {\n return (\n \n \n (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n )}>\n (\n \n ),\n }}\n />\n (\n \n ),\n }}\n />\n \n \n \n );\n}\n"}}} /> - - - - ## Props - - - - -
- -### shifting - -
- - - -
- -### labeled - -
- - - -
- -### compact - -
- - - -
- -### navigationState (required) - -
- - - -
- -### renderIcon - -
- - - -
- -### renderLabel - -
- - - -
- -### renderTouchable - -
- - - -
- -### getAccessibilityLabel - -
- - - -
- -### getBadge - -
- - - -
- -### getColor - -
- - - -
- -### getLabelText - -
- - - -
- -### getTestID - -
- - - -
- -### onTabPress (required) - -
- - - -
- -### onTabLongPress - -
- - - -
- -### activeColor - -
- - - -
- -### inactiveColor - -
- - - -
- -### animationEasing - -
- - - -
- -### keyboardHidesNavigationBar - -
- - - -
- -### safeAreaInsets - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### activeIndicatorStyle - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/versioned_docs/version-5.x/components/Card/CardCover.mdx b/docs/versioned_docs/version-5.x/components/Card/CardCover.mdx deleted file mode 100644 index 01c0f5cd08..0000000000 --- a/docs/versioned_docs/version-5.x/components/Card/CardCover.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Card.Cover ---- - -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; - -A component to show a cover image inside a Card. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Card } from 'react-native-paper'; - -const MyComponent = () => ( - - - -); - -export default MyComponent; -``` - - - - - ## Props - ### Image props - - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxIOS.mdx b/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxIOS.mdx deleted file mode 100644 index f3096f3564..0000000000 --- a/docs/versioned_docs/version-5.x/components/Checkbox/CheckboxIOS.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Checkbox.IOS ---- - -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; - - - - - - - -Checkboxes allow the selection of multiple options from a set. -This component follows platform guidelines for iOS, but can be used -on any platform. - - - - - ## Props - ### TouchableRipple props - - - -
- -### status (required) - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/versioned_docs/version-5.x/components/DataTable/DataTableRow.mdx b/docs/versioned_docs/version-5.x/components/DataTable/DataTableRow.mdx deleted file mode 100644 index 4a16b262d5..0000000000 --- a/docs/versioned_docs/version-5.x/components/DataTable/DataTableRow.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: DataTable.Row ---- - -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; - -A component to show a single row inside of a table. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - 1 - 2 - 3 - 4 - -); - -export default MyComponent; -``` - - - - - ## Props - ### TouchableRipple props - - - -
- -### children (required) - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### pointerEvents - -
- - - - - - - - - - diff --git a/docs/versioned_docs/version-5.x/components/Divider.mdx b/docs/versioned_docs/version-5.x/components/Divider.mdx deleted file mode 100644 index ffe25229b4..0000000000 --- a/docs/versioned_docs/version-5.x/components/Divider.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Divider ---- - -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; - -A divider is a thin, lightweight separator that groups content in lists and page layouts. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Divider, Text } from 'react-native-paper'; - -const MyComponent = () => ( - - Lemon - - Mango - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### leftInset Renamed from 'inset' to 'leftInset` in v5.x - -
- - - -
- -### horizontalInset Available in v5.x with theme version 3 - -
- - - -
- -### bold Available in v5.x with theme version 3 - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/versioned_docs/version-5.x/components/IconButton/IconButton.mdx b/docs/versioned_docs/version-5.x/components/IconButton/IconButton.mdx deleted file mode 100644 index 440a96bc59..0000000000 --- a/docs/versioned_docs/version-5.x/components/IconButton/IconButton.mdx +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: IconButton ---- - -import PropTable from '@site/src/components/PropTable.tsx'; -import ExtendsLink from '@site/src/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@site/src/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@site/src/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@site/src/components/ExtendedExample.tsx'; - -An icon button is a button which displays only an icon without a label. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { IconButton, MD3Colors } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')} - /> -); - -export default MyComponent; -``` - - - - - ## Props - ### TouchableRipple props - - - -
- -### icon (required) - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### iconColor Renamed from 'color' to 'iconColor' in v5.x - -
- - - -
- -### containerColor - -
- - - -
- -### rippleColor - -
- - - -
- -### selected Available in v5.x with theme version 3 - -
- - - -
- -### size - -
- - - -
- -### disabled - -
- - - -
- -### animated - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### contentStyle - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - -
- -### loading - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/versioned_docs/version-5.x/components/_category_.json b/docs/versioned_docs/version-5.x/components/_category_.json deleted file mode 100644 index c1ea2a2a75..0000000000 --- a/docs/versioned_docs/version-5.x/components/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Components" -} \ No newline at end of file diff --git a/docs/versioned_docs/version-5.x/guides/_category_.json b/docs/versioned_docs/version-5.x/guides/_category_.json deleted file mode 100644 index 82c9b69949..0000000000 --- a/docs/versioned_docs/version-5.x/guides/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Guides", - "position": 0 -} diff --git a/docs/versioned_docs/version-6.x/components/_category_.json b/docs/versioned_docs/version-6.x/components/_category_.json deleted file mode 100644 index c1ea2a2a75..0000000000 --- a/docs/versioned_docs/version-6.x/components/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Components" -} \ No newline at end of file diff --git a/docs/versioned_docs/version-6.x/guides/_category_.json b/docs/versioned_docs/version-6.x/guides/_category_.json deleted file mode 100644 index 82c9b69949..0000000000 --- a/docs/versioned_docs/version-6.x/guides/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Guides", - "position": 0 -} diff --git a/docs/versioned_sidebars/version-5.x-sidebars.json b/docs/versioned_sidebars/version-5.x-sidebars.json deleted file mode 100644 index 9553dd0602..0000000000 --- a/docs/versioned_sidebars/version-5.x-sidebars.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "tutorialSidebar": [ - { - "type": "autogenerated", - "dirName": "guides" - } - ], - "components": [ - { - "type": "autogenerated", - "dirName": "components" - } - ] -} diff --git a/docs/versioned_sidebars/version-6.x-sidebars.json b/docs/versioned_sidebars/version-6.x-sidebars.json deleted file mode 100644 index 9553dd0602..0000000000 --- a/docs/versioned_sidebars/version-6.x-sidebars.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "tutorialSidebar": [ - { - "type": "autogenerated", - "dirName": "guides" - } - ], - "components": [ - { - "type": "autogenerated", - "dirName": "components" - } - ] -} diff --git a/docs/versions.json b/docs/versions.json deleted file mode 100644 index 3f6328c126..0000000000 --- a/docs/versions.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "6.x", - "5.x" -] diff --git a/eslint.config.mjs b/eslint.config.mjs index de942288e7..ee6bef8b48 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -20,15 +20,27 @@ export default defineConfig( 'dist/', 'web-build/', 'docs/.cache-loader/', - 'docs/.docusaurus/', 'docs/build/', 'docs/dist/', 'docs/docs/components/', 'docs/public/', - 'docs/static/', 'example/coverage/', 'example/dist/', + // generated docs + 'docs/5.x/_nav.json', + 'docs/5.x/index.mdx', + 'docs/5.x/docs/_meta.json', + 'docs/5.x/docs/guides/_meta.json', + 'docs/5.x/docs/components/_meta.json', + 'docs/5.x/docs/components/**/_meta.json', + 'docs/6.x/_nav.json', + 'docs/6.x/index.mdx', + 'docs/6.x/docs/_meta.json', + 'docs/6.x/docs/guides/_meta.json', + 'docs/6.x/docs/components/', + 'docs/src/data/componentDocs6x.json', + // generated by bob 'lib/', @@ -43,6 +55,16 @@ export default defineConfig( plugins: { '@typescript-eslint': tseslint.plugin, }, + languageOptions: { + parserOptions: { + project: [ + './tsconfig.source.json', + './docs/tsconfig.json', + './example/tsconfig.json', + ], + tsconfigRootDir: import.meta.dirname, + }, + }, rules: { '@typescript-eslint/no-unused-vars': [ 'error', @@ -208,17 +230,7 @@ export default defineConfig( { files: ['docs/**/*.{js,ts,tsx}'], settings: { - 'import/core-modules': [ - 'react-native-paper', - '@docusaurus/BrowserOnly', - '@docusaurus/Link', - '@docusaurus/plugin-content-docs/client', - '@site/component-docs-plugin/useDocs', - '@theme/Admonition', - '@theme/CodeBlock', - '@theme/TabItem', - '@theme/Tabs', - ], + 'import/core-modules': ['@rspress/core/runtime', 'react-native-paper'], }, }, { diff --git a/example/tsconfig.json b/example/tsconfig.json index f60f9a761f..e07e1cd93d 100644 --- a/example/tsconfig.json +++ b/example/tsconfig.json @@ -1,3 +1,4 @@ { "extends": "../tsconfig", + "include": ["**/*.ts", "**/*.tsx"] } diff --git a/package.json b/package.json index e183be6c56..3a0296b5f5 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ ], "scripts": { "lint": "eslint", - "typecheck": "tsc", + "typecheck": "tsc -b", "test": "jest", "prepack": "bob build", "generate-mappings": "node ./scripts/generate-mappings.ts", diff --git a/scripts/generate-component-docs.ts b/scripts/generate-component-docs.ts index c4adcbeb52..7550fd1669 100644 --- a/scripts/generate-component-docs.ts +++ b/scripts/generate-component-docs.ts @@ -11,8 +11,6 @@ type PluginOptions = { pages: unknown; }; -type PluginConfig = [string, PluginOptions]; - type PluginFactory = ( context: unknown, options: PluginOptions @@ -23,9 +21,41 @@ type PluginFactory = ( const isRecord = (value: unknown): value is { [key: string]: unknown } => typeof value === 'object' && value !== null; +const isPluginOptions = (value: unknown): value is PluginOptions => { + if (!isRecord(value)) { + return false; + } + + return ( + typeof value.docsRootDir === 'string' && + typeof value.libsRootDir === 'string' && + 'pages' in value + ); +}; + const isPluginFactory = (value: unknown): value is PluginFactory => typeof value === 'function'; +const loadPluginConfig = ( + sourceDir: string, + requireFromScript: ReturnType +): PluginOptions => { + const sharedConfigPath = path.join( + sourceDir, + 'docs', + 'component-docs.config.js' + ); + const sharedConfig: unknown = requireFromScript(sharedConfigPath); + + if (!isPluginOptions(sharedConfig)) { + throw new Error( + `Unable to read component docs config from ${sharedConfigPath}` + ); + } + + return sharedConfig; +}; + const normalizeDocs = (value: unknown, sourceDir: string): unknown => { if (Array.isArray(value)) { return value.map((item) => normalizeDocs(item, sourceDir)); @@ -67,6 +97,11 @@ const normalizeDocs = (value: unknown, sourceDir: string): unknown => { ); }; +const writeJson = (destination: string, value: unknown) => { + fs.mkdirSync(path.dirname(destination), { recursive: true }); + fs.writeFileSync(destination, `${JSON.stringify(value, null, 2)}\n`); +}; + const main = async () => { const [branchName, outputPath = 'docs/src/data/componentDocs5x.json'] = process.argv.slice(2); @@ -126,28 +161,7 @@ const main = async () => { fs.symlinkSync(nodeModulesPath, archivedNodeModulesPath, 'dir'); } - const configPath = path.join(sourceDir, 'docs', 'docusaurus.config.js'); - const config: unknown = requireFromScript(configPath); - - if (!isRecord(config) || !Array.isArray(config.plugins)) { - throw new Error(`Unable to read plugins from ${configPath}`); - } - - const pluginConfig = config.plugins.find( - (plugin: unknown): plugin is PluginConfig => - Array.isArray(plugin) && - plugin[0] === './component-docs-plugin' && - isRecord(plugin[1]) && - typeof plugin[1].docsRootDir === 'string' && - typeof plugin[1].libsRootDir === 'string' - ); - - if (!pluginConfig) { - throw new Error( - `Unable to find component docs plugin config in ${configPath}` - ); - } - + const pluginOptions = loadPluginConfig(sourceDir, requireFromScript); const pluginFactoryPath = path.join( sourceDir, 'docs', @@ -161,19 +175,13 @@ const main = async () => { ); } - const plugin = await pluginFactory({}, pluginConfig[1]); + const plugin = await pluginFactory({}, pluginOptions); const docs = await plugin.loadContent(); const destination = path.resolve(rootDir, outputPath); - fs.mkdirSync(path.dirname(destination), { recursive: true }); - fs.writeFileSync( - destination, - `${JSON.stringify( - { docs: normalizeDocs(docs, fs.realpathSync(sourceDir)) }, - null, - 2 - )}\n` - ); + writeJson(destination, { + docs: normalizeDocs(docs, fs.realpathSync(sourceDir)), + }); } catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; diff --git a/tsconfig.build.json b/tsconfig.build.json index e7485e3e40..2a7adecd37 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,13 @@ { - "extends": "./tsconfig", - "exclude": ["example", "docs", "**/__tests__/*", "src/test-utils.tsx"] + "extends": "./tsconfig.json", + "include": ["**/*.ts", "**/*.tsx"], + "exclude": [ + "**/__tests__/*", + "__ts-tests__", + "docs", + "example", + "lib", + "node_modules", + "src/test-utils.tsx" + ] } diff --git a/tsconfig.json b/tsconfig.json index 2c3227b757..8178b5189f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "customConditions": ["react-native-paper-source"], "allowUnreachableCode": false, "allowUnusedLabels": false, + "allowImportingTsExtensions": true, "erasableSyntaxOnly": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, @@ -30,5 +31,16 @@ "types": ["node"], "verbatimModuleSyntax": true }, - "exclude": ["lib/**/*"] + "files": [], + "references": [ + { + "path": "./tsconfig.source.json" + }, + { + "path": "./docs" + }, + { + "path": "./example" + } + ] } diff --git a/tsconfig.source.json b/tsconfig.source.json new file mode 100644 index 0000000000..c5bb31ce7f --- /dev/null +++ b/tsconfig.source.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["docs", "example", "lib", "node_modules"] +} diff --git a/yarn.lock b/yarn.lock index e74b5f06e7..313bbc5130 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,361 +5,6 @@ __metadata: version: 8 cacheKey: 10c0 -"@algolia/abtesting@npm:1.12.2": - version: 1.12.2 - resolution: "@algolia/abtesting@npm:1.12.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/2bc0c3e86a0f840664815f612e038fb941247f8c785e9aa1e1e90728875472bd853e4fa7681ff3e4f9dbe1c0c3d3997a6ce89b13eaf03af61b2125e8a10837c1 - languageName: node - linkType: hard - -"@algolia/autocomplete-core@npm:1.17.9": - version: 1.17.9 - resolution: "@algolia/autocomplete-core@npm:1.17.9" - dependencies: - "@algolia/autocomplete-plugin-algolia-insights": "npm:1.17.9" - "@algolia/autocomplete-shared": "npm:1.17.9" - checksum: 10c0/e1111769a8723b9dd45fc38cd7edc535c86c1f908b84b5fdc5de06ba6b8c7aca14e5f52ebce84fa5f7adf857332e396b93b7e7933b157b2c9aefc0a19d9574ab - languageName: node - linkType: hard - -"@algolia/autocomplete-plugin-algolia-insights@npm:1.17.9": - version: 1.17.9 - resolution: "@algolia/autocomplete-plugin-algolia-insights@npm:1.17.9" - dependencies: - "@algolia/autocomplete-shared": "npm:1.17.9" - peerDependencies: - search-insights: ">= 1 < 3" - checksum: 10c0/05c21502631643abdcd6e9f70b5814a60d34bad59bca501e26e030fd72e689be5cecfb6e8939a0a1bdcb2394591e55e26a42a82c7247528eafeff714db0819a4 - languageName: node - linkType: hard - -"@algolia/autocomplete-preset-algolia@npm:1.17.9": - version: 1.17.9 - resolution: "@algolia/autocomplete-preset-algolia@npm:1.17.9" - dependencies: - "@algolia/autocomplete-shared": "npm:1.17.9" - peerDependencies: - "@algolia/client-search": ">= 4.9.1 < 6" - algoliasearch: ">= 4.9.1 < 6" - checksum: 10c0/99159c7e02a927d0d96717cb4cfd2f8dbc4da73267a8eae4f83af5bf74087089f6e7dbffd316512e713a4cc534e936b6a7ccb5c4a5ff84b4bf73f2d3cc050e79 - languageName: node - linkType: hard - -"@algolia/autocomplete-shared@npm:1.17.9": - version: 1.17.9 - resolution: "@algolia/autocomplete-shared@npm:1.17.9" - peerDependencies: - "@algolia/client-search": ">= 4.9.1 < 6" - algoliasearch: ">= 4.9.1 < 6" - checksum: 10c0/b318281aecdaae09171b47ee4f7bc66b613852cad4506e9d6278fff35ba68a12dd9cce2d90b5f4c3ba0e3d7d780583cbe46b22275260e41bbf09fb01e4a18f49 - languageName: node - linkType: hard - -"@algolia/cache-browser-local-storage@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/cache-browser-local-storage@npm:4.25.3" - dependencies: - "@algolia/cache-common": "npm:4.25.3" - checksum: 10c0/d511fc9d74aaccea5a5be22ca3a99f476573146a6abf3e053f11700c441088c6586a6a584f4b3dba9ea159eaf2da448e7494e566992f813fdbd1875960de42fd - languageName: node - linkType: hard - -"@algolia/cache-common@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/cache-common@npm:4.25.3" - checksum: 10c0/134d43e96f90701ced24edd029289da504538f73ed7441118ca835e299aaa0902e83a1338b09afe64004b759cd633ecfa8c4f9ede3d0169cc4831d6efe0291cc - languageName: node - linkType: hard - -"@algolia/cache-in-memory@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/cache-in-memory@npm:4.25.3" - dependencies: - "@algolia/cache-common": "npm:4.25.3" - checksum: 10c0/bc61281f4f481b045fb43a1bd16f677c23c1b8aea5ba8ca7e5cf4816843a705a5115edeebfee60c8f90544f99f312d919daf40db72de52ca5edef7f1cf3d35c9 - languageName: node - linkType: hard - -"@algolia/client-abtesting@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/client-abtesting@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/be50eb231e023538745fac1ea61a632244816b605b31d519eacb16b28bd8e2a444842979f81391ad92e7c7737c8b0ef37037260aca0cf6651653a6383cd79603 - languageName: node - linkType: hard - -"@algolia/client-account@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/client-account@npm:4.25.3" - dependencies: - "@algolia/client-common": "npm:4.25.3" - "@algolia/client-search": "npm:4.25.3" - "@algolia/transporter": "npm:4.25.3" - checksum: 10c0/bb718d06febb72ea6bead8c64562a5d804fa878a9ae3a78fafa461eece6bbd0109fddb0ca517add1ef745cee7b5fe736b31f937cabc4fb29e820e8398b6bae9f - languageName: node - linkType: hard - -"@algolia/client-analytics@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/client-analytics@npm:4.25.3" - dependencies: - "@algolia/client-common": "npm:4.25.3" - "@algolia/client-search": "npm:4.25.3" - "@algolia/requester-common": "npm:4.25.3" - "@algolia/transporter": "npm:4.25.3" - checksum: 10c0/4922f34ecea4f17e56d66a5f34d9d65b685e1c813444ee70db861aa14e8c25ded61e9d7c1555428ca3900500c18b3d7430d99a6299d1cb7b49c6ed06f9649101 - languageName: node - linkType: hard - -"@algolia/client-analytics@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/client-analytics@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/80f9df688d31987d328296779a7cc30248a58f7ec6efe07837101482c737c9214796009e687e11e50f7dde1fd976b78213210916ba3666e0aa7b9d2884ccd956 - languageName: node - linkType: hard - -"@algolia/client-common@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/client-common@npm:4.25.3" - dependencies: - "@algolia/requester-common": "npm:4.25.3" - "@algolia/transporter": "npm:4.25.3" - checksum: 10c0/f0865dd8763b5695fd852b3d4fdb9fc1fcc5546235683a8714d97e994ab168cb4184a03fa79eb156daec9c789d3d93baf9f846c47acc6cdda6028b02984e3294 - languageName: node - linkType: hard - -"@algolia/client-common@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/client-common@npm:5.46.2" - checksum: 10c0/a88ca3f5141a6445f8518a384f53ecaad248150affc058d8edf84fd1d7397fb4aa7b66a50d10d848d278216a7b873027224375bf64145ad8435213c6a3727021 - languageName: node - linkType: hard - -"@algolia/client-insights@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/client-insights@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/4a398ab4796e32c3e02f89392b2294985ad3bea7e9b9052c2c3642691b7975564cdc4b7f06b0bc6ca6d6695efb77c9932e6ff04abb56a486ca8840242b3ebe1c - languageName: node - linkType: hard - -"@algolia/client-personalization@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/client-personalization@npm:4.25.3" - dependencies: - "@algolia/client-common": "npm:4.25.3" - "@algolia/requester-common": "npm:4.25.3" - "@algolia/transporter": "npm:4.25.3" - checksum: 10c0/bf8d1f13805534059354af7597d5a4f5ac7f5415751d4905e7e0f5b367ae6940c557ff35867201ae64b4d7cf1ce6591a0368b319c865a82b497415f8e0353084 - languageName: node - linkType: hard - -"@algolia/client-personalization@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/client-personalization@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/e5f2225ac50e55defd544c4689438beb22db2f71143da78bb2e987b4be9d908601995e32bc21b64d8ad6fe149caa16039a6580aa5453f5ccd2d665446f98f41d - languageName: node - linkType: hard - -"@algolia/client-query-suggestions@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/client-query-suggestions@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/98869c0a230bbcaffe538603f3518b4be1b6c97a8db5cb26ed763863734624b44e549a680eea127b64924fc72c343af8c7e20b15eed7467852e4cce414ac8e7e - languageName: node - linkType: hard - -"@algolia/client-search@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/client-search@npm:4.25.3" - dependencies: - "@algolia/client-common": "npm:4.25.3" - "@algolia/requester-common": "npm:4.25.3" - "@algolia/transporter": "npm:4.25.3" - checksum: 10c0/32ad49da60a14d2cfe78f30e9f3e3c57931ba91ccd21d2c5f319c1521b4a173c6ee6510d53557ef7831f964000401fd9fa02d853a2dbf00dd4d740dffe1b8325 - languageName: node - linkType: hard - -"@algolia/client-search@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/client-search@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/a6cc3d80e7cb5c83b98396d67b0b8486336ad63514be47b8f5ba5ed782a2a90ec62c826653c42a9fff6630b52e3d4949f7930bc88fa1e532a62ac31a283311ed - languageName: node - linkType: hard - -"@algolia/events@npm:^4.0.1": - version: 4.0.1 - resolution: "@algolia/events@npm:4.0.1" - checksum: 10c0/f398d815c6ed21ac08f6caadf1e9155add74ac05d99430191c3b1f1335fd91deaf468c6b304e6225c9885d3d44c06037c53def101e33d9c22daff175b2a65ca9 - languageName: node - linkType: hard - -"@algolia/ingestion@npm:1.46.2": - version: 1.46.2 - resolution: "@algolia/ingestion@npm:1.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/b5236313c07b89457381cf614fd66b59ce3a00575afc281b2a28ee4531d0d3f66188735694339034e8faae4f7d329591006de57f9b7db7c55da63aec53b058c0 - languageName: node - linkType: hard - -"@algolia/logger-common@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/logger-common@npm:4.25.3" - checksum: 10c0/503434de662b7b667522ebcd2d9fbc29d9e401ba253fe16ff86a7572e6e113fe021b2762498da8a57b06c2533f86dcecd165f528865e313c5421f099fca9a27e - languageName: node - linkType: hard - -"@algolia/logger-console@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/logger-console@npm:4.25.3" - dependencies: - "@algolia/logger-common": "npm:4.25.3" - checksum: 10c0/996df8eb0b1e9866deec1a0a7f58af1a779c487674a6742bf660a7d31ee2b5a163b99dbbe5050c834922a992066404f9dadf49cf9bbdf0b9adeb7a16fc569337 - languageName: node - linkType: hard - -"@algolia/monitoring@npm:1.46.2": - version: 1.46.2 - resolution: "@algolia/monitoring@npm:1.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/c676a3b5af8914af73a6bb2c19653381d9c6ca7e530e13f46f2bd52d0ae6d0cb162c6dfc511be2bd0e59c98b8083636553000faaf08ab0e31177635230a49726 - languageName: node - linkType: hard - -"@algolia/recommend@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/recommend@npm:4.25.3" - dependencies: - "@algolia/cache-browser-local-storage": "npm:4.25.3" - "@algolia/cache-common": "npm:4.25.3" - "@algolia/cache-in-memory": "npm:4.25.3" - "@algolia/client-common": "npm:4.25.3" - "@algolia/client-search": "npm:4.25.3" - "@algolia/logger-common": "npm:4.25.3" - "@algolia/logger-console": "npm:4.25.3" - "@algolia/requester-browser-xhr": "npm:4.25.3" - "@algolia/requester-common": "npm:4.25.3" - "@algolia/requester-node-http": "npm:4.25.3" - "@algolia/transporter": "npm:4.25.3" - checksum: 10c0/c59db46fcf46a784a982df8dce31298c838f65ffb3cf6193c05791c9c7114508819d81f5fe27da856820f6f2dc4a376c4cb98d3dcd9fc2e682da64c25dc5fe23 - languageName: node - linkType: hard - -"@algolia/recommend@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/recommend@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/5224e8965b4e3dc7f2fadb12ecc073866fab5eceb825079d14689c0ca2bb25fad7d794d9a84d535718ae20bb28ee2a31bb2c941a9d878ffeedb46f6cb6e05600 - languageName: node - linkType: hard - -"@algolia/requester-browser-xhr@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/requester-browser-xhr@npm:4.25.3" - dependencies: - "@algolia/requester-common": "npm:4.25.3" - checksum: 10c0/2b923f192f32d454cef1f889cf788971cdb7b64645aed6d28770bc498036108fb84cd9a3868cf40980e2fe7632016c5eafb3fc82cdd8bd1647c26d36f741de3c - languageName: node - linkType: hard - -"@algolia/requester-browser-xhr@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/requester-browser-xhr@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - checksum: 10c0/4942ccc4327a4de475e8f2d57cf880fc0646d3a8a6e7aa82f429a903f74b2a337acede8dce7993d3638cb6dda432470627219da3d609ff657a7885f4c0b1bd76 - languageName: node - linkType: hard - -"@algolia/requester-common@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/requester-common@npm:4.25.3" - checksum: 10c0/1ba00218e1e36b676dc0809e3fc102d6912f15f71cc6f9d605af4e3510a5d1e4f57d6679a9b1187164099dfd842c4fe97c493fec732efb53cd2238fb3f144fd5 - languageName: node - linkType: hard - -"@algolia/requester-fetch@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/requester-fetch@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - checksum: 10c0/0023a3a9918f169a967865be1c026fe639ed49fd80ec3392d5366726f672411e890f9b7898cb72da8581f754056eb6017ebe46a719e20c68cf68b892a15fa211 - languageName: node - linkType: hard - -"@algolia/requester-node-http@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/requester-node-http@npm:4.25.3" - dependencies: - "@algolia/requester-common": "npm:4.25.3" - checksum: 10c0/38b2b4d87e9c48f14e7325fed17663f15472f6e0217da177a7ef8516f33e871705993626ac025072511ad587373c1eacd635c68f6922c0514fb2f7758d4c941c - languageName: node - linkType: hard - -"@algolia/requester-node-http@npm:5.46.2": - version: 5.46.2 - resolution: "@algolia/requester-node-http@npm:5.46.2" - dependencies: - "@algolia/client-common": "npm:5.46.2" - checksum: 10c0/60054e5a7f549d12917642b4cdeedbf2698436f85de6e063e1af740e774e7ef9719a620ecd44839023d3ce069e1a1a5fef18fec7dfd9449e342da3f52927e3e9 - languageName: node - linkType: hard - -"@algolia/transporter@npm:4.25.3": - version: 4.25.3 - resolution: "@algolia/transporter@npm:4.25.3" - dependencies: - "@algolia/cache-common": "npm:4.25.3" - "@algolia/logger-common": "npm:4.25.3" - "@algolia/requester-common": "npm:4.25.3" - checksum: 10c0/c7160ad528c77b5a88c6da8f2ca0d28171598cbca0b9cade690e78b4baacd814541d09c7df68036849d02e73ee54ee9b06447264617d644b5f823f73e95bfcf7 - languageName: node - linkType: hard - "@ark/schema@npm:0.56.0": version: 0.56.0 resolution: "@ark/schema@npm:0.56.0" @@ -376,7 +21,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.20.0, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.8.3": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.20.0, @babel/code-frame@npm:^7.27.1": version: 7.27.1 resolution: "@babel/code-frame@npm:7.27.1" dependencies: @@ -430,31 +75,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:7.12.9": - version: 7.12.9 - resolution: "@babel/core@npm:7.12.9" - dependencies: - "@babel/code-frame": "npm:^7.10.4" - "@babel/generator": "npm:^7.12.5" - "@babel/helper-module-transforms": "npm:^7.12.1" - "@babel/helpers": "npm:^7.12.5" - "@babel/parser": "npm:^7.12.7" - "@babel/template": "npm:^7.12.7" - "@babel/traverse": "npm:^7.12.9" - "@babel/types": "npm:^7.12.7" - convert-source-map: "npm:^1.7.0" - debug: "npm:^4.1.0" - gensync: "npm:^1.0.0-beta.1" - json5: "npm:^2.1.2" - lodash: "npm:^4.17.19" - resolve: "npm:^1.3.2" - semver: "npm:^5.4.1" - source-map: "npm:^0.5.0" - checksum: 10c0/c11d26f5a33a29c94fdd1c492dfd723f48926c51e975448dda57c081c0d74c7b03298642b2651559e0d330ec868b5757b60f9648c71cf7f89fddf79a17cf006f - languageName: node - linkType: hard - -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.6, @babel/core@npm:^7.19.6, @babel/core@npm:^7.20.0, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2, @babel/core@npm:^7.7.5": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.20.0, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.2, @babel/core@npm:^7.7.5": version: 7.28.5 resolution: "@babel/core@npm:7.28.5" dependencies: @@ -537,7 +158,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:>=7, @babel/generator@npm:^7.12.11, @babel/generator@npm:^7.12.5, @babel/generator@npm:^7.18.7, @babel/generator@npm:^7.20.5, @babel/generator@npm:^7.28.5, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:>=7, @babel/generator@npm:^7.12.11, @babel/generator@npm:^7.20.5, @babel/generator@npm:^7.28.5, @babel/generator@npm:^7.7.2": version: 7.28.5 resolution: "@babel/generator@npm:7.28.5" dependencies: @@ -764,7 +385,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.27.1, @babel/helper-module-transforms@npm:^7.28.3": +"@babel/helper-module-transforms@npm:^7.27.1, @babel/helper-module-transforms@npm:^7.28.3": version: 7.28.3 resolution: "@babel/helper-module-transforms@npm:7.28.3" dependencies: @@ -812,13 +433,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:7.10.4": - version: 7.10.4 - resolution: "@babel/helper-plugin-utils@npm:7.10.4" - checksum: 10c0/113d0405281f5490658f7c1c3a81b4a37927375e1ebcccd2fd90be538a102da0c2d6024561aaf26bd1c71ef7688b5a8b96a87d938db8d9774454ab635011fc7f - languageName: node - linkType: hard - "@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.27.1, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.27.1 resolution: "@babel/helper-plugin-utils@npm:7.27.1" @@ -942,7 +556,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.28.4": +"@babel/helpers@npm:^7.28.4": version: 7.28.4 resolution: "@babel/helpers@npm:7.28.4" dependencies: @@ -972,7 +586,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.18.8, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.5": version: 7.28.5 resolution: "@babel/parser@npm:7.28.5" dependencies: @@ -1136,19 +750,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:7.12.1": - version: 7.12.1 - resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.12.1" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.0" - "@babel/plugin-transform-parameters": "npm:^7.12.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/f773d59ead8b056b646d585e95d610cca2f0aeaa2eeaad74b3eb9e25821b06f27e361dd0aac9a088a10c22fee1ead8863f82a2be073e28eb04ca9a330a00941e - languageName: node - linkType: hard - "@babel/plugin-proposal-object-rest-spread@npm:^7.3.2": version: 7.20.7 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" @@ -1349,17 +950,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:7.12.1": - version: 7.12.1 - resolution: "@babel/plugin-syntax-jsx@npm:7.12.1" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/11d435f9e4e71c0f00e5bc295b40747c2c42341b7f38ddc5f8ac41d49ddfa247514dbe91932fa3dabd65581b4c7a9fe5b3d1c2b285e5ca32f4e5296cc185d40c - languageName: node - linkType: hard - "@babel/plugin-syntax-jsx@npm:^7.2.0, @babel/plugin-syntax-jsx@npm:^7.27.1, @babel/plugin-syntax-jsx@npm:^7.7.2": version: 7.27.1 resolution: "@babel/plugin-syntax-jsx@npm:7.27.1" @@ -1426,7 +1016,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-object-rest-spread@npm:7.8.3, @babel/plugin-syntax-object-rest-spread@npm:^7.8.0, @babel/plugin-syntax-object-rest-spread@npm:^7.8.3": +"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": version: 7.8.3 resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" dependencies: @@ -2189,7 +1779,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.24.7, @babel/plugin-transform-parameters@npm:^7.27.7": +"@babel/plugin-transform-parameters@npm:^7.20.7, @babel/plugin-transform-parameters@npm:^7.24.7, @babel/plugin-transform-parameters@npm:^7.27.7": version: 7.27.7 resolution: "@babel/plugin-transform-parameters@npm:7.27.7" dependencies: @@ -2261,17 +1851,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-constant-elements@npm:^7.18.12": - version: 7.27.1 - resolution: "@babel/plugin-transform-react-constant-elements@npm:7.27.1" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.27.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/07fa88dd312c97d05de95e344a11a78e24d711e7bde879076d8880869ad7b0dc69c5a5ad056790595043cb9c533fd93af0ba015eed4631315282295f767ccfbe - languageName: node - linkType: hard - "@babel/plugin-transform-react-display-name@npm:^7.24.7, @babel/plugin-transform-react-display-name@npm:^7.28.0": version: 7.28.0 resolution: "@babel/plugin-transform-react-display-name@npm:7.28.0" @@ -2430,7 +2009,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:>=7, @babel/plugin-transform-runtime@npm:^7.18.6, @babel/plugin-transform-runtime@npm:^7.24.7": +"@babel/plugin-transform-runtime@npm:>=7, @babel/plugin-transform-runtime@npm:^7.24.7": version: 7.28.5 resolution: "@babel/plugin-transform-runtime@npm:7.28.5" dependencies: @@ -2611,7 +2190,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.12.1, @babel/preset-env@npm:^7.18.6, @babel/preset-env@npm:^7.19.4": +"@babel/preset-env@npm:^7.12.1": version: 7.28.5 resolution: "@babel/preset-env@npm:7.28.5" dependencies: @@ -2798,7 +2377,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-react@npm:^7.12.1, @babel/preset-react@npm:^7.18.6, @babel/preset-react@npm:^7.28.5": +"@babel/preset-react@npm:^7.12.1, @babel/preset-react@npm:^7.28.5": version: 7.28.5 resolution: "@babel/preset-react@npm:7.28.5" dependencies: @@ -2814,7 +2393,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-typescript@npm:^7.12.1, @babel/preset-typescript@npm:^7.18.6, @babel/preset-typescript@npm:^7.23.0, @babel/preset-typescript@npm:^7.27.1, @babel/preset-typescript@npm:^7.28.5": +"@babel/preset-typescript@npm:^7.12.1, @babel/preset-typescript@npm:^7.23.0, @babel/preset-typescript@npm:^7.27.1, @babel/preset-typescript@npm:^7.28.5": version: 7.28.5 resolution: "@babel/preset-typescript@npm:7.28.5" dependencies: @@ -2844,23 +2423,14 @@ __metadata: languageName: node linkType: hard -"@babel/runtime-corejs3@npm:^7.18.6": - version: 7.28.4 - resolution: "@babel/runtime-corejs3@npm:7.28.4" - dependencies: - core-js-pure: "npm:^3.43.0" - checksum: 10c0/0a7fe2d4e36d345acf090dd685b5c6ed55af3ead69a84e2cfca56631815dd757f3a362031b376cc746f63f0fd856e7a5280807833f7fa9a5b7f1febd97f8c0da - languageName: node - linkType: hard - -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.18.9, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.7.6": +"@babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.18.9, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.7.6": version: 7.28.4 resolution: "@babel/runtime@npm:7.28.4" checksum: 10c0/792ce7af9750fb9b93879cc9d1db175701c4689da890e6ced242ea0207c9da411ccf16dc04e689cc01158b28d7898c40d75598f4559109f761c12ce01e959bf7 languageName: node linkType: hard -"@babel/template@npm:>=7, @babel/template@npm:^7.12.7, @babel/template@npm:^7.27.1, @babel/template@npm:^7.27.2, @babel/template@npm:^7.3.3": +"@babel/template@npm:>=7, @babel/template@npm:^7.27.1, @babel/template@npm:^7.27.2, @babel/template@npm:^7.3.3": version: 7.27.2 resolution: "@babel/template@npm:7.27.2" dependencies: @@ -2893,7 +2463,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.18.8, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4, @babel/traverse@npm:^7.28.5": +"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.28.0, @babel/traverse@npm:^7.28.3, @babel/traverse@npm:^7.28.4, @babel/traverse@npm:^7.28.5": version: 7.28.5 resolution: "@babel/traverse@npm:7.28.5" dependencies: @@ -2938,7 +2508,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.7, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" dependencies: @@ -3018,6 +2588,32 @@ __metadata: languageName: node linkType: hard +"@callstack/rspress-preset@npm:0.6.6": + version: 0.6.6 + resolution: "@callstack/rspress-preset@npm:0.6.6" + dependencies: + "@callstack/rspress-theme": "npm:0.6.6" + "@rspress/plugin-sitemap": "npm:^2.0.8" + "@vercel/analytics": "npm:^2.0.1" + rsbuild-plugin-open-graph: "npm:^1.1.2" + zod: "npm:^4.3.6" + peerDependencies: + "@rspress/core": ^2.0.8 + checksum: 10c0/2f45d4e6a231bd9ca36e809d6a55064bbe035ebf042ecf8d9e31e819fa6d8b703192488cf4a884ee2772be84a08d12dbc5a2d10aee5df453804b402882363929 + languageName: node + linkType: hard + +"@callstack/rspress-theme@npm:0.6.6": + version: 0.6.6 + resolution: "@callstack/rspress-theme@npm:0.6.6" + peerDependencies: + "@rspress/core": ^2.0.8 + react: ^19.2.4 + react-dom: ^19.2.4 + checksum: 10c0/e361625a922389bb23ab223e2b3ff4720c976e16c016784fbb0da373ee816920b3316ce9ad3ede6e485d05aeec942568940e62d0854ff785dd66c839957848e3 + languageName: node + linkType: hard + "@cnakazawa/watch@npm:^1.0.3": version: 1.0.4 resolution: "@cnakazawa/watch@npm:1.0.4" @@ -3030,13 +2626,6 @@ __metadata: languageName: node linkType: hard -"@colors/colors@npm:1.5.0": - version: 1.5.0 - resolution: "@colors/colors@npm:1.5.0" - checksum: 10c0/eb42729851adca56d19a08e48d5a1e95efd2a32c55ae0323de8119052be0510d4b7a1611f2abcbf28c044a6c11e6b7d38f99fccdad7429300c37a8ea5fb95b44 - languageName: node - linkType: hard - "@commitlint/cli@npm:^8.3.6": version: 8.3.6 resolution: "@commitlint/cli@npm:8.3.6" @@ -3199,682 +2788,74 @@ __metadata: languageName: node linkType: hard -"@discoveryjs/json-ext@npm:0.5.7": - version: 0.5.7 - resolution: "@discoveryjs/json-ext@npm:0.5.7" - checksum: 10c0/e10f1b02b78e4812646ddf289b7d9f2cb567d336c363b266bd50cd223cf3de7c2c74018d91cd2613041568397ef3a4a2b500aba588c6e5bd78c38374ba68f38c - languageName: node - linkType: hard - -"@docsearch/css@npm:3.9.0": - version: 3.9.0 - resolution: "@docsearch/css@npm:3.9.0" - checksum: 10c0/6300551e1cab7a5487063ec3581ae78ddaee3d93ec799556b451054448559b3ba849751b825fbd8b678367ef944bd82b3f11bc1d9e74e08e3cc48db40487b396 - languageName: node - linkType: hard - -"@docsearch/react@npm:^3.1.1": - version: 3.9.0 - resolution: "@docsearch/react@npm:3.9.0" +"@egjs/hammerjs@npm:^2.0.17": + version: 2.0.17 + resolution: "@egjs/hammerjs@npm:2.0.17" dependencies: - "@algolia/autocomplete-core": "npm:1.17.9" - "@algolia/autocomplete-preset-algolia": "npm:1.17.9" - "@docsearch/css": "npm:3.9.0" - algoliasearch: "npm:^5.14.2" - peerDependencies: - "@types/react": ">= 16.8.0 < 20.0.0" - react: ">= 16.8.0 < 20.0.0" - react-dom: ">= 16.8.0 < 20.0.0" - search-insights: ">= 1 < 3" - peerDependenciesMeta: - "@types/react": - optional: true - react: - optional: true - react-dom: - optional: true - search-insights: - optional: true - checksum: 10c0/5e737a5d9ef1daae1cd93e89870214c1ab0c36a3a2193e898db044bcc5d9de59f85228b2360ec0e8f10cdac7fd2fe3c6ec8a05d943ee7e17d6c1cef2e6e9ff2d + "@types/hammerjs": "npm:^2.0.36" + checksum: 10c0/dbedc15a0e633f887c08394bd636faf6a3abd05726dc0909a0e01209d5860a752d9eca5e512da623aecfabe665f49f1d035de3103eb2f9022c5cea692f9cc9be languageName: node linkType: hard -"@docusaurus/core@npm:2.4.3, @docusaurus/core@npm:^2.3.6": - version: 2.4.3 - resolution: "@docusaurus/core@npm:2.4.3" +"@emnapi/core@npm:1.10.0": + version: 1.10.0 + resolution: "@emnapi/core@npm:1.10.0" dependencies: - "@babel/core": "npm:^7.18.6" - "@babel/generator": "npm:^7.18.7" - "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" - "@babel/plugin-transform-runtime": "npm:^7.18.6" - "@babel/preset-env": "npm:^7.18.6" - "@babel/preset-react": "npm:^7.18.6" - "@babel/preset-typescript": "npm:^7.18.6" - "@babel/runtime": "npm:^7.18.6" - "@babel/runtime-corejs3": "npm:^7.18.6" - "@babel/traverse": "npm:^7.18.8" - "@docusaurus/cssnano-preset": "npm:2.4.3" - "@docusaurus/logger": "npm:2.4.3" - "@docusaurus/mdx-loader": "npm:2.4.3" - "@docusaurus/react-loadable": "npm:5.5.2" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-common": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - "@slorber/static-site-generator-webpack-plugin": "npm:^4.0.7" - "@svgr/webpack": "npm:^6.2.1" - autoprefixer: "npm:^10.4.7" - babel-loader: "npm:^8.2.5" - babel-plugin-dynamic-import-node: "npm:^2.3.3" - boxen: "npm:^6.2.1" - chalk: "npm:^4.1.2" - chokidar: "npm:^3.5.3" - clean-css: "npm:^5.3.0" - cli-table3: "npm:^0.6.2" - combine-promises: "npm:^1.1.0" - commander: "npm:^5.1.0" - copy-webpack-plugin: "npm:^11.0.0" - core-js: "npm:^3.23.3" - css-loader: "npm:^6.7.1" - css-minimizer-webpack-plugin: "npm:^4.0.0" - cssnano: "npm:^5.1.12" - del: "npm:^6.1.1" - detect-port: "npm:^1.3.0" - escape-html: "npm:^1.0.3" - eta: "npm:^2.0.0" - file-loader: "npm:^6.2.0" - fs-extra: "npm:^10.1.0" - html-minifier-terser: "npm:^6.1.0" - html-tags: "npm:^3.2.0" - html-webpack-plugin: "npm:^5.5.0" - import-fresh: "npm:^3.3.0" - leven: "npm:^3.1.0" - lodash: "npm:^4.17.21" - mini-css-extract-plugin: "npm:^2.6.1" - postcss: "npm:^8.4.14" - postcss-loader: "npm:^7.0.0" - prompts: "npm:^2.4.2" - react-dev-utils: "npm:^12.0.1" - react-helmet-async: "npm:^1.3.0" - react-loadable: "npm:@docusaurus/react-loadable@5.5.2" - react-loadable-ssr-addon-v5-slorber: "npm:^1.0.1" - react-router: "npm:^5.3.3" - react-router-config: "npm:^5.1.1" - react-router-dom: "npm:^5.3.3" - rtl-detect: "npm:^1.0.4" - semver: "npm:^7.3.7" - serve-handler: "npm:^6.1.3" - shelljs: "npm:^0.8.5" - terser-webpack-plugin: "npm:^5.3.3" + "@emnapi/wasi-threads": "npm:1.2.1" tslib: "npm:^2.4.0" - update-notifier: "npm:^5.1.0" - url-loader: "npm:^4.1.1" - wait-on: "npm:^6.0.1" - webpack: "npm:^5.73.0" - webpack-bundle-analyzer: "npm:^4.5.0" - webpack-dev-server: "npm:^4.9.3" - webpack-merge: "npm:^5.8.0" - webpackbar: "npm:^5.0.2" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - bin: - docusaurus: bin/docusaurus.mjs - checksum: 10c0/afa7e504e537499d110cbf1aad58873864b3ddce8ee6891ad951bb43c8a5bbab39e46519c3b92506bd12476e25abfaa8330fbb261e2cc90ef18386e8e8e3e9e3 + checksum: 10c0/f51d08227857b60632de7714d708124f0e100a1462dde6df8221760939aa3204a73193830371830fac0716f3ccd2129f2cac1b17cd7d7958bc4da9018a296edb languageName: node linkType: hard -"@docusaurus/cssnano-preset@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/cssnano-preset@npm:2.4.3" +"@emnapi/runtime@npm:1.10.0": + version: 1.10.0 + resolution: "@emnapi/runtime@npm:1.10.0" dependencies: - cssnano-preset-advanced: "npm:^5.3.8" - postcss: "npm:^8.4.14" - postcss-sort-media-queries: "npm:^4.2.1" tslib: "npm:^2.4.0" - checksum: 10c0/f069913f17d8ccacecb7ed9f471f3f7bfce9f98872de796e0751066546378f6dcfe74bf2c1cd11764c372c7ae4b1538f8564971d982c3209160b24d39fc7f383 + checksum: 10c0/953f14991d1aefb92ee6f8eb27dea725e484791a53a0cb5f47d9e0087b9a2c929ff2e92adf95af15d6ad456db6300c6b761ebf72b50a875b874a83520b3ba093 languageName: node linkType: hard -"@docusaurus/logger@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/logger@npm:2.4.3" +"@emnapi/wasi-threads@npm:1.2.1": + version: 1.2.1 + resolution: "@emnapi/wasi-threads@npm:1.2.1" dependencies: - chalk: "npm:^4.1.2" tslib: "npm:^2.4.0" - checksum: 10c0/a94de20b5c3742fb273878db497e16c14f130b7a245579f6eb293dab59b13833085317e864876e0e87f145a92cdf522bcb0ea2c98ae7dd7af00b8d6aa0da9229 + checksum: 10c0/32fcfa81ab396533b2ec1f4082b1ff779a05d9c836bbbd3f4398405b0e6814c0d9503b7993130e37bc6941dbc1ded49f55e9700ae9ca4e803bab2b5bc5deb331 languageName: node linkType: hard -"@docusaurus/mdx-loader@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/mdx-loader@npm:2.4.3" +"@emotion/is-prop-valid@npm:^0.8.8": + version: 0.8.8 + resolution: "@emotion/is-prop-valid@npm:0.8.8" dependencies: - "@babel/parser": "npm:^7.18.8" - "@babel/traverse": "npm:^7.18.8" - "@docusaurus/logger": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@mdx-js/mdx": "npm:^1.6.22" - escape-html: "npm:^1.0.3" - file-loader: "npm:^6.2.0" - fs-extra: "npm:^10.1.0" - image-size: "npm:^1.0.1" - mdast-util-to-string: "npm:^2.0.0" - remark-emoji: "npm:^2.2.0" - stringify-object: "npm:^3.3.0" - tslib: "npm:^2.4.0" - unified: "npm:^9.2.2" - unist-util-visit: "npm:^2.0.3" - url-loader: "npm:^4.1.1" - webpack: "npm:^5.73.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/3daf13e28ecd152003977ec428cba3a06db347968e6a26d83b3191d35f036a852f536bf66dd2e8928086ad6c9d5496695bac6a75935cf4722234481f608b9cfa + "@emotion/memoize": "npm:0.7.4" + checksum: 10c0/f6be625f067c7fa56a12a4edaf090715616dc4fc7803c87212831f38c969350107b9709b1be54100e53153b18d9fa068eb4bf4f9ac66a37a8edf1bac9b64e279 languageName: node linkType: hard -"@docusaurus/module-type-aliases@npm:2.4.3, @docusaurus/module-type-aliases@npm:^2.3.6": - version: 2.4.3 - resolution: "@docusaurus/module-type-aliases@npm:2.4.3" - dependencies: - "@docusaurus/react-loadable": "npm:5.5.2" - "@docusaurus/types": "npm:2.4.3" - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - "@types/react-router-config": "npm:*" - "@types/react-router-dom": "npm:*" - react-helmet-async: "npm:*" - react-loadable: "npm:@docusaurus/react-loadable@5.5.2" - peerDependencies: - react: "*" - react-dom: "*" - checksum: 10c0/9141a6d6162e4b5a610152a8323830696484e98484c1687661098103936b1ac8137c8131b55bff9e270975dcdba1b59426e561451d56fe1b2424c1485ec31906 +"@emotion/memoize@npm:0.7.4": + version: 0.7.4 + resolution: "@emotion/memoize@npm:0.7.4" + checksum: 10c0/b2376548fc147b43afd1ff005a80a1a025bd7eb4fb759fdb23e96e5ff290ee8ba16628a332848d600fb91c3cdc319eee5395fa33d8875e5d5a8c4ce18cddc18e languageName: node linkType: hard -"@docusaurus/plugin-content-blog@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/plugin-content-blog@npm:2.4.3" +"@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": + version: 4.9.1 + resolution: "@eslint-community/eslint-utils@npm:4.9.1" dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/logger": "npm:2.4.3" - "@docusaurus/mdx-loader": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-common": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - cheerio: "npm:^1.0.0-rc.12" - feed: "npm:^4.2.2" - fs-extra: "npm:^10.1.0" - lodash: "npm:^4.17.21" - reading-time: "npm:^1.5.0" - tslib: "npm:^2.4.0" - unist-util-visit: "npm:^2.0.3" - utility-types: "npm:^3.10.0" - webpack: "npm:^5.73.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/23869d0533bd9693ea9d32d7a638e9adb51a9a9dbbc189533f1f0c62092c613538448d10c9b9fc24b2e1fd4832d8f969574768191a2850bdb40fdd8e7b0470b6 - languageName: node - linkType: hard - -"@docusaurus/plugin-content-docs@npm:2.4.3, @docusaurus/plugin-content-docs@npm:^2.0.0-rc.1": - version: 2.4.3 - resolution: "@docusaurus/plugin-content-docs@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/logger": "npm:2.4.3" - "@docusaurus/mdx-loader": "npm:2.4.3" - "@docusaurus/module-type-aliases": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - "@types/react-router-config": "npm:^5.0.6" - combine-promises: "npm:^1.1.0" - fs-extra: "npm:^10.1.0" - import-fresh: "npm:^3.3.0" - js-yaml: "npm:^4.1.0" - lodash: "npm:^4.17.21" - tslib: "npm:^2.4.0" - utility-types: "npm:^3.10.0" - webpack: "npm:^5.73.0" + eslint-visitor-keys: "npm:^3.4.3" peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/ad22c773a875c96e82b1ecb770d19d34edbf9acc23464ce2e5098049d2b1f6220493f94d77c3670668572a3e70dbd2e52dc1015137bcf0944d4cc8867dbc8cef + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/dc4ab5e3e364ef27e33666b11f4b86e1a6c1d7cbf16f0c6ff87b1619b3562335e9201a3d6ce806221887ff780ec9d828962a290bb910759fd40a674686503f02 languageName: node linkType: hard -"@docusaurus/plugin-content-pages@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/plugin-content-pages@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/mdx-loader": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - fs-extra: "npm:^10.1.0" - tslib: "npm:^2.4.0" - webpack: "npm:^5.73.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/c488027a1f119e207f264a96fdb5fba33bb3ec02b01d1ecbe0beaddc66773e446aca58e8ae2f62b705e9ace0789d02f9731902a150d3c1b60a3e9b75ca9c9839 - languageName: node - linkType: hard - -"@docusaurus/plugin-debug@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/plugin-debug@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - fs-extra: "npm:^10.1.0" - react-json-view: "npm:^1.21.3" - tslib: "npm:^2.4.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/64cd8c0c9868f4da2b1a19525c1af3a5e8ee8284a29053243afbbb888542c506f6a0c30711042dca31d170ff6fada5b2f5736603c4b2ea0017155ec9659aa77f - languageName: node - linkType: hard - -"@docusaurus/plugin-google-analytics@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/plugin-google-analytics@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - tslib: "npm:^2.4.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/2998a4ed28d046baa6c443a57b9420fb3f4d78d318c076308b4c1890fc8d05b3f4f7236eba271c1ed88cc80621314eb176f06f29f7ee23274c62d916916a7dee - languageName: node - linkType: hard - -"@docusaurus/plugin-google-gtag@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/plugin-google-gtag@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - tslib: "npm:^2.4.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/e978622437b19fe6873c301cb2da5ff64e6d464bb10cac509b07eef01622f5518f4b41c3c91fb8a384017b0414e32439970dbacef61cddba8279615eb59a011c - languageName: node - linkType: hard - -"@docusaurus/plugin-google-tag-manager@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/plugin-google-tag-manager@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - tslib: "npm:^2.4.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/d17e82aa6bbaa6f25752677dc50f3343212e112035ab5c2dfc065126d287f93354adfe655c726708be59de3e66c0cecdf6feb12fd5eb36b8939ed26ad4338a10 - languageName: node - linkType: hard - -"@docusaurus/plugin-sitemap@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/plugin-sitemap@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/logger": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-common": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - fs-extra: "npm:^10.1.0" - sitemap: "npm:^7.1.1" - tslib: "npm:^2.4.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/da268caa3ef80c6581c66bcc141ab4ead9256be45c92acfbb9fa1ddf1288f1eaeae482bf5957193efdfa8b1723696f3d7d4f2cc256d0db009fcc3cea7f53b9e7 - languageName: node - linkType: hard - -"@docusaurus/preset-classic@npm:^2.3.6": - version: 2.4.3 - resolution: "@docusaurus/preset-classic@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/plugin-content-blog": "npm:2.4.3" - "@docusaurus/plugin-content-docs": "npm:2.4.3" - "@docusaurus/plugin-content-pages": "npm:2.4.3" - "@docusaurus/plugin-debug": "npm:2.4.3" - "@docusaurus/plugin-google-analytics": "npm:2.4.3" - "@docusaurus/plugin-google-gtag": "npm:2.4.3" - "@docusaurus/plugin-google-tag-manager": "npm:2.4.3" - "@docusaurus/plugin-sitemap": "npm:2.4.3" - "@docusaurus/theme-classic": "npm:2.4.3" - "@docusaurus/theme-common": "npm:2.4.3" - "@docusaurus/theme-search-algolia": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/2ab1e2a8f4e3b80edf1b6e8900d0c49976ff1e05b74ee5cc1d9c41b805f1dbc5948f1ad01189278682f2cbb4269dfa1d0ea0e73e3defc7406522ff047bc06321 - languageName: node - linkType: hard - -"@docusaurus/react-loadable@npm:5.5.2, react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version: 5.5.2 - resolution: "@docusaurus/react-loadable@npm:5.5.2" - dependencies: - "@types/react": "npm:*" - prop-types: "npm:^15.6.2" - peerDependencies: - react: "*" - checksum: 10c0/3f6a335d55c811c4fd40300ff0d87ae88f44f96e9c43a4c3f54f1c19b7a55bae601e43d66f797074e204699fd6abb69affa65fc4c5a819e8f1c2adb8a912da46 - languageName: node - linkType: hard - -"@docusaurus/remark-plugin-npm2yarn@npm:^2.3.6": - version: 2.4.3 - resolution: "@docusaurus/remark-plugin-npm2yarn@npm:2.4.3" - dependencies: - npm-to-yarn: "npm:^2.0.0" - tslib: "npm:^2.4.1" - unist-util-visit: "npm:^2.0.3" - checksum: 10c0/e80fe27f9d366154686c3db6328d37871d192ef6dbc0762f75852b9705cb1d193832aa74ba73cff375c175845f84c05a202165d17f2f2a2f9c525ecf4aa51f8b - languageName: node - linkType: hard - -"@docusaurus/theme-classic@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/theme-classic@npm:2.4.3" - dependencies: - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/mdx-loader": "npm:2.4.3" - "@docusaurus/module-type-aliases": "npm:2.4.3" - "@docusaurus/plugin-content-blog": "npm:2.4.3" - "@docusaurus/plugin-content-docs": "npm:2.4.3" - "@docusaurus/plugin-content-pages": "npm:2.4.3" - "@docusaurus/theme-common": "npm:2.4.3" - "@docusaurus/theme-translations": "npm:2.4.3" - "@docusaurus/types": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-common": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - "@mdx-js/react": "npm:^1.6.22" - clsx: "npm:^1.2.1" - copy-text-to-clipboard: "npm:^3.0.1" - infima: "npm:0.2.0-alpha.43" - lodash: "npm:^4.17.21" - nprogress: "npm:^0.2.0" - postcss: "npm:^8.4.14" - prism-react-renderer: "npm:^1.3.5" - prismjs: "npm:^1.28.0" - react-router-dom: "npm:^5.3.3" - rtlcss: "npm:^3.5.0" - tslib: "npm:^2.4.0" - utility-types: "npm:^3.10.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/18d2c997e416f8f933781db522f48ed2ee5d89ea80822f33ab0b4056d3866f9890cf19fdbd8e90839625aad858dcc7c63c1cf52c737a1d4ac8272606e505ec88 - languageName: node - linkType: hard - -"@docusaurus/theme-common@npm:2.4.3, @docusaurus/theme-common@npm:^2.3.6": - version: 2.4.3 - resolution: "@docusaurus/theme-common@npm:2.4.3" - dependencies: - "@docusaurus/mdx-loader": "npm:2.4.3" - "@docusaurus/module-type-aliases": "npm:2.4.3" - "@docusaurus/plugin-content-blog": "npm:2.4.3" - "@docusaurus/plugin-content-docs": "npm:2.4.3" - "@docusaurus/plugin-content-pages": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-common": "npm:2.4.3" - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - "@types/react-router-config": "npm:*" - clsx: "npm:^1.2.1" - parse-numeric-range: "npm:^1.3.0" - prism-react-renderer: "npm:^1.3.5" - tslib: "npm:^2.4.0" - use-sync-external-store: "npm:^1.2.0" - utility-types: "npm:^3.10.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/bb0c6e253a77a033d8041b7e042e67d28feb3fbc99a3c753fb45e1b4ffa38ce9f24d21b3363ff971d1d63bc34025ec5baf09a476a7c8eba372c94c5db24e8b34 - languageName: node - linkType: hard - -"@docusaurus/theme-search-algolia@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/theme-search-algolia@npm:2.4.3" - dependencies: - "@docsearch/react": "npm:^3.1.1" - "@docusaurus/core": "npm:2.4.3" - "@docusaurus/logger": "npm:2.4.3" - "@docusaurus/plugin-content-docs": "npm:2.4.3" - "@docusaurus/theme-common": "npm:2.4.3" - "@docusaurus/theme-translations": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - "@docusaurus/utils-validation": "npm:2.4.3" - algoliasearch: "npm:^4.13.1" - algoliasearch-helper: "npm:^3.10.0" - clsx: "npm:^1.2.1" - eta: "npm:^2.0.0" - fs-extra: "npm:^10.1.0" - lodash: "npm:^4.17.21" - tslib: "npm:^2.4.0" - utility-types: "npm:^3.10.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/657974a334ecaf96e8901af1c56c716b2d18e188420c828740dea12e04f49897108c621fb49319ce2b6ed82fc09ceb6931deb242ac35d0a42b96e9fad4275567 - languageName: node - linkType: hard - -"@docusaurus/theme-translations@npm:2.4.3, @docusaurus/theme-translations@npm:^2.0.0-rc.1": - version: 2.4.3 - resolution: "@docusaurus/theme-translations@npm:2.4.3" - dependencies: - fs-extra: "npm:^10.1.0" - tslib: "npm:^2.4.0" - checksum: 10c0/6b3765b73453e05e2d4b4f90a30373d1e8511ede04a6f0134157a7d79ef0f27d7a8990b30e932c769fc51058942943b01b82d73c3acae7d2573a7075e00d97dd - languageName: node - linkType: hard - -"@docusaurus/types@npm:2.4.3": - version: 2.4.3 - resolution: "@docusaurus/types@npm:2.4.3" - dependencies: - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - commander: "npm:^5.1.0" - joi: "npm:^17.6.0" - react-helmet-async: "npm:^1.3.0" - utility-types: "npm:^3.10.0" - webpack: "npm:^5.73.0" - webpack-merge: "npm:^5.8.0" - peerDependencies: - react: ^16.8.4 || ^17.0.0 - react-dom: ^16.8.4 || ^17.0.0 - checksum: 10c0/71ae109849862629033cc2f275b1064c1e2dde7f39b065793320fc1a442c40353fcc8730a7274da9814c577a059f0ddb956429228ad407ada6c0f5b5816d0aee - languageName: node - linkType: hard - -"@docusaurus/utils-common@npm:2.4.3, @docusaurus/utils-common@npm:^2.0.0-rc.1": - version: 2.4.3 - resolution: "@docusaurus/utils-common@npm:2.4.3" - dependencies: - tslib: "npm:^2.4.0" - peerDependencies: - "@docusaurus/types": "*" - peerDependenciesMeta: - "@docusaurus/types": - optional: true - checksum: 10c0/6810da88b7b2df0031ec40e9f73f34eef6c8457a40a5719eef8c8e351f880a0666570a0ed77aea9b3d85c17fedc77b9a8b58ac4426fb4f11e34bc6994601ced3 - languageName: node - linkType: hard - -"@docusaurus/utils-validation@npm:2.4.3, @docusaurus/utils-validation@npm:^2.0.0-rc.1": - version: 2.4.3 - resolution: "@docusaurus/utils-validation@npm:2.4.3" - dependencies: - "@docusaurus/logger": "npm:2.4.3" - "@docusaurus/utils": "npm:2.4.3" - joi: "npm:^17.6.0" - js-yaml: "npm:^4.1.0" - tslib: "npm:^2.4.0" - checksum: 10c0/cb8e2dc5201870ef7c42e89389ab9f0d43c2ff5be26793aa1be9432d9d3510f1696a41108e99aac439308606d8496d5b038776c4037f3925aadedeae1d56148f - languageName: node - linkType: hard - -"@docusaurus/utils@npm:2.4.3, @docusaurus/utils@npm:^2.0.0-rc.1": - version: 2.4.3 - resolution: "@docusaurus/utils@npm:2.4.3" - dependencies: - "@docusaurus/logger": "npm:2.4.3" - "@svgr/webpack": "npm:^6.2.1" - escape-string-regexp: "npm:^4.0.0" - file-loader: "npm:^6.2.0" - fs-extra: "npm:^10.1.0" - github-slugger: "npm:^1.4.0" - globby: "npm:^11.1.0" - gray-matter: "npm:^4.0.3" - js-yaml: "npm:^4.1.0" - lodash: "npm:^4.17.21" - micromatch: "npm:^4.0.5" - resolve-pathname: "npm:^3.0.0" - shelljs: "npm:^0.8.5" - tslib: "npm:^2.4.0" - url-loader: "npm:^4.1.1" - webpack: "npm:^5.73.0" - peerDependencies: - "@docusaurus/types": "*" - peerDependenciesMeta: - "@docusaurus/types": - optional: true - checksum: 10c0/84e11e0245b11f7c611297ede2deb4598dbfb62e14a7146199f6bfb0306c35d015336a2c220fb902e4d35ba432ffe86bc48a03125c4fd1f295570965f58065d2 - languageName: node - linkType: hard - -"@easyops-cn/autocomplete.js@npm:^0.38.1": - version: 0.38.1 - resolution: "@easyops-cn/autocomplete.js@npm:0.38.1" - dependencies: - cssesc: "npm:^3.0.0" - immediate: "npm:^3.2.3" - checksum: 10c0/114132fc71811cb319463ef9592179b99ab6265030e612c0c790839929764c6f632d7e155784fe89921365005ddf838cb83ed428799de67c581d25e1110bc84d - languageName: node - linkType: hard - -"@easyops-cn/docusaurus-search-local@npm:^0.33.4": - version: 0.33.6 - resolution: "@easyops-cn/docusaurus-search-local@npm:0.33.6" - dependencies: - "@docusaurus/plugin-content-docs": "npm:^2.0.0-rc.1" - "@docusaurus/theme-translations": "npm:^2.0.0-rc.1" - "@docusaurus/utils": "npm:^2.0.0-rc.1" - "@docusaurus/utils-common": "npm:^2.0.0-rc.1" - "@docusaurus/utils-validation": "npm:^2.0.0-rc.1" - "@easyops-cn/autocomplete.js": "npm:^0.38.1" - "@node-rs/jieba": "npm:^1.6.0" - cheerio: "npm:^1.0.0-rc.3" - clsx: "npm:^1.1.1" - debug: "npm:^4.2.0" - fs-extra: "npm:^10.0.0" - klaw-sync: "npm:^6.0.0" - lunr: "npm:^2.3.9" - lunr-languages: "npm:^1.4.0" - mark.js: "npm:^8.11.1" - tslib: "npm:^2.4.0" - peerDependencies: - "@docusaurus/theme-common": ^2.0.0-rc.1 - react: ^16.14.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/ba6b141afdc16b50ee0141742b1c7ee16662ec49b735dea90e2348327cc989111ab1fdd4774defcae550ff77bfd4d0506adb14a0836b9b2af3c4d63c47dfeb8e - languageName: node - linkType: hard - -"@egjs/hammerjs@npm:^2.0.17": - version: 2.0.17 - resolution: "@egjs/hammerjs@npm:2.0.17" - dependencies: - "@types/hammerjs": "npm:^2.0.36" - checksum: 10c0/dbedc15a0e633f887c08394bd636faf6a3abd05726dc0909a0e01209d5860a752d9eca5e512da623aecfabe665f49f1d035de3103eb2f9022c5cea692f9cc9be - languageName: node - linkType: hard - -"@emnapi/core@npm:^1.4.3": - version: 1.8.1 - resolution: "@emnapi/core@npm:1.8.1" - dependencies: - "@emnapi/wasi-threads": "npm:1.1.0" - tslib: "npm:^2.4.0" - checksum: 10c0/2c242f4b49779bac403e1cbcc98edacdb1c8ad36562408ba9a20663824669e930bc8493be46a2522d9dc946b8d96cd7073970bae914928c7671b5221c85b432e - languageName: node - linkType: hard - -"@emnapi/runtime@npm:^1.4.3": - version: 1.8.1 - resolution: "@emnapi/runtime@npm:1.8.1" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/f4929d75e37aafb24da77d2f58816761fe3f826aad2e37fa6d4421dac9060cbd5098eea1ac3c9ecc4526b89deb58153852fa432f87021dc57863f2ff726d713f - languageName: node - linkType: hard - -"@emnapi/wasi-threads@npm:1.1.0": - version: 1.1.0 - resolution: "@emnapi/wasi-threads@npm:1.1.0" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10c0/e6d54bf2b1e64cdd83d2916411e44e579b6ae35d5def0dea61a3c452d9921373044dff32a8b8473ae60c80692bdc39323e98b96a3f3d87ba6886b24dd0ef7ca1 - languageName: node - linkType: hard - -"@emotion/is-prop-valid@npm:^0.8.8": - version: 0.8.8 - resolution: "@emotion/is-prop-valid@npm:0.8.8" - dependencies: - "@emotion/memoize": "npm:0.7.4" - checksum: 10c0/f6be625f067c7fa56a12a4edaf090715616dc4fc7803c87212831f38c969350107b9709b1be54100e53153b18d9fa068eb4bf4f9ac66a37a8edf1bac9b64e279 - languageName: node - linkType: hard - -"@emotion/memoize@npm:0.7.4": - version: 0.7.4 - resolution: "@emotion/memoize@npm:0.7.4" - checksum: 10c0/b2376548fc147b43afd1ff005a80a1a025bd7eb4fb759fdb23e96e5ff290ee8ba16628a332848d600fb91c3cdc319eee5395fa33d8875e5d5a8c4ce18cddc18e - languageName: node - linkType: hard - -"@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": - version: 4.9.1 - resolution: "@eslint-community/eslint-utils@npm:4.9.1" - dependencies: - eslint-visitor-keys: "npm:^3.4.3" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 10c0/dc4ab5e3e364ef27e33666b11f4b86e1a6c1d7cbf16f0c6ff87b1619b3562335e9201a3d6ce806221887ff780ec9d828962a290bb910759fd40a674686503f02 - languageName: node - linkType: hard - -"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": - version: 4.12.2 - resolution: "@eslint-community/regexpp@npm:4.12.2" - checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d +"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d languageName: node linkType: hard @@ -4546,22 +3527,6 @@ __metadata: languageName: node linkType: hard -"@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": - version: 9.3.0 - resolution: "@hapi/hoek@npm:9.3.0" - checksum: 10c0/a096063805051fb8bba4c947e293c664b05a32b47e13bc654c0dd43813a1cec993bdd8f29ceb838020299e1d0f89f68dc0d62a603c13c9cc8541963f0beca055 - languageName: node - linkType: hard - -"@hapi/topo@npm:^5.1.0": - version: 5.1.0 - resolution: "@hapi/topo@npm:5.1.0" - dependencies: - "@hapi/hoek": "npm:^9.0.0" - checksum: 10c0/b16b06d9357947149e032bdf10151eb71aea8057c79c4046bf32393cb89d0d0f7ca501c40c0f7534a5ceca078de0700d2257ac855c15e59fe4e00bba2f25c86f - languageName: node - linkType: hard - "@humanfs/core@npm:^0.19.2": version: 0.19.2 resolution: "@humanfs/core@npm:0.19.2" @@ -5005,13 +3970,6 @@ __metadata: languageName: node linkType: hard -"@leichtgewicht/ip-codec@npm:^2.0.1": - version: 2.0.5 - resolution: "@leichtgewicht/ip-codec@npm:2.0.5" - checksum: 10c0/14a0112bd59615eef9e3446fea018045720cd3da85a98f801a685a818b0d96ef2a1f7227e8d271def546b2e2a0fe91ef915ba9dc912ab7967d2317b1a051d66b - languageName: node - linkType: hard - "@marionebl/sander@npm:^0.6.0": version: 0.6.1 resolution: "@marionebl/sander@npm:0.6.1" @@ -5050,39 +4008,48 @@ __metadata: languageName: node linkType: hard -"@mdx-js/mdx@npm:^1.6.22": - version: 1.6.22 - resolution: "@mdx-js/mdx@npm:1.6.22" +"@mdx-js/mdx@npm:^3.1.1": + version: 3.1.1 + resolution: "@mdx-js/mdx@npm:3.1.1" + dependencies: + "@types/estree": "npm:^1.0.0" + "@types/estree-jsx": "npm:^1.0.0" + "@types/hast": "npm:^3.0.0" + "@types/mdx": "npm:^2.0.0" + acorn: "npm:^8.0.0" + collapse-white-space: "npm:^2.0.0" + devlop: "npm:^1.0.0" + estree-util-is-identifier-name: "npm:^3.0.0" + estree-util-scope: "npm:^1.0.0" + estree-walker: "npm:^3.0.0" + hast-util-to-jsx-runtime: "npm:^2.0.0" + markdown-extensions: "npm:^2.0.0" + recma-build-jsx: "npm:^1.0.0" + recma-jsx: "npm:^1.0.0" + recma-stringify: "npm:^1.0.0" + rehype-recma: "npm:^1.0.0" + remark-mdx: "npm:^3.0.0" + remark-parse: "npm:^11.0.0" + remark-rehype: "npm:^11.0.0" + source-map: "npm:^0.7.0" + unified: "npm:^11.0.0" + unist-util-position-from-estree: "npm:^2.0.0" + unist-util-stringify-position: "npm:^4.0.0" + unist-util-visit: "npm:^5.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/371ed95e2bee7731f30a7ce57db66383a0b7470e66c38139427174cb456d6a40bf7d259f3652716370c1de64acfba50a1ba27eb8c556e7a431dc7940b04cb1a1 + languageName: node + linkType: hard + +"@mdx-js/react@npm:^3.1.1": + version: 3.1.1 + resolution: "@mdx-js/react@npm:3.1.1" dependencies: - "@babel/core": "npm:7.12.9" - "@babel/plugin-syntax-jsx": "npm:7.12.1" - "@babel/plugin-syntax-object-rest-spread": "npm:7.8.3" - "@mdx-js/util": "npm:1.6.22" - babel-plugin-apply-mdx-type-prop: "npm:1.6.22" - babel-plugin-extract-import-names: "npm:1.6.22" - camelcase-css: "npm:2.0.1" - detab: "npm:2.0.4" - hast-util-raw: "npm:6.0.1" - lodash.uniq: "npm:4.5.0" - mdast-util-to-hast: "npm:10.0.1" - remark-footnotes: "npm:2.0.0" - remark-mdx: "npm:1.6.22" - remark-parse: "npm:8.0.3" - remark-squeeze-paragraphs: "npm:4.0.0" - style-to-object: "npm:0.3.0" - unified: "npm:9.2.0" - unist-builder: "npm:2.0.3" - unist-util-visit: "npm:2.0.3" - checksum: 10c0/7f4c38911fc269159834240d3cc9279839145022a992bd61657530750c7ab5d0f674e8d6319b6e2e426d0e1adc6cc5ab1876e57548208783d8a3d1b8ef73ebca - languageName: node - linkType: hard - -"@mdx-js/react@npm:^1.6.22": - version: 1.6.22 - resolution: "@mdx-js/react@npm:1.6.22" + "@types/mdx": "npm:^2.0.0" peerDependencies: - react: ^16.13.1 || ^17.0.0 - checksum: 10c0/ed896671ffab04c1f11cdba45bfb2786acff58cd0b749b0a13d9b7a7022ac75cc036bec067ca946e6540e2934727e0ba8bf174e4ae10c916f30cda6aecac8992 + "@types/react": ">=16" + react: ">=16" + checksum: 10c0/34ca98bc2a0f969894ea144dc5c8a5294690505458cd24965cd9be854d779c193ad9192bf9143c4c18438fafd1902e100d99067e045c69319288562d497558c6 languageName: node linkType: hard @@ -5095,13 +4062,6 @@ __metadata: languageName: node linkType: hard -"@mdx-js/util@npm:1.6.22": - version: 1.6.22 - resolution: "@mdx-js/util@npm:1.6.22" - checksum: 10c0/2ee8da6afea0f42297ea31f52b1d50d228744d2895cce7cc9571b7d5ce97c7c96037c80b6dbcded9caa8099c9a994eda62980099eabe1c000aaa792816c66f10 - languageName: node - linkType: hard - "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3": version: 3.0.3 resolution: "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3" @@ -5144,14 +4104,15 @@ __metadata: languageName: node linkType: hard -"@napi-rs/wasm-runtime@npm:^0.2.3": - version: 0.2.12 - resolution: "@napi-rs/wasm-runtime@npm:0.2.12" +"@napi-rs/wasm-runtime@npm:1.1.4": + version: 1.1.4 + resolution: "@napi-rs/wasm-runtime@npm:1.1.4" dependencies: - "@emnapi/core": "npm:^1.4.3" - "@emnapi/runtime": "npm:^1.4.3" - "@tybys/wasm-util": "npm:^0.10.0" - checksum: 10c0/6d07922c0613aab30c6a497f4df297ca7c54e5b480e00035e0209b872d5c6aab7162fc49477267556109c2c7ed1eb9c65a174e27e9b87568106a87b0a6e3ca7d + "@tybys/wasm-util": "npm:^0.10.1" + peerDependencies: + "@emnapi/core": ^1.7.1 + "@emnapi/runtime": ^1.7.1 + checksum: 10c0/2e88e1955258949ccf2d18c79975821ad38071b465ef126a5e14110977b97868867b016c1ad046e963cccc42c0bd9db6c8ff5fd1ebb61b87bb3487f339041658 languageName: node linkType: hard @@ -5164,238 +4125,87 @@ __metadata: languageName: node linkType: hard -"@node-rs/jieba-android-arm-eabi@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-android-arm-eabi@npm:1.10.4" - conditions: os=android & cpu=arm +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb languageName: node linkType: hard -"@node-rs/jieba-android-arm64@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-android-arm64@npm:1.10.4" - conditions: os=android & cpu=arm64 +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d languageName: node linkType: hard -"@node-rs/jieba-darwin-arm64@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-darwin-arm64@npm:1.10.4" - conditions: os=darwin & cpu=arm64 +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 languageName: node linkType: hard -"@node-rs/jieba-darwin-x64@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-darwin-x64@npm:1.10.4" - conditions: os=darwin & cpu=x64 +"@npmcli/agent@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/agent@npm:4.0.0" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^11.2.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10c0/f7b5ce0f3dd42c3f8c6546e8433573d8049f67ef11ec22aa4704bc41483122f68bf97752e06302c455ead667af5cb753e6a09bff06632bc465c1cfd4c4b75a53 languageName: node linkType: hard -"@node-rs/jieba-freebsd-x64@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-freebsd-x64@npm:1.10.4" - conditions: os=freebsd & cpu=x64 +"@npmcli/fs@npm:^5.0.0": + version: 5.0.0 + resolution: "@npmcli/fs@npm:5.0.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/26e376d780f60ff16e874a0ac9bc3399186846baae0b6e1352286385ac134d900cc5dafaded77f38d77f86898fc923ae1cee9d7399f0275b1aa24878915d722b languageName: node linkType: hard -"@node-rs/jieba-linux-arm-gnueabihf@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-linux-arm-gnueabihf@npm:1.10.4" - conditions: os=linux & cpu=arm +"@octokit/auth-token@npm:^2.4.4": + version: 2.5.0 + resolution: "@octokit/auth-token@npm:2.5.0" + dependencies: + "@octokit/types": "npm:^6.0.3" + checksum: 10c0/e9f757b6acdee91885dab97069527c86829da0dc60476c38cdff3a739ff47fd026262715965f91e84ec9d01bc43d02678bc8ed472a85395679af621b3ddbe045 languageName: node linkType: hard -"@node-rs/jieba-linux-arm64-gnu@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-linux-arm64-gnu@npm:1.10.4" - conditions: os=linux & cpu=arm64 & libc=glibc +"@octokit/core@npm:^3.5.0": + version: 3.6.0 + resolution: "@octokit/core@npm:3.6.0" + dependencies: + "@octokit/auth-token": "npm:^2.4.4" + "@octokit/graphql": "npm:^4.5.8" + "@octokit/request": "npm:^5.6.3" + "@octokit/request-error": "npm:^2.0.5" + "@octokit/types": "npm:^6.0.3" + before-after-hook: "npm:^2.2.0" + universal-user-agent: "npm:^6.0.0" + checksum: 10c0/78d9799a57fe9cf155cce485ba8b7ec32f05024350bf5dd8ab5e0da8995cc22168c39dbbbcfc29bc6c562dd482c1c4a3064f466f49e2e9ce4efad57cf28a7360 languageName: node linkType: hard -"@node-rs/jieba-linux-arm64-musl@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-linux-arm64-musl@npm:1.10.4" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@node-rs/jieba-linux-x64-gnu@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-linux-x64-gnu@npm:1.10.4" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@node-rs/jieba-linux-x64-musl@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-linux-x64-musl@npm:1.10.4" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@node-rs/jieba-wasm32-wasi@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-wasm32-wasi@npm:1.10.4" - dependencies: - "@napi-rs/wasm-runtime": "npm:^0.2.3" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@node-rs/jieba-win32-arm64-msvc@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-win32-arm64-msvc@npm:1.10.4" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@node-rs/jieba-win32-ia32-msvc@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-win32-ia32-msvc@npm:1.10.4" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@node-rs/jieba-win32-x64-msvc@npm:1.10.4": - version: 1.10.4 - resolution: "@node-rs/jieba-win32-x64-msvc@npm:1.10.4" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@node-rs/jieba@npm:^1.6.0": - version: 1.10.4 - resolution: "@node-rs/jieba@npm:1.10.4" - dependencies: - "@node-rs/jieba-android-arm-eabi": "npm:1.10.4" - "@node-rs/jieba-android-arm64": "npm:1.10.4" - "@node-rs/jieba-darwin-arm64": "npm:1.10.4" - "@node-rs/jieba-darwin-x64": "npm:1.10.4" - "@node-rs/jieba-freebsd-x64": "npm:1.10.4" - "@node-rs/jieba-linux-arm-gnueabihf": "npm:1.10.4" - "@node-rs/jieba-linux-arm64-gnu": "npm:1.10.4" - "@node-rs/jieba-linux-arm64-musl": "npm:1.10.4" - "@node-rs/jieba-linux-x64-gnu": "npm:1.10.4" - "@node-rs/jieba-linux-x64-musl": "npm:1.10.4" - "@node-rs/jieba-wasm32-wasi": "npm:1.10.4" - "@node-rs/jieba-win32-arm64-msvc": "npm:1.10.4" - "@node-rs/jieba-win32-ia32-msvc": "npm:1.10.4" - "@node-rs/jieba-win32-x64-msvc": "npm:1.10.4" - dependenciesMeta: - "@node-rs/jieba-android-arm-eabi": - optional: true - "@node-rs/jieba-android-arm64": - optional: true - "@node-rs/jieba-darwin-arm64": - optional: true - "@node-rs/jieba-darwin-x64": - optional: true - "@node-rs/jieba-freebsd-x64": - optional: true - "@node-rs/jieba-linux-arm-gnueabihf": - optional: true - "@node-rs/jieba-linux-arm64-gnu": - optional: true - "@node-rs/jieba-linux-arm64-musl": - optional: true - "@node-rs/jieba-linux-x64-gnu": - optional: true - "@node-rs/jieba-linux-x64-musl": - optional: true - "@node-rs/jieba-wasm32-wasi": - optional: true - "@node-rs/jieba-win32-arm64-msvc": - optional: true - "@node-rs/jieba-win32-ia32-msvc": - optional: true - "@node-rs/jieba-win32-x64-msvc": - optional: true - checksum: 10c0/9c51e51e9c0319e1a9d658bf08adf7b2ace06e30fb248ef499fe0059fc582071e8e1d197783c1fcf084cf0d738c3828f7fd50f35735f2b1cf9d003171b8e43e2 - languageName: node - linkType: hard - -"@nodelib/fs.scandir@npm:2.1.5": - version: 2.1.5 - resolution: "@nodelib/fs.scandir@npm:2.1.5" - dependencies: - "@nodelib/fs.stat": "npm:2.0.5" - run-parallel: "npm:^1.1.9" - checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb - languageName: node - linkType: hard - -"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": - version: 2.0.5 - resolution: "@nodelib/fs.stat@npm:2.0.5" - checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d - languageName: node - linkType: hard - -"@nodelib/fs.walk@npm:^1.2.3": - version: 1.2.8 - resolution: "@nodelib/fs.walk@npm:1.2.8" - dependencies: - "@nodelib/fs.scandir": "npm:2.1.5" - fastq: "npm:^1.6.0" - checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 - languageName: node - linkType: hard - -"@npmcli/agent@npm:^4.0.0": - version: 4.0.0 - resolution: "@npmcli/agent@npm:4.0.0" - dependencies: - agent-base: "npm:^7.1.0" - http-proxy-agent: "npm:^7.0.0" - https-proxy-agent: "npm:^7.0.1" - lru-cache: "npm:^11.2.1" - socks-proxy-agent: "npm:^8.0.3" - checksum: 10c0/f7b5ce0f3dd42c3f8c6546e8433573d8049f67ef11ec22aa4704bc41483122f68bf97752e06302c455ead667af5cb753e6a09bff06632bc465c1cfd4c4b75a53 - languageName: node - linkType: hard - -"@npmcli/fs@npm:^5.0.0": - version: 5.0.0 - resolution: "@npmcli/fs@npm:5.0.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10c0/26e376d780f60ff16e874a0ac9bc3399186846baae0b6e1352286385ac134d900cc5dafaded77f38d77f86898fc923ae1cee9d7399f0275b1aa24878915d722b - languageName: node - linkType: hard - -"@octokit/auth-token@npm:^2.4.4": - version: 2.5.0 - resolution: "@octokit/auth-token@npm:2.5.0" - dependencies: - "@octokit/types": "npm:^6.0.3" - checksum: 10c0/e9f757b6acdee91885dab97069527c86829da0dc60476c38cdff3a739ff47fd026262715965f91e84ec9d01bc43d02678bc8ed472a85395679af621b3ddbe045 - languageName: node - linkType: hard - -"@octokit/core@npm:^3.5.0": - version: 3.6.0 - resolution: "@octokit/core@npm:3.6.0" - dependencies: - "@octokit/auth-token": "npm:^2.4.4" - "@octokit/graphql": "npm:^4.5.8" - "@octokit/request": "npm:^5.6.3" - "@octokit/request-error": "npm:^2.0.5" - "@octokit/types": "npm:^6.0.3" - before-after-hook: "npm:^2.2.0" - universal-user-agent: "npm:^6.0.0" - checksum: 10c0/78d9799a57fe9cf155cce485ba8b7ec32f05024350bf5dd8ab5e0da8995cc22168c39dbbbcfc29bc6c562dd482c1c4a3064f466f49e2e9ce4efad57cf28a7360 - languageName: node - linkType: hard - -"@octokit/endpoint@npm:^6.0.1": - version: 6.0.12 - resolution: "@octokit/endpoint@npm:6.0.12" - dependencies: - "@octokit/types": "npm:^6.0.3" - is-plain-object: "npm:^5.0.0" - universal-user-agent: "npm:^6.0.0" - checksum: 10c0/b2d9c91f00ab7c997338d08a06bfd12a67d86060bc40471f921ba424e4de4e5a0a1117631f2a8a8787107d89d631172dd157cb5e2633674b1ae3a0e2b0dcfa3e +"@octokit/endpoint@npm:^6.0.1": + version: 6.0.12 + resolution: "@octokit/endpoint@npm:6.0.12" + dependencies: + "@octokit/types": "npm:^6.0.3" + is-plain-object: "npm:^5.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: 10c0/b2d9c91f00ab7c997338d08a06bfd12a67d86060bc40471f921ba424e4de4e5a0a1117631f2a8a8787107d89d631172dd157cb5e2633674b1ae3a0e2b0dcfa3e languageName: node linkType: hard @@ -5502,13 +4312,6 @@ __metadata: languageName: node linkType: hard -"@polka/url@npm:^1.0.0-next.24": - version: 1.0.0-next.29 - resolution: "@polka/url@npm:1.0.0-next.29" - checksum: 10c0/0d58e081844095cb029d3c19a659bfefd09d5d51a2f791bc61eba7ea826f13d6ee204a8a448c2f5a855c17df07b37517373ff916dd05801063c0568ae9937684 - languageName: node - linkType: hard - "@react-native-async-storage/async-storage@npm:2.2.0": version: 2.2.0 resolution: "@react-native-async-storage/async-storage@npm:2.2.0" @@ -5893,253 +4696,409 @@ __metadata: languageName: node linkType: hard -"@rtsao/scc@npm:^1.1.0": - version: 1.1.0 - resolution: "@rtsao/scc@npm:1.1.0" - checksum: 10c0/b5bcfb0d87f7d1c1c7c0f7693f53b07866ed9fec4c34a97a8c948fb9a7c0082e416ce4d3b60beb4f5e167cbe04cdeefbf6771320f3ede059b9ce91188c409a5b +"@rsbuild/core@npm:^2.0.9": + version: 2.0.15 + resolution: "@rsbuild/core@npm:2.0.15" + dependencies: + "@rspack/core": "npm:~2.0.8" + "@swc/helpers": "npm:^0.5.23" + peerDependencies: + core-js: ">= 3.0.0" + peerDependenciesMeta: + core-js: + optional: true + bin: + rsbuild: ./bin/rsbuild.js + checksum: 10c0/0aa77a2e4928e1b62b26fa986467d8226a89f2fb0680ae825a36f7e581a976e6bb43debcca4dcdb7fd8d68b05a7f7ef2e5b25b9769afd70ea2a65c1103b59e2e languageName: node linkType: hard -"@sideway/address@npm:^4.1.5": - version: 4.1.5 - resolution: "@sideway/address@npm:4.1.5" +"@rsbuild/plugin-react@npm:~2.0.1": + version: 2.0.1 + resolution: "@rsbuild/plugin-react@npm:2.0.1" dependencies: - "@hapi/hoek": "npm:^9.0.0" - checksum: 10c0/638eb6f7e7dba209053dd6c8da74d7cc995e2b791b97644d0303a7dd3119263bcb7225a4f6804d4db2bc4f96e5a9d262975a014f58eae4d1753c27cbc96ef959 + "@rspack/plugin-react-refresh": "npm:2.0.0" + react-refresh: "npm:^0.18.0" + peerDependencies: + "@rsbuild/core": ^2.0.0-0 + peerDependenciesMeta: + "@rsbuild/core": + optional: true + checksum: 10c0/b9bb8941ca8081a7231e5b61fe071f8eda79f09ef3177b1ba4d2472ad6df749931446e0a3c71f2de930b8cdf05eac90d37d7f50f498752577516a694c2ee43e9 languageName: node linkType: hard -"@sideway/formula@npm:^3.0.1": - version: 3.0.1 - resolution: "@sideway/formula@npm:3.0.1" - checksum: 10c0/3fe81fa9662efc076bf41612b060eb9b02e846ea4bea5bd114f1662b7f1541e9dedcf98aff0d24400bcb92f113964a50e0290b86e284edbdf6346fa9b7e2bf2c +"@rspack/binding-darwin-arm64@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-darwin-arm64@npm:2.0.8" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@sideway/pinpoint@npm:^2.0.0": - version: 2.0.0 - resolution: "@sideway/pinpoint@npm:2.0.0" - checksum: 10c0/d2ca75dacaf69b8fc0bb8916a204e01def3105ee44d8be16c355e5f58189eb94039e15ce831f3d544f229889ccfa35562a0ce2516179f3a7ee1bbe0b71e55b36 +"@rspack/binding-darwin-x64@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-darwin-x64@npm:2.0.8" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@sinclair/typebox@npm:^0.27.8": - version: 0.27.8 - resolution: "@sinclair/typebox@npm:0.27.8" - checksum: 10c0/ef6351ae073c45c2ac89494dbb3e1f87cc60a93ce4cde797b782812b6f97da0d620ae81973f104b43c9b7eaa789ad20ba4f6a1359f1cc62f63729a55a7d22d4e +"@rspack/binding-linux-arm64-gnu@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-linux-arm64-gnu@npm:2.0.8" + conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@sinclair/typebox@npm:^0.34.0": - version: 0.34.49 - resolution: "@sinclair/typebox@npm:0.34.49" - checksum: 10c0/16b7d87f039a49b68c10bb4cdcae2ce5242b2472228851fd6483731616aba4ef977690aa517b230a8d20da8185bb416eb34e326f30568b3963c1cf26b05d1ad8 +"@rspack/binding-linux-arm64-musl@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-linux-arm64-musl@npm:2.0.8" + conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@sindresorhus/is@npm:^0.14.0": - version: 0.14.0 - resolution: "@sindresorhus/is@npm:0.14.0" - checksum: 10c0/7247aa9314d4fc3df9b3f63d8b5b962a89c7600a5db1f268546882bfc4d31a975a899f5f42a09dd41a11e58636e6402f7c40f92df853aee417247bb11faee9a0 +"@rspack/binding-linux-x64-gnu@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-linux-x64-gnu@npm:2.0.8" + conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@sindresorhus/is@npm:^4.0.0": - version: 4.6.0 - resolution: "@sindresorhus/is@npm:4.6.0" - checksum: 10c0/33b6fb1d0834ec8dd7689ddc0e2781c2bfd8b9c4e4bacbcb14111e0ae00621f2c264b8a7d36541799d74888b5dccdf422a891a5cb5a709ace26325eedc81e22e +"@rspack/binding-linux-x64-musl@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-linux-x64-musl@npm:2.0.8" + conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@sindresorhus/merge-streams@npm:^2.1.0": - version: 2.3.0 - resolution: "@sindresorhus/merge-streams@npm:2.3.0" - checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 +"@rspack/binding-wasm32-wasi@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-wasm32-wasi@npm:2.0.8" + dependencies: + "@emnapi/core": "npm:1.10.0" + "@emnapi/runtime": "npm:1.10.0" + "@napi-rs/wasm-runtime": "npm:1.1.4" + conditions: cpu=wasm32 languageName: node linkType: hard -"@sinonjs/commons@npm:^3.0.0": - version: 3.0.1 - resolution: "@sinonjs/commons@npm:3.0.1" - dependencies: - type-detect: "npm:4.0.8" - checksum: 10c0/1227a7b5bd6c6f9584274db996d7f8cee2c8c350534b9d0141fc662eaf1f292ea0ae3ed19e5e5271c8fd390d27e492ca2803acd31a1978be2cdc6be0da711403 +"@rspack/binding-win32-arm64-msvc@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-win32-arm64-msvc@npm:2.0.8" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^10.0.2": - version: 10.3.0 - resolution: "@sinonjs/fake-timers@npm:10.3.0" - dependencies: - "@sinonjs/commons": "npm:^3.0.0" - checksum: 10c0/2e2fb6cc57f227912814085b7b01fede050cd4746ea8d49a1e44d5a0e56a804663b0340ae2f11af7559ea9bf4d087a11f2f646197a660ea3cb04e19efc04aa63 +"@rspack/binding-win32-ia32-msvc@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-win32-ia32-msvc@npm:2.0.8" + conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@slorber/static-site-generator-webpack-plugin@npm:^4.0.7": - version: 4.0.7 - resolution: "@slorber/static-site-generator-webpack-plugin@npm:4.0.7" - dependencies: - eval: "npm:^0.1.8" - p-map: "npm:^4.0.0" - webpack-sources: "npm:^3.2.2" - checksum: 10c0/6ba8abc2d99e8c513bb955502f9cd219c78b2c7b9b76668bf05067cf369cfa838089b52ee51c957e1e6e8442f9dd4f2bbd8df706a3c3388e9a0d41b09a895f97 +"@rspack/binding-win32-x64-msvc@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding-win32-x64-msvc@npm:2.0.8" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@svgr/babel-plugin-add-jsx-attribute@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:6.5.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/a13ed0797189d5497890530449029bec388310e260a96459e304e2729e7a2cf4d20d34f882d9a77ccce73dd3d36065afbb6987258fdff618d7d57955065a8ad4 +"@rspack/binding@npm:2.0.8": + version: 2.0.8 + resolution: "@rspack/binding@npm:2.0.8" + dependencies: + "@rspack/binding-darwin-arm64": "npm:2.0.8" + "@rspack/binding-darwin-x64": "npm:2.0.8" + "@rspack/binding-linux-arm64-gnu": "npm:2.0.8" + "@rspack/binding-linux-arm64-musl": "npm:2.0.8" + "@rspack/binding-linux-x64-gnu": "npm:2.0.8" + "@rspack/binding-linux-x64-musl": "npm:2.0.8" + "@rspack/binding-wasm32-wasi": "npm:2.0.8" + "@rspack/binding-win32-arm64-msvc": "npm:2.0.8" + "@rspack/binding-win32-ia32-msvc": "npm:2.0.8" + "@rspack/binding-win32-x64-msvc": "npm:2.0.8" + dependenciesMeta: + "@rspack/binding-darwin-arm64": + optional: true + "@rspack/binding-darwin-x64": + optional: true + "@rspack/binding-linux-arm64-gnu": + optional: true + "@rspack/binding-linux-arm64-musl": + optional: true + "@rspack/binding-linux-x64-gnu": + optional: true + "@rspack/binding-linux-x64-musl": + optional: true + "@rspack/binding-wasm32-wasi": + optional: true + "@rspack/binding-win32-arm64-msvc": + optional: true + "@rspack/binding-win32-ia32-msvc": + optional: true + "@rspack/binding-win32-x64-msvc": + optional: true + checksum: 10c0/a5a61d6ea25fafb002bbdb6bc2600c4fba1ae1e2da3d4afd86b458eb83bfff413fa2415ecd83ffd7c4e8aa60f2cc90df6793a27e56681a5fdb415d0df428e42b languageName: node linkType: hard -"@svgr/babel-plugin-remove-jsx-attribute@npm:*": - version: 8.0.0 - resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0" +"@rspack/core@npm:~2.0.8": + version: 2.0.8 + resolution: "@rspack/core@npm:2.0.8" + dependencies: + "@rspack/binding": "npm:2.0.8" peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/8a98e59bd9971e066815b4129409932f7a4db4866834fe75677ea6d517972fb40b380a69a4413189f20e7947411f9ab1b0f029dd5e8068686a5a0188d3ccd4c7 + "@module-federation/runtime-tools": ^0.24.1 || ^2.0.0 + "@swc/helpers": ^0.5.23 + peerDependenciesMeta: + "@module-federation/runtime-tools": + optional: true + "@swc/helpers": + optional: true + checksum: 10c0/7862d5e17df9bcf70de1472142df047188a1a87c028481225702af15d69940d9935b91502e05d04f42ccc12fcdd70d6d846222817a2054b18135b4143d483047 languageName: node linkType: hard -"@svgr/babel-plugin-remove-jsx-empty-expression@npm:*": - version: 8.0.0 - resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0" +"@rspack/plugin-react-refresh@npm:2.0.0": + version: 2.0.0 + resolution: "@rspack/plugin-react-refresh@npm:2.0.0" peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/517dcca75223bd05d3f056a8514dbba3031278bea4eadf0842c576d84f4651e7a4e0e7082d3ee4ef42456de0f9c4531d8a1917c04876ca64b014b859ca8f1bde + "@rspack/core": ^2.0.0-0 + react-refresh: ">=0.10.0 <1.0.0" + peerDependenciesMeta: + "@rspack/core": + optional: true + checksum: 10c0/ee8e6492c0772536abb98ab5ec92d0e4ad092613a3a6085542f0b354131eef48f4d7cbe9a8cf9a9389d5e51d623d08a2c07594c8add5ad5fb54fd5543150ead1 languageName: node linkType: hard -"@svgr/babel-plugin-replace-jsx-attribute-value@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:6.5.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/318786787c9a217c33a7340c8856436858e1fffa5a6df635fedc6b9a371f3afea080ea074b9e3cfbbd9dd962ead924fde8bc9855a394c38dd60e391883a58c81 +"@rspress/core@npm:2.0.14": + version: 2.0.14 + resolution: "@rspress/core@npm:2.0.14" + dependencies: + "@mdx-js/mdx": "npm:^3.1.1" + "@mdx-js/react": "npm:^3.1.1" + "@rsbuild/core": "npm:^2.0.9" + "@rsbuild/plugin-react": "npm:~2.0.1" + "@rspress/shared": "npm:2.0.14" + "@shikijs/rehype": "npm:^4.0.2" + "@types/unist": "npm:^3.0.3" + "@unhead/react": "npm:^2.1.15" + body-scroll-lock: "npm:4.0.0-beta.0" + clsx: "npm:2.1.1" + copy-to-clipboard: "npm:^3.3.3" + flexsearch: "npm:0.8.212" + hast-util-heading-rank: "npm:^3.0.0" + hast-util-to-jsx-runtime: "npm:^2.3.6" + mdast-util-mdx: "npm:^3.0.0" + mdast-util-mdxjs-esm: "npm:^2.0.1" + medium-zoom: "npm:1.1.0" + nprogress: "npm:^0.2.0" + react: "npm:^19.2.6" + react-dom: "npm:^19.2.6" + react-lazy-with-preload: "npm:^2.2.1" + react-reconciler: "npm:0.33.0" + react-render-to-markdown: "npm:19.1.0" + react-router-dom: "npm:^7.15.1" + rehype-external-links: "npm:^3.0.0" + rehype-raw: "npm:^7.0.0" + remark-cjk-friendly: "npm:^2.0.1" + remark-cjk-friendly-gfm-strikethrough: "npm:^2.0.1" + remark-gfm: "npm:^4.0.1" + remark-mdx: "npm:^3.1.1" + remark-parse: "npm:^11.0.0" + remark-stringify: "npm:^11.0.0" + scroll-into-view-if-needed: "npm:^3.1.0" + shiki: "npm:^4.0.2" + unified: "npm:^11.0.5" + unist-util-remove: "npm:^4.0.0" + unist-util-visit: "npm:^5.1.0" + unist-util-visit-children: "npm:^3.0.0" + bin: + rspress: bin/rspress.js + checksum: 10c0/118ae88259f55dae748f92bdb64a7185861612fe5d784ebb5991b2598b85320ec9a718a72f6015542b79f620c358a34ed9f4f83cf98ff2aab45036f44b0f9455 languageName: node linkType: hard -"@svgr/babel-plugin-svg-dynamic-title@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:6.5.1" +"@rspress/plugin-sitemap@npm:^2.0.8": + version: 2.0.14 + resolution: "@rspress/plugin-sitemap@npm:2.0.14" peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/16ef228c793b909fec47dd7dc05c1c3c2d77a824f42055df37e141e0534081b1bc4aec6dcc51be50c221df9f262f59270fc1c379923bfd4f5db302abafabfd8d + "@rspress/core": ^2.0.10 + checksum: 10c0/d8d1c902cdc5315ecbefb30b9c2a3530031e153d679fc547b8b916a7c8f390c097ffe9150a99dc2117a887c6923f13e06e081a8b134ce0d3bab9608fc6d2c8df languageName: node linkType: hard -"@svgr/babel-plugin-svg-em-dimensions@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:6.5.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/dfdd5cbe6ae543505eaa0da69df0735b7407294c4b0504b3e74c0e7e371f1acb914eb99fd21ff39ef5bd626b3474f064a4cccc50f41b7c556ee834f9a6d6610a +"@rspress/shared@npm:2.0.14": + version: 2.0.14 + resolution: "@rspress/shared@npm:2.0.14" + dependencies: + "@rsbuild/core": "npm:^2.0.9" + "@shikijs/rehype": "npm:^4.0.2" + unified: "npm:^11.0.5" + checksum: 10c0/a33bec3b057886e549f5fbd5b6c754e770836533346b339241d2cc77a8792ee501654434afceac459fcc5b1bf1a7b0d1d2f940ba296df57a628e9697accde596 languageName: node linkType: hard -"@svgr/babel-plugin-transform-react-native-svg@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:6.5.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/332fbf3bbc19d938b744440dbab9c8acd8f7a2ed6bf9c4e23f40e3f2c25615a60b3bf00902a4f1f6c20b5f382a1547b3acc6f2b2d70d80e532b5d45945f1b979 +"@rtsao/scc@npm:^1.1.0": + version: 1.1.0 + resolution: "@rtsao/scc@npm:1.1.0" + checksum: 10c0/b5bcfb0d87f7d1c1c7c0f7693f53b07866ed9fec4c34a97a8c948fb9a7c0082e416ce4d3b60beb4f5e167cbe04cdeefbf6771320f3ede059b9ce91188c409a5b languageName: node linkType: hard -"@svgr/babel-plugin-transform-svg-component@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-plugin-transform-svg-component@npm:6.5.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/8d9e1c7c62abce23837e53cdacc6d09bc1f1f2b0ad7322105001c097995e9aa8dca4fa41acf39148af69f342e40081c438106949fb083e997ca497cb0448f27d +"@shikijs/core@npm:4.2.0": + version: 4.2.0 + resolution: "@shikijs/core@npm:4.2.0" + dependencies: + "@shikijs/primitive": "npm:4.2.0" + "@shikijs/types": "npm:4.2.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.4" + hast-util-to-html: "npm:^9.0.5" + checksum: 10c0/86828109468e6de10a9976d951ab09c124b06ded3f46cf8043a3e45d7f78f1d6fe24abbdf0044e7fe4fd734e837ecd9b63b7fa964eccadb7e1b0c95519c44f5a languageName: node linkType: hard -"@svgr/babel-preset@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/babel-preset@npm:6.5.1" +"@shikijs/engine-javascript@npm:4.2.0": + version: 4.2.0 + resolution: "@shikijs/engine-javascript@npm:4.2.0" dependencies: - "@svgr/babel-plugin-add-jsx-attribute": "npm:^6.5.1" - "@svgr/babel-plugin-remove-jsx-attribute": "npm:*" - "@svgr/babel-plugin-remove-jsx-empty-expression": "npm:*" - "@svgr/babel-plugin-replace-jsx-attribute-value": "npm:^6.5.1" - "@svgr/babel-plugin-svg-dynamic-title": "npm:^6.5.1" - "@svgr/babel-plugin-svg-em-dimensions": "npm:^6.5.1" - "@svgr/babel-plugin-transform-react-native-svg": "npm:^6.5.1" - "@svgr/babel-plugin-transform-svg-component": "npm:^6.5.1" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/8e8d7a0049279152f9ac308fbfd4ce74063d8a376154718cba6309bae4316318804a32201c75c5839c629f8e1e5d641a87822764000998161d0fc1de24b0374a + "@shikijs/types": "npm:4.2.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + oniguruma-to-es: "npm:^4.3.6" + checksum: 10c0/681045e8acfe19feaf5bb2b282ffdd006a5a06c666a726f844f8bd489c9c12eb5290cd29b97655d43a366370f2edac4eea868d8221637a5fcdc886e562bef0c6 languageName: node linkType: hard -"@svgr/core@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/core@npm:6.5.1" +"@shikijs/engine-oniguruma@npm:4.2.0": + version: 4.2.0 + resolution: "@shikijs/engine-oniguruma@npm:4.2.0" dependencies: - "@babel/core": "npm:^7.19.6" - "@svgr/babel-preset": "npm:^6.5.1" - "@svgr/plugin-jsx": "npm:^6.5.1" - camelcase: "npm:^6.2.0" - cosmiconfig: "npm:^7.0.1" - checksum: 10c0/60cce11e13391171132115dcc8da592d23e51f155ebadf9b819bd1836b8c13d40aa5c30a03a7d429f65e70a71c50669b2e10c94e4922de4e58bc898275f46c05 + "@shikijs/types": "npm:4.2.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + checksum: 10c0/ffba487224c9d96735a251b2aacd33a1769d27f4ad6b80b83bb025abd7ae2195c7e07dfc96c5978a6cc4b32d55b232dc9fe6e67c0752a78962c649891700bf7f languageName: node linkType: hard -"@svgr/hast-util-to-babel-ast@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/hast-util-to-babel-ast@npm:6.5.1" +"@shikijs/langs@npm:4.2.0": + version: 4.2.0 + resolution: "@shikijs/langs@npm:4.2.0" dependencies: - "@babel/types": "npm:^7.20.0" - entities: "npm:^4.4.0" - checksum: 10c0/18fa37b36581ba1678f5cc5a05ce0411e08df4db267f3cd900af7ffdf5bd90522f3a46465f315cd5d7345264949479133930aafdd27ce05c474e63756196256f + "@shikijs/types": "npm:4.2.0" + checksum: 10c0/a069467bf1dc458a1deef91388e3b48dffeef20502d51fa0c548c0dc3adfc4222c789fa47975ff28b0f9fe76de139899830ad320cfddc99ce2c193b05de69937 languageName: node linkType: hard -"@svgr/plugin-jsx@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/plugin-jsx@npm:6.5.1" +"@shikijs/primitive@npm:4.2.0": + version: 4.2.0 + resolution: "@shikijs/primitive@npm:4.2.0" dependencies: - "@babel/core": "npm:^7.19.6" - "@svgr/babel-preset": "npm:^6.5.1" - "@svgr/hast-util-to-babel-ast": "npm:^6.5.1" - svg-parser: "npm:^2.0.4" - peerDependencies: - "@svgr/core": ^6.0.0 - checksum: 10c0/365da6e43ceeff6b49258fa2fbb3c880210300e4a85ba74831e92d2dc9c53e6ab8dda422dc33fb6a339803227cf8d9a0024ce769401c46fd87209abe36d5ae43 + "@shikijs/types": "npm:4.2.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.4" + checksum: 10c0/8acd8af89d427bc8e1c0c9a98be8ca3ac0b7cb193039c59389fc80a4747daaf4ee0ee273d33e99e94e969be1ca3eb33da700caabd17218c848b11526cb6eaf52 languageName: node linkType: hard -"@svgr/plugin-svgo@npm:^6.5.1": - version: 6.5.1 - resolution: "@svgr/plugin-svgo@npm:6.5.1" +"@shikijs/rehype@npm:^4.0.2": + version: 4.2.0 + resolution: "@shikijs/rehype@npm:4.2.0" dependencies: - cosmiconfig: "npm:^7.0.1" - deepmerge: "npm:^4.2.2" - svgo: "npm:^2.8.0" - peerDependencies: - "@svgr/core": "*" - checksum: 10c0/da40e461145af1a92fd2ec50ea64626681fa73786f218497a4b4fb85393a58812999ca2744ee33bb7ab771aa5ce9ab1dbd08a189cb3d7a89fb58fd96913ddf91 + "@shikijs/types": "npm:4.2.0" + "@types/hast": "npm:^3.0.4" + hast-util-to-string: "npm:^3.0.1" + shiki: "npm:4.2.0" + unified: "npm:^11.0.5" + unist-util-visit: "npm:^5.1.0" + checksum: 10c0/209e03b211e9be445fa7dcc592a399c0df41e598fbd70340b3b3e736c1ced98b6463c041727cf1fe1d7de4f49306d880ee9a3d811d2d2c20d793881dc8a60f88 + languageName: node + linkType: hard + +"@shikijs/themes@npm:4.2.0": + version: 4.2.0 + resolution: "@shikijs/themes@npm:4.2.0" + dependencies: + "@shikijs/types": "npm:4.2.0" + checksum: 10c0/95c3611fab74802efe6025154a7f145fe5c71fb7ad8a998b061b0a628788bce667f942ade9bd8f0c16b47d2afde10a4bbeccbd455c96f35e21ccecf126740c7d + languageName: node + linkType: hard + +"@shikijs/types@npm:4.2.0": + version: 4.2.0 + resolution: "@shikijs/types@npm:4.2.0" + dependencies: + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.4" + checksum: 10c0/d78095c1df9ec8ee0a3cd3e203b9431943f88d1f5efce5cfabad87935f6bcd9628d3f450b42c8e65e7119fd94549e84ae4c5ac564cb55424bab12f67f26a0f0b + languageName: node + linkType: hard + +"@shikijs/vscode-textmate@npm:^10.0.2": + version: 10.0.2 + resolution: "@shikijs/vscode-textmate@npm:10.0.2" + checksum: 10c0/36b682d691088ec244de292dc8f91b808f95c89466af421cf84cbab92230f03c8348649c14b3251991b10ce632b0c715e416e992dd5f28ff3221dc2693fd9462 + languageName: node + linkType: hard + +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 10c0/ef6351ae073c45c2ac89494dbb3e1f87cc60a93ce4cde797b782812b6f97da0d620ae81973f104b43c9b7eaa789ad20ba4f6a1359f1cc62f63729a55a7d22d4e + languageName: node + linkType: hard + +"@sinclair/typebox@npm:^0.34.0": + version: 0.34.49 + resolution: "@sinclair/typebox@npm:0.34.49" + checksum: 10c0/16b7d87f039a49b68c10bb4cdcae2ce5242b2472228851fd6483731616aba4ef977690aa517b230a8d20da8185bb416eb34e326f30568b3963c1cf26b05d1ad8 + languageName: node + linkType: hard + +"@sindresorhus/is@npm:^0.14.0": + version: 0.14.0 + resolution: "@sindresorhus/is@npm:0.14.0" + checksum: 10c0/7247aa9314d4fc3df9b3f63d8b5b962a89c7600a5db1f268546882bfc4d31a975a899f5f42a09dd41a11e58636e6402f7c40f92df853aee417247bb11faee9a0 + languageName: node + linkType: hard + +"@sindresorhus/is@npm:^4.0.0": + version: 4.6.0 + resolution: "@sindresorhus/is@npm:4.6.0" + checksum: 10c0/33b6fb1d0834ec8dd7689ddc0e2781c2bfd8b9c4e4bacbcb14111e0ae00621f2c264b8a7d36541799d74888b5dccdf422a891a5cb5a709ace26325eedc81e22e + languageName: node + linkType: hard + +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 + languageName: node + linkType: hard + +"@sinonjs/commons@npm:^3.0.0": + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" + dependencies: + type-detect: "npm:4.0.8" + checksum: 10c0/1227a7b5bd6c6f9584274db996d7f8cee2c8c350534b9d0141fc662eaf1f292ea0ae3ed19e5e5271c8fd390d27e492ca2803acd31a1978be2cdc6be0da711403 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^10.0.2": + version: 10.3.0 + resolution: "@sinonjs/fake-timers@npm:10.3.0" + dependencies: + "@sinonjs/commons": "npm:^3.0.0" + checksum: 10c0/2e2fb6cc57f227912814085b7b01fede050cd4746ea8d49a1e44d5a0e56a804663b0340ae2f11af7559ea9bf4d087a11f2f646197a660ea3cb04e19efc04aa63 languageName: node linkType: hard -"@svgr/webpack@npm:^6.2.1": - version: 6.5.1 - resolution: "@svgr/webpack@npm:6.5.1" +"@swc/helpers@npm:^0.5.23": + version: 0.5.23 + resolution: "@swc/helpers@npm:0.5.23" dependencies: - "@babel/core": "npm:^7.19.6" - "@babel/plugin-transform-react-constant-elements": "npm:^7.18.12" - "@babel/preset-env": "npm:^7.19.4" - "@babel/preset-react": "npm:^7.18.6" - "@babel/preset-typescript": "npm:^7.18.6" - "@svgr/core": "npm:^6.5.1" - "@svgr/plugin-jsx": "npm:^6.5.1" - "@svgr/plugin-svgo": "npm:^6.5.1" - checksum: 10c0/3e9edfbc2ef3dc07b5f50c9c5ff5c951048511dff9dffb0407e6d15343849dfb36099fc7e1e3911429382cab81f7735a86ba1d6f77d21bb8f9ca33a5dec4824a + tslib: "npm:^2.8.0" + checksum: 10c0/02da7b4df465693933ecd4851cc193ec729c309939c8a84eccae5ec0010aafc3894e713b8ef8d13a6ba401759f0e900c88e2dcfef5872c27bb91e70f73275cce languageName: node linkType: hard @@ -6181,26 +5140,12 @@ __metadata: languageName: node linkType: hard -"@trysound/sax@npm:0.2.0": - version: 0.2.0 - resolution: "@trysound/sax@npm:0.2.0" - checksum: 10c0/44907308549ce775a41c38a815f747009ac45929a45d642b836aa6b0a536e4978d30b8d7d680bbd116e9dd73b7dbe2ef0d1369dcfc2d09e83ba381e485ecbe12 - languageName: node - linkType: hard - -"@tsconfig/docusaurus@npm:^1.0.6": - version: 1.0.7 - resolution: "@tsconfig/docusaurus@npm:1.0.7" - checksum: 10c0/49a9bb6f04a4eaee889a6950084dd14c6755fbd68951c7b9101f7031f7186ab7b46bb7e58e60e7a42d58db7ea20f981f43501ea653c1125f9863287c7108eb86 - languageName: node - linkType: hard - -"@tybys/wasm-util@npm:^0.10.0": - version: 0.10.1 - resolution: "@tybys/wasm-util@npm:0.10.1" +"@tybys/wasm-util@npm:^0.10.1": + version: 0.10.2 + resolution: "@tybys/wasm-util@npm:0.10.2" dependencies: tslib: "npm:^2.4.0" - checksum: 10c0/b255094f293794c6d2289300c5fbcafbb5532a3aed3a5ffd2f8dc1828e639b88d75f6a376dd8f94347a44813fd7a7149d8463477a9a49525c8b2dcaa38c2d1e8 + checksum: 10c0/26165bcd1fd7269f42d7fbe3de318f854a8968de8397e89fc9a423bb3e2da35a52150f382e6323b3367595beb16d9800a6f35971a5599daf76da1742ec3afc25 languageName: node linkType: hard @@ -6245,25 +5190,6 @@ __metadata: languageName: node linkType: hard -"@types/body-parser@npm:*": - version: 1.19.6 - resolution: "@types/body-parser@npm:1.19.6" - dependencies: - "@types/connect": "npm:*" - "@types/node": "npm:*" - checksum: 10c0/542da05c924dce58ee23f50a8b981fee36921850c82222e384931fda3e106f750f7880c47be665217d72dbe445129049db6eb1f44e7a06b09d62af8f3cca8ea7 - languageName: node - linkType: hard - -"@types/bonjour@npm:^3.5.9": - version: 3.5.13 - resolution: "@types/bonjour@npm:3.5.13" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/eebedbca185ac3c39dd5992ef18d9e2a9f99e7f3c2f52f5561f90e9ed482c5d224c7962db95362712f580ed5713264e777a98d8f0bd8747f4eadf62937baed16 - languageName: node - linkType: hard - "@types/cacheable-request@npm:^6.0.1": version: 6.0.3 resolution: "@types/cacheable-request@npm:6.0.3" @@ -6301,22 +5227,12 @@ __metadata: languageName: node linkType: hard -"@types/connect-history-api-fallback@npm:^1.3.5": - version: 1.5.4 - resolution: "@types/connect-history-api-fallback@npm:1.5.4" - dependencies: - "@types/express-serve-static-core": "npm:*" - "@types/node": "npm:*" - checksum: 10c0/1b4035b627dcd714b05a22557f942e24a57ca48e7377dde0d2f86313fe685bc0a6566512a73257a55b5665b96c3041fb29228ac93331d8133011716215de8244 - languageName: node - linkType: hard - -"@types/connect@npm:*": - version: 3.4.38 - resolution: "@types/connect@npm:3.4.38" +"@types/debug@npm:^4.0.0": + version: 4.1.13 + resolution: "@types/debug@npm:4.1.13" dependencies: - "@types/node": "npm:*" - checksum: 10c0/2e1cdba2c410f25649e77856505cd60223250fa12dff7a503e492208dbfdd25f62859918f28aba95315251fd1f5e1ffbfca1e25e73037189ab85dd3f8d0a148c + "@types/ms": "npm:*" + checksum: 10c0/e5e124021bbdb23a82727eee0a726ae0fc8a3ae1f57253cbcc47497f259afb357de7f6941375e773e1abbfa1604c1555b901a409d762ec2bb4c1612131d4afb7 languageName: node linkType: hard @@ -6340,6 +5256,15 @@ __metadata: languageName: node linkType: hard +"@types/estree-jsx@npm:^1.0.0": + version: 1.0.5 + resolution: "@types/estree-jsx@npm:1.0.5" + dependencies: + "@types/estree": "npm:*" + checksum: 10c0/07b354331516428b27a3ab99ee397547d47eb223c34053b48f84872fafb841770834b90cc1a0068398e7c7ccb15ec51ab00ec64b31dc5e3dbefd624638a35c6d + languageName: node + linkType: hard + "@types/estree@npm:*, @types/estree@npm:^1.0.8": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" @@ -6347,60 +5272,13 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:^1.0.6": +"@types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6": version: 1.0.9 resolution: "@types/estree@npm:1.0.9" checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 languageName: node linkType: hard -"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^5.0.0": - version: 5.1.0 - resolution: "@types/express-serve-static-core@npm:5.1.0" - dependencies: - "@types/node": "npm:*" - "@types/qs": "npm:*" - "@types/range-parser": "npm:*" - "@types/send": "npm:*" - checksum: 10c0/1918233c68a0c69695f78331af1aed5fb5190f91da6309318f700adeb78573be840b5d206cb8eda804b65a9989fdeccdaaf84c1e95adc3615052749224b64519 - languageName: node - linkType: hard - -"@types/express-serve-static-core@npm:^4.17.33": - version: 4.19.7 - resolution: "@types/express-serve-static-core@npm:4.19.7" - dependencies: - "@types/node": "npm:*" - "@types/qs": "npm:*" - "@types/range-parser": "npm:*" - "@types/send": "npm:*" - checksum: 10c0/c239df87863b8515e68dcb18203a9e2ba6108f86fdc385090284464a57a6dca6abb60a961cb6a73fea2110576f4f8acefa1cb06b60d14b6b0e5104478e7d57d1 - languageName: node - linkType: hard - -"@types/express@npm:*": - version: 5.0.6 - resolution: "@types/express@npm:5.0.6" - dependencies: - "@types/body-parser": "npm:*" - "@types/express-serve-static-core": "npm:^5.0.0" - "@types/serve-static": "npm:^2" - checksum: 10c0/f1071e3389a955d4f9a38aae38634121c7cd9b3171ba4201ec9b56bd534aba07866839d278adc0dda05b942b05a901a02fd174201c3b1f70ce22b10b6c68f24b - languageName: node - linkType: hard - -"@types/express@npm:^4.17.13": - version: 4.17.25 - resolution: "@types/express@npm:4.17.25" - dependencies: - "@types/body-parser": "npm:*" - "@types/express-serve-static-core": "npm:^4.17.33" - "@types/qs": "npm:*" - "@types/serve-static": "npm:^1" - checksum: 10c0/f42b616d2c9dbc50352c820db7de182f64ebbfa8dba6fb6c98e5f8f0e2ef3edde0131719d9dc6874803d25ad9ca2d53471d0fec2fbc60a6003a43d015bab72c4 - languageName: node - linkType: hard - "@types/graceful-fs@npm:^4.1.3": version: 4.1.9 resolution: "@types/graceful-fs@npm:4.1.9" @@ -6426,17 +5304,12 @@ __metadata: languageName: node linkType: hard -"@types/history@npm:^4.7.11": - version: 4.7.11 - resolution: "@types/history@npm:4.7.11" - checksum: 10c0/3facf37c2493d1f92b2e93a22cac7ea70b06351c2ab9aaceaa3c56aa6099fb63516f6c4ec1616deb5c56b4093c026a043ea2d3373e6c0644d55710364d02c934 - languageName: node - linkType: hard - -"@types/html-minifier-terser@npm:^6.0.0": - version: 6.1.0 - resolution: "@types/html-minifier-terser@npm:6.1.0" - checksum: 10c0/a62fb8588e2f3818d82a2d7b953ad60a4a52fd767ae04671de1c16f5788bd72f1ed3a6109ed63fd190c06a37d919e3c39d8adbc1793a005def76c15a3f5f5dab +"@types/hast@npm:^3.0.0, @types/hast@npm:^3.0.4": + version: 3.0.4 + resolution: "@types/hast@npm:3.0.4" + dependencies: + "@types/unist": "npm:*" + checksum: 10c0/3249781a511b38f1d330fd1e3344eed3c4e7ea8eff82e835d35da78e637480d36fad37a78be5a7aed8465d237ad0446abc1150859d0fde395354ea634decf9f7 languageName: node linkType: hard @@ -6447,22 +5320,6 @@ __metadata: languageName: node linkType: hard -"@types/http-errors@npm:*": - version: 2.0.5 - resolution: "@types/http-errors@npm:2.0.5" - checksum: 10c0/00f8140fbc504f47356512bd88e1910c2f07e04233d99c88c854b3600ce0523c8cd0ba7d1897667243282eb44c59abb9245959e2428b9de004f93937f52f7c15 - languageName: node - linkType: hard - -"@types/http-proxy@npm:^1.17.8": - version: 1.17.17 - resolution: "@types/http-proxy@npm:1.17.17" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/547e322a5eecf0b50d08f6a46bd89c8c8663d67dbdcd472da5daf968b03e63a82f6b3650443378abe6c10a46475dac52015f30e8c74ba2ea5820dd4e9cdef2d4 - languageName: node - linkType: hard - "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.6 resolution: "@types/istanbul-lib-coverage@npm:2.0.6" @@ -6498,7 +5355,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db @@ -6528,19 +5385,19 @@ __metadata: languageName: node linkType: hard -"@types/mdast@npm:^3.0.0": - version: 3.0.15 - resolution: "@types/mdast@npm:3.0.15" +"@types/mdast@npm:^4.0.0": + version: 4.0.4 + resolution: "@types/mdast@npm:4.0.4" dependencies: - "@types/unist": "npm:^2" - checksum: 10c0/fcbf716c03d1ed5465deca60862e9691414f9c43597c288c7d2aefbe274552e1bbd7aeee91b88a02597e88a28c139c57863d0126fcf8416a95fdc681d054ee3d + "@types/unist": "npm:*" + checksum: 10c0/84f403dbe582ee508fd9c7643ac781ad8597fcbfc9ccb8d4715a2c92e4545e5772cbd0dbdf18eda65789386d81b009967fdef01b24faf6640f817287f54d9c82 languageName: node linkType: hard -"@types/mime@npm:^1": - version: 1.3.5 - resolution: "@types/mime@npm:1.3.5" - checksum: 10c0/c2ee31cd9b993804df33a694d5aa3fa536511a49f2e06eeab0b484fef59b4483777dbb9e42a4198a0809ffbf698081fdbca1e5c2218b82b91603dfab10a10fbc +"@types/mdx@npm:^2.0.0": + version: 2.0.14 + resolution: "@types/mdx@npm:2.0.14" + checksum: 10c0/f1e3873c1e36e2685fb99222bf81a23c55a8e2edf49d0c45cabf2c875b0ae814cd6050cce5770ff1c6881ac747d2ad33731e1de32139b04cf181d8d7e1943ff4 languageName: node linkType: hard @@ -6551,12 +5408,10 @@ __metadata: languageName: node linkType: hard -"@types/node-forge@npm:^1.3.0": - version: 1.3.14 - resolution: "@types/node-forge@npm:1.3.14" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/da6158fd34fa7652aa7f8164508f97a76b558724ab292f13c257e39d54d95d4d77604e8fb14dc454a867f1aeec7af70118294889195ec4400cecbb8a5c77a212 +"@types/ms@npm:*": + version: 2.1.0 + resolution: "@types/ms@npm:2.1.0" + checksum: 10c0/5ce692ffe1549e1b827d99ef8ff71187457e0eb44adbae38fdf7b9a74bae8d20642ee963c14516db1d35fa2652e65f47680fdf679dcbde52bbfadd021f497225 languageName: node linkType: hard @@ -6569,13 +5424,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^17.0.5": - version: 17.0.45 - resolution: "@types/node@npm:17.0.45" - checksum: 10c0/0db377133d709b33a47892581a21a41cd7958f22723a3cc6c71d55ac018121382de42fbfc7970d5ae3e7819dbe5f40e1c6a5174aedf7e7964e9cb8fa72b580b0 - languageName: node - linkType: hard - "@types/node@npm:^24.0.0": version: 24.13.2 resolution: "@types/node@npm:24.13.2" @@ -6606,20 +5454,6 @@ __metadata: languageName: node linkType: hard -"@types/qs@npm:*": - version: 6.14.0 - resolution: "@types/qs@npm:6.14.0" - checksum: 10c0/5b3036df6e507483869cdb3858201b2e0b64b4793dc4974f188caa5b5732f2333ab9db45c08157975054d3b070788b35088b4bc60257ae263885016ee2131310 - languageName: node - linkType: hard - -"@types/range-parser@npm:*": - version: 1.2.7 - resolution: "@types/range-parser@npm:1.2.7" - checksum: 10c0/361bb3e964ec5133fa40644a0b942279ed5df1949f21321d77de79f48b728d39253e5ce0408c9c17e4e0fd95ca7899da36841686393b9f7a1e209916e9381a3c - languageName: node - linkType: hard - "@types/react-color@npm:^3.0.6": version: 3.0.13 resolution: "@types/react-color@npm:3.0.13" @@ -6631,6 +5465,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:^19.2.3": + version: 19.2.3 + resolution: "@types/react-dom@npm:19.2.3" + peerDependencies: + "@types/react": ^19.2.0 + checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 + languageName: node + linkType: hard + "@types/react-reconciler@npm:~0.33.0": version: 0.33.0 resolution: "@types/react-reconciler@npm:0.33.0" @@ -6640,38 +5483,6 @@ __metadata: languageName: node linkType: hard -"@types/react-router-config@npm:*, @types/react-router-config@npm:^5.0.6": - version: 5.0.11 - resolution: "@types/react-router-config@npm:5.0.11" - dependencies: - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - "@types/react-router": "npm:^5.1.0" - checksum: 10c0/3fa4daf8c14689a05f34e289fc53c4a892e97f35715455c507a8048d9875b19cd3d3142934ca973effed6a6c38f33539b6e173cd254f67e2021ecd5458d551c8 - languageName: node - linkType: hard - -"@types/react-router-dom@npm:*": - version: 5.3.3 - resolution: "@types/react-router-dom@npm:5.3.3" - dependencies: - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - "@types/react-router": "npm:*" - checksum: 10c0/a9231a16afb9ed5142678147eafec9d48582809295754fb60946e29fcd3757a4c7a3180fa94b45763e4c7f6e3f02379e2fcb8dd986db479dcab40eff5fc62a91 - languageName: node - linkType: hard - -"@types/react-router@npm:*, @types/react-router@npm:^5.1.0": - version: 5.1.20 - resolution: "@types/react-router@npm:5.1.20" - dependencies: - "@types/history": "npm:^4.7.11" - "@types/react": "npm:*" - checksum: 10c0/1f7eee61981d2f807fa01a34a0ef98ebc0774023832b6611a69c7f28fdff01de5a38cabf399f32e376bf8099dcb7afaf724775bea9d38870224492bea4cb5737 - languageName: node - linkType: hard - "@types/react-test-renderer@npm:^19.1.0": version: 19.1.0 resolution: "@types/react-test-renderer@npm:19.1.0" @@ -6717,80 +5528,6 @@ __metadata: languageName: node linkType: hard -"@types/retry@npm:0.12.0": - version: 0.12.0 - resolution: "@types/retry@npm:0.12.0" - checksum: 10c0/7c5c9086369826f569b83a4683661557cab1361bac0897a1cefa1a915ff739acd10ca0d62b01071046fe3f5a3f7f2aec80785fe283b75602dc6726781ea3e328 - languageName: node - linkType: hard - -"@types/sax@npm:^1.2.1": - version: 1.2.7 - resolution: "@types/sax@npm:1.2.7" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/d077a761a0753b079bf8279b3993948030ca86ed9125437b9b29c1de40db9b2deb7fddc369f014b58861d450e8b8cc75f163aa29dc8cea81952efbfd859168cf - languageName: node - linkType: hard - -"@types/send@npm:*": - version: 1.2.1 - resolution: "@types/send@npm:1.2.1" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/7673747f8c2d8e67f3b1b3b57e9d4d681801a4f7b526ecf09987bb9a84a61cf94aa411c736183884dc762c1c402a61681eb1ef200d8d45d7e5ec0ab67ea5f6c1 - languageName: node - linkType: hard - -"@types/send@npm:<1": - version: 0.17.6 - resolution: "@types/send@npm:0.17.6" - dependencies: - "@types/mime": "npm:^1" - "@types/node": "npm:*" - checksum: 10c0/a9d76797f0637738062f1b974e0fcf3d396a28c5dc18c3f95ecec5dabda82e223afbc2d56a0bca46b6326fd7bb229979916cea40de2270a98128fd94441b87c2 - languageName: node - linkType: hard - -"@types/serve-index@npm:^1.9.1": - version: 1.9.4 - resolution: "@types/serve-index@npm:1.9.4" - dependencies: - "@types/express": "npm:*" - checksum: 10c0/94c1b9e8f1ea36a229e098e1643d5665d9371f8c2658521718e259130a237c447059b903bac0dcc96ee2c15fd63f49aa647099b7d0d437a67a6946527a837438 - languageName: node - linkType: hard - -"@types/serve-static@npm:^1, @types/serve-static@npm:^1.13.10": - version: 1.15.10 - resolution: "@types/serve-static@npm:1.15.10" - dependencies: - "@types/http-errors": "npm:*" - "@types/node": "npm:*" - "@types/send": "npm:<1" - checksum: 10c0/842fca14c9e80468f89b6cea361773f2dcd685d4616a9f59013b55e1e83f536e4c93d6d8e3ba5072d40c4e7e64085210edd6646b15d538ded94512940a23021f - languageName: node - linkType: hard - -"@types/serve-static@npm:^2": - version: 2.2.0 - resolution: "@types/serve-static@npm:2.2.0" - dependencies: - "@types/http-errors": "npm:*" - "@types/node": "npm:*" - checksum: 10c0/a3c6126bdbf9685e6c7dc03ad34639666eff32754e912adeed9643bf3dd3aa0ff043002a7f69039306e310d233eb8e160c59308f95b0a619f32366bbc48ee094 - languageName: node - linkType: hard - -"@types/sockjs@npm:^0.3.33": - version: 0.3.36 - resolution: "@types/sockjs@npm:0.3.36" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/b20b7820ee813f22de4f2ce98bdd12c68c930e016a8912b1ed967595ac0d8a4cbbff44f4d486dd97f77f5927e7b5725bdac7472c9ec5b27f53a5a13179f0612f - languageName: node - linkType: hard - "@types/stack-utils@npm:^2.0.0": version: 2.0.3 resolution: "@types/stack-utils@npm:2.0.3" @@ -6798,14 +5535,14 @@ __metadata: languageName: node linkType: hard -"@types/unist@npm:*": +"@types/unist@npm:*, @types/unist@npm:^3.0.0, @types/unist@npm:^3.0.3": version: 3.0.3 resolution: "@types/unist@npm:3.0.3" checksum: 10c0/2b1e4adcab78388e088fcc3c0ae8700f76619dbcb4741d7d201f87e2cb346bfc29a89003cfea2d76c996e1061452e14fcd737e8b25aacf949c1f2d6b2bc3dd60 languageName: node linkType: hard -"@types/unist@npm:^2, @types/unist@npm:^2.0.0, @types/unist@npm:^2.0.2, @types/unist@npm:^2.0.3": +"@types/unist@npm:^2, @types/unist@npm:^2.0.0, @types/unist@npm:^2.0.2": version: 2.0.11 resolution: "@types/unist@npm:2.0.11" checksum: 10c0/24dcdf25a168f453bb70298145eb043cfdbb82472db0bc0b56d6d51cd2e484b9ed8271d4ac93000a80da568f2402e9339723db262d0869e2bf13bc58e081768d @@ -6833,15 +5570,6 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.5": - version: 8.18.1 - resolution: "@types/ws@npm:8.18.1" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/61aff1129143fcc4312f083bc9e9e168aa3026b7dd6e70796276dcfb2c8211c4292603f9c4864fae702f2ed86e4abd4d38aa421831c2fd7f856c931a481afbab - languageName: node - linkType: hard - "@types/yargs-parser@npm:*": version: 21.0.3 resolution: "@types/yargs-parser@npm:21.0.3" @@ -7002,6 +5730,13 @@ __metadata: languageName: node linkType: hard +"@ungap/structured-clone@npm:^1.0.0": + version: 1.3.1 + resolution: "@ungap/structured-clone@npm:1.3.1" + checksum: 10c0/7e75faf93cf12ff07c3d15a9e4d326b68f57d13f7246d9f4df2c1ed1a5cde581f899d397816ba5d5d703a0d7f6219e4408f385160156cf20b4e082721817cc37 + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.3.0": version: 1.3.0 resolution: "@ungap/structured-clone@npm:1.3.0" @@ -7009,6 +5744,50 @@ __metadata: languageName: node linkType: hard +"@unhead/react@npm:^2.1.15": + version: 2.1.15 + resolution: "@unhead/react@npm:2.1.15" + dependencies: + unhead: "npm:2.1.15" + peerDependencies: + react: ">=18.3.1" + checksum: 10c0/0a341187a86b9ac38065b521cad124bc0f8092052507551ffbc2d9538598c0dfcd0be7c473a9d117c20949aae0a02fced271a9b81d3826a6d62c0c69e1b4482b + languageName: node + linkType: hard + +"@vercel/analytics@npm:^2.0.1": + version: 2.0.1 + resolution: "@vercel/analytics@npm:2.0.1" + peerDependencies: + "@remix-run/react": ^2 + "@sveltejs/kit": ^1 || ^2 + next: ">= 13" + nuxt: ">= 3" + react: ^18 || ^19 || ^19.0.0-rc + svelte: ">= 4" + vue: ^3 + vue-router: ^4 + peerDependenciesMeta: + "@remix-run/react": + optional: true + "@sveltejs/kit": + optional: true + next: + optional: true + nuxt: + optional: true + react: + optional: true + svelte: + optional: true + vue: + optional: true + vue-router: + optional: true + checksum: 10c0/c788c03d0f072f6f2be52d3f6f4f0680d2343f0d8765a759a044204223b14a6e14b285d0c5d5d05c0b66d4419746c5b245656ebdc48398328d85d96f1ede982b + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.14.1, @webassemblyjs/ast@npm:^1.14.1": version: 1.14.1 resolution: "@webassemblyjs/ast@npm:1.14.1" @@ -7181,13 +5960,6 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/lockfile@npm:^1.1.0": - version: 1.1.0 - resolution: "@yarnpkg/lockfile@npm:1.1.0" - checksum: 10c0/0bfa50a3d756623d1f3409bc23f225a1d069424dbc77c6fd2f14fb377390cd57ec703dc70286e081c564be9051ead9ba85d81d66a3e68eeb6eb506d4e0c0fbda - languageName: node - linkType: hard - "JSONStream@npm:^1.0.4": version: 1.3.5 resolution: "JSONStream@npm:1.3.5" @@ -7216,7 +5988,7 @@ __metadata: languageName: node linkType: hard -"accepts@npm:^1.3.8, accepts@npm:~1.3.4, accepts@npm:~1.3.8": +"accepts@npm:^1.3.8, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" dependencies: @@ -7245,7 +6017,7 @@ __metadata: languageName: node linkType: hard -"acorn-jsx@npm:^5.3.2": +"acorn-jsx@npm:^5.0.0, acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" peerDependencies: @@ -7254,16 +6026,16 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.0.0": - version: 8.3.4 - resolution: "acorn-walk@npm:8.3.4" - dependencies: - acorn: "npm:^8.11.0" - checksum: 10c0/76537ac5fb2c37a64560feaf3342023dadc086c46da57da363e64c6148dc21b57d49ace26f949e225063acb6fb441eabffd89f7a3066de5ad37ab3e328927c62 +"acorn@npm:^8.0.0": + version: 8.17.0 + resolution: "acorn@npm:8.17.0" + bin: + acorn: bin/acorn + checksum: 10c0/5dcefea5f8f023b6cc24cbe71fb5a8112b601d36c4fa07d14e4e6ffc2ee47383332c46b36c766d9437725aa6660156eae50efa0c838719823b50d7c327c4ed42 languageName: node linkType: hard -"acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.15.0": +"acorn@npm:^8.15.0": version: 8.15.0 resolution: "acorn@npm:8.15.0" bin: @@ -7279,13 +6051,6 @@ __metadata: languageName: node linkType: hard -"address@npm:^1.0.1, address@npm:^1.1.2": - version: 1.2.2 - resolution: "address@npm:1.2.2" - checksum: 10c0/1c8056b77fb124456997b78ed682ecc19d2fd7ea8bd5850a2aa8c3e3134c913847c57bcae418622efd32ba858fa1e242a40a251ac31da0515664fc0ac03a047d - languageName: node - linkType: hard - "agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.4 resolution: "agent-base@npm:7.1.4" @@ -7293,16 +6058,6 @@ __metadata: languageName: node linkType: hard -"aggregate-error@npm:^3.0.0": - version: 3.1.0 - resolution: "aggregate-error@npm:3.1.0" - dependencies: - clean-stack: "npm:^2.0.0" - indent-string: "npm:^4.0.0" - checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039 - languageName: node - linkType: hard - "ajv-formats@npm:^2.1.1": version: 2.1.1 resolution: "ajv-formats@npm:2.1.1" @@ -7317,7 +6072,7 @@ __metadata: languageName: node linkType: hard -"ajv-keywords@npm:^3.4.1, ajv-keywords@npm:^3.5.2": +"ajv-keywords@npm:^3.5.2": version: 3.5.2 resolution: "ajv-keywords@npm:3.5.2" peerDependencies: @@ -7337,7 +6092,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5": +"ajv@npm:^6.12.4, ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -7373,62 +6128,6 @@ __metadata: languageName: node linkType: hard -"algoliasearch-helper@npm:^3.10.0": - version: 3.27.0 - resolution: "algoliasearch-helper@npm:3.27.0" - dependencies: - "@algolia/events": "npm:^4.0.1" - peerDependencies: - algoliasearch: ">= 3.1 < 6" - checksum: 10c0/09cdb9c5faf14127030e3dfb4bf6715bb36a9896edf3b43e128c8b2137a1f7d448817995840c0c1ca53dfd94891a2c3554774e885e826f07a0325f9dede3c527 - languageName: node - linkType: hard - -"algoliasearch@npm:^4.13.1": - version: 4.25.3 - resolution: "algoliasearch@npm:4.25.3" - dependencies: - "@algolia/cache-browser-local-storage": "npm:4.25.3" - "@algolia/cache-common": "npm:4.25.3" - "@algolia/cache-in-memory": "npm:4.25.3" - "@algolia/client-account": "npm:4.25.3" - "@algolia/client-analytics": "npm:4.25.3" - "@algolia/client-common": "npm:4.25.3" - "@algolia/client-personalization": "npm:4.25.3" - "@algolia/client-search": "npm:4.25.3" - "@algolia/logger-common": "npm:4.25.3" - "@algolia/logger-console": "npm:4.25.3" - "@algolia/recommend": "npm:4.25.3" - "@algolia/requester-browser-xhr": "npm:4.25.3" - "@algolia/requester-common": "npm:4.25.3" - "@algolia/requester-node-http": "npm:4.25.3" - "@algolia/transporter": "npm:4.25.3" - checksum: 10c0/e293b79ad5c49991b1882fd811ef69ab81af1e524632cfbaa8afcc96315a7f1a777d7cc5cef075cd7460a3d54d49e03f8c0bb6338bf9f31890e5b05fa20ec844 - languageName: node - linkType: hard - -"algoliasearch@npm:^5.14.2": - version: 5.46.2 - resolution: "algoliasearch@npm:5.46.2" - dependencies: - "@algolia/abtesting": "npm:1.12.2" - "@algolia/client-abtesting": "npm:5.46.2" - "@algolia/client-analytics": "npm:5.46.2" - "@algolia/client-common": "npm:5.46.2" - "@algolia/client-insights": "npm:5.46.2" - "@algolia/client-personalization": "npm:5.46.2" - "@algolia/client-query-suggestions": "npm:5.46.2" - "@algolia/client-search": "npm:5.46.2" - "@algolia/ingestion": "npm:1.46.2" - "@algolia/monitoring": "npm:1.46.2" - "@algolia/recommend": "npm:5.46.2" - "@algolia/requester-browser-xhr": "npm:5.46.2" - "@algolia/requester-fetch": "npm:5.46.2" - "@algolia/requester-node-http": "npm:5.46.2" - checksum: 10c0/37f30159a89f0b4a67d8198a4fe32d32b19c6961bc1d3dc99ea8b8f284dfa0e095b5e24580c15f3b6213a1b3983ef1aae529c41afb3f056b32c1560e329b3a1f - languageName: node - linkType: hard - "all-contributors-cli@npm:^6.24.0": version: 6.26.1 resolution: "all-contributors-cli@npm:6.26.1" @@ -7460,7 +6159,7 @@ __metadata: languageName: node linkType: hard -"ansi-align@npm:^3.0.0, ansi-align@npm:^3.0.1": +"ansi-align@npm:^3.0.0": version: 3.0.1 resolution: "ansi-align@npm:3.0.1" dependencies: @@ -7478,7 +6177,7 @@ __metadata: languageName: node linkType: hard -"ansi-html-community@npm:0.0.8, ansi-html-community@npm:^0.0.8": +"ansi-html-community@npm:0.0.8": version: 0.0.8 resolution: "ansi-html-community@npm:0.0.8" bin: @@ -7501,7 +6200,7 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^6.0.1, ansi-regex@npm:^6.2.2": +"ansi-regex@npm:^6.2.2": version: 6.2.2 resolution: "ansi-regex@npm:6.2.2" checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f @@ -7533,7 +6232,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": +"ansi-styles@npm:^6.2.1": version: 6.2.3 resolution: "ansi-styles@npm:6.2.3" checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 @@ -7550,7 +6249,7 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:^3.0.3, anymatch@npm:~3.1.2": +"anymatch@npm:^3.0.3": version: 3.1.3 resolution: "anymatch@npm:3.1.3" dependencies: @@ -7567,7 +6266,7 @@ __metadata: languageName: node linkType: hard -"arg@npm:^5.0.0, arg@npm:^5.0.2": +"arg@npm:^5.0.2": version: 5.0.2 resolution: "arg@npm:5.0.2" checksum: 10c0/ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e @@ -7810,6 +6509,15 @@ __metadata: languageName: node linkType: hard +"astring@npm:^1.8.0": + version: 1.9.0 + resolution: "astring@npm:1.9.0" + bin: + astring: bin/astring + checksum: 10c0/e7519544d9824494e80ef0e722bb3a0c543a31440d59691c13aeaceb75b14502af536b23f08db50aa6c632dafaade54caa25f0788aa7550b6b2d6e2df89e0830 + languageName: node + linkType: hard + "async-function@npm:^1.0.0": version: 1.0.0 resolution: "async-function@npm:1.0.0" @@ -7863,23 +6571,6 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.12, autoprefixer@npm:^10.4.7": - version: 10.4.23 - resolution: "autoprefixer@npm:10.4.23" - dependencies: - browserslist: "npm:^4.28.1" - caniuse-lite: "npm:^1.0.30001760" - fraction.js: "npm:^5.3.4" - picocolors: "npm:^1.1.1" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.1.0 - bin: - autoprefixer: bin/autoprefixer - checksum: 10c0/3765c5d0fa3e95fb2ebe9d5a6d4da0156f5d346c7ec9ac0fbf5c97c8139d0ca1e8743bf5dc1b4aa954467be6929fddf8498a3b6202d468d70b5f359f3b6af90f - languageName: node - linkType: hard - "available-typed-arrays@npm:^1.0.7": version: 1.0.7 resolution: "available-typed-arrays@npm:1.0.7" @@ -7889,15 +6580,6 @@ __metadata: languageName: node linkType: hard -"axios@npm:^0.25.0": - version: 0.25.0 - resolution: "axios@npm:0.25.0" - dependencies: - follow-redirects: "npm:^1.14.7" - checksum: 10c0/33475dc847e8494341db5ff5d3ce3a830e511d9698eda1e2507618a0a875ca774e7c59b9194c70664e886699b5917416175da2ea5ccb67a2945f7cdc939d7c56 - languageName: node - linkType: hard - "babel-jest@npm:^29.6.3, babel-jest@npm:^29.7.0": version: 29.7.0 resolution: "babel-jest@npm:29.7.0" @@ -7915,7 +6597,7 @@ __metadata: languageName: node linkType: hard -"babel-loader@npm:^8.1.0, babel-loader@npm:^8.2.5": +"babel-loader@npm:^8.1.0": version: 8.4.1 resolution: "babel-loader@npm:8.4.1" dependencies: @@ -7930,36 +6612,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-apply-mdx-type-prop@npm:1.6.22": - version: 1.6.22 - resolution: "babel-plugin-apply-mdx-type-prop@npm:1.6.22" - dependencies: - "@babel/helper-plugin-utils": "npm:7.10.4" - "@mdx-js/util": "npm:1.6.22" - peerDependencies: - "@babel/core": ^7.11.6 - checksum: 10c0/d1fd88f2eee87f3d709373cfac5165f8407793b123e1c7061308311f7e6b0778e093a4a93e7130b47c5a742f2515d0c1d4f3da5097ff195ef91011688ec17ddc - languageName: node - linkType: hard - -"babel-plugin-dynamic-import-node@npm:^2.3.3": - version: 2.3.3 - resolution: "babel-plugin-dynamic-import-node@npm:2.3.3" - dependencies: - object.assign: "npm:^4.1.0" - checksum: 10c0/1bd80df981e1fc1aff0cd4e390cf27aaa34f95f7620cd14dff07ba3bad56d168c098233a7d2deb2c9b1dc13643e596a6b94fc608a3412ee3c56e74a25cd2167e - languageName: node - linkType: hard - -"babel-plugin-extract-import-names@npm:1.6.22": - version: 1.6.22 - resolution: "babel-plugin-extract-import-names@npm:1.6.22" - dependencies: - "@babel/helper-plugin-utils": "npm:7.10.4" - checksum: 10c0/c7b7206222f7b70f2c9852caa621cc3742b5d9f7dd4229a6e3c560d7683b82f835a8ea46db632df5dab5ad91b1439ead3771a8576a7a14e418248c16fd1f0cc4 - languageName: node - linkType: hard - "babel-plugin-istanbul@npm:^6.1.1": version: 6.1.1 resolution: "babel-plugin-istanbul@npm:6.1.1" @@ -8308,6 +6960,13 @@ __metadata: languageName: node linkType: hard +"bail@npm:^2.0.0": + version: 2.0.2 + resolution: "bail@npm:2.0.2" + checksum: 10c0/25cbea309ef6a1f56214187004e8f34014eb015713ea01fa5b9b7e9e776ca88d0fdffd64143ac42dc91966c915a4b7b683411b56e14929fad16153fc026ffb8b + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -8322,13 +6981,6 @@ __metadata: languageName: node linkType: hard -"base16@npm:^1.0.0": - version: 1.0.0 - resolution: "base16@npm:1.0.0" - checksum: 10c0/af1aee7b297d968528ef47c8de2c5274029743e8a4a5f61ec823e36b673781691d124168cb22936c7997f53d89b344c58bf7ecf93eeb148cffa7e3fb4e4b8b18 - languageName: node - linkType: hard - "base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -8369,13 +7021,6 @@ __metadata: languageName: node linkType: hard -"batch@npm:0.6.1": - version: 0.6.1 - resolution: "batch@npm:0.6.1" - checksum: 10c0/925a13897b4db80d4211082fe287bcf96d297af38e26448c857cee3e095c9792e3b8f26b37d268812e7f38a589f694609de8534a018b1937d7dc9f84e6b387c5 - languageName: node - linkType: hard - "before-after-hook@npm:^2.2.0": version: 2.2.3 resolution: "before-after-hook@npm:2.2.3" @@ -8397,13 +7042,6 @@ __metadata: languageName: node linkType: hard -"binary-extensions@npm:^2.0.0": - version: 2.3.0 - resolution: "binary-extensions@npm:2.3.0" - checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 - languageName: node - linkType: hard - "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -8435,20 +7073,10 @@ __metadata: languageName: node linkType: hard -"bonjour-service@npm:^1.0.11": - version: 1.3.0 - resolution: "bonjour-service@npm:1.3.0" - dependencies: - fast-deep-equal: "npm:^3.1.3" - multicast-dns: "npm:^7.2.5" - checksum: 10c0/5721fd9f9bb968e9cc16c1e8116d770863dd2329cb1f753231de1515870648c225142b7eefa71f14a5c22bc7b37ddd7fdeb018700f28a8c936d50d4162d433c7 - languageName: node - linkType: hard - -"boolbase@npm:^1.0.0": - version: 1.0.0 - resolution: "boolbase@npm:1.0.0" - checksum: 10c0/e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf +"body-scroll-lock@npm:4.0.0-beta.0": + version: 4.0.0-beta.0 + resolution: "body-scroll-lock@npm:4.0.0-beta.0" + checksum: 10c0/32a9553b83424e69f3784d7bbcef2949c43159ea13b5084c96dc08b1e2585e4eeb1e915f14e37a4ac44110339dfa6fa2ddde4d3d812be88ecbea060aa724a791 languageName: node linkType: hard @@ -8468,38 +7096,6 @@ __metadata: languageName: node linkType: hard -"boxen@npm:^5.0.0": - version: 5.1.2 - resolution: "boxen@npm:5.1.2" - dependencies: - ansi-align: "npm:^3.0.0" - camelcase: "npm:^6.2.0" - chalk: "npm:^4.1.0" - cli-boxes: "npm:^2.2.1" - string-width: "npm:^4.2.2" - type-fest: "npm:^0.20.2" - widest-line: "npm:^3.1.0" - wrap-ansi: "npm:^7.0.0" - checksum: 10c0/71f31c2eb3dcacd5fce524ae509e0cc90421752e0bfbd0281fd3352871d106c462a0f810c85f2fdb02f3a9fab2d7a84e9718b4999384d651b76104ebe5d2c024 - languageName: node - linkType: hard - -"boxen@npm:^6.2.1": - version: 6.2.1 - resolution: "boxen@npm:6.2.1" - dependencies: - ansi-align: "npm:^3.0.1" - camelcase: "npm:^6.2.0" - chalk: "npm:^4.1.2" - cli-boxes: "npm:^3.0.0" - string-width: "npm:^5.0.1" - type-fest: "npm:^2.5.0" - widest-line: "npm:^4.0.1" - wrap-ansi: "npm:^8.0.1" - checksum: 10c0/2a50d059c950a50d9f3c873093702747740814ce8819225c4f8cbe92024c9f5a9219d2b7128f5cfa17c022644d929bbbc88b9591de67249c6ebe07f7486bdcfd - languageName: node - linkType: hard - "bplist-creator@npm:0.1.0": version: 0.1.0 resolution: "bplist-creator@npm:0.1.0" @@ -8564,7 +7160,7 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.3, braces@npm:~3.0.2": +"braces@npm:^3.0.3": version: 3.0.3 resolution: "braces@npm:3.0.3" dependencies: @@ -8573,7 +7169,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.18.1, browserslist@npm:^4.21.4, browserslist@npm:^4.24.0, browserslist@npm:^4.25.0, browserslist@npm:^4.28.0, browserslist@npm:^4.28.1": +"browserslist@npm:^4.24.0, browserslist@npm:^4.25.0, browserslist@npm:^4.28.0, browserslist@npm:^4.28.1": version: 4.28.1 resolution: "browserslist@npm:4.28.1" dependencies: @@ -8629,13 +7225,6 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.0.0": - version: 3.0.0 - resolution: "bytes@npm:3.0.0" - checksum: 10c0/91d42c38601c76460519ffef88371caacaea483a354c8e4b8808e7b027574436a5713337c003ea3de63ee4991c2a9a637884fdfe7f761760d746929d9e8fec60 - languageName: node - linkType: hard - "bytes@npm:3.1.2, bytes@npm:~3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -8812,23 +7401,6 @@ __metadata: languageName: node linkType: hard -"camel-case@npm:^4.1.2": - version: 4.1.2 - resolution: "camel-case@npm:4.1.2" - dependencies: - pascal-case: "npm:^3.1.2" - tslib: "npm:^2.0.3" - checksum: 10c0/bf9eefaee1f20edbed2e9a442a226793bc72336e2b99e5e48c6b7252b6f70b080fc46d8246ab91939e2af91c36cdd422e0af35161e58dd089590f302f8f64c8a - languageName: node - linkType: hard - -"camelcase-css@npm:2.0.1": - version: 2.0.1 - resolution: "camelcase-css@npm:2.0.1" - checksum: 10c0/1a1a3137e8a781e6cbeaeab75634c60ffd8e27850de410c162cce222ea331cd1ba5364e8fb21c95e5ca76f52ac34b81a090925ca00a87221355746d049c6e273 - languageName: node - linkType: hard - "camelcase-keys@npm:^4.0.0": version: 4.2.0 resolution: "camelcase-keys@npm:4.2.0" @@ -8879,19 +7451,7 @@ __metadata: languageName: node linkType: hard -"caniuse-api@npm:^3.0.0": - version: 3.0.0 - resolution: "caniuse-api@npm:3.0.0" - dependencies: - browserslist: "npm:^4.0.0" - caniuse-lite: "npm:^1.0.0" - lodash.memoize: "npm:^4.1.2" - lodash.uniq: "npm:^4.5.0" - checksum: 10c0/60f9e85a3331e6d761b1b03eec71ca38ef7d74146bece34694853033292156b815696573ed734b65583acf493e88163618eda915c6c826d46a024c71a9572b4c - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001759, caniuse-lite@npm:^1.0.30001760": +"caniuse-lite@npm:^1.0.30001759": version: 1.0.30001763 resolution: "caniuse-lite@npm:1.0.30001763" checksum: 10c0/4524f6ae15a18c8510849e43baed01441940c50cab29687cba62dde6354a49549d5bcb7f79b864dadf3f0c8c342038c63131cdb64af382a501593106b1d0028b @@ -8921,6 +7481,13 @@ __metadata: languageName: node linkType: hard +"ccount@npm:^2.0.0": + version: 2.0.1 + resolution: "ccount@npm:2.0.1" + checksum: 10c0/3939b1664390174484322bc3f45b798462e6c07ee6384cb3d645e0aa2f318502d174845198c1561930e1d431087f74cf1fe291ae9a4722821a9f4ba67e574350 + languageName: node + linkType: hard + "chalk@npm:2.4.2, chalk@npm:^2.0.1, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -9009,6 +7576,13 @@ __metadata: languageName: node linkType: hard +"character-entities-html4@npm:^2.0.0": + version: 2.1.0 + resolution: "character-entities-html4@npm:2.1.0" + checksum: 10c0/fe61b553f083400c20c0b0fd65095df30a0b445d960f3bbf271536ae6c3ba676f39cb7af0b4bf2755812f08ab9b88f2feed68f9aebb73bb153f7a115fe5c6e40 + languageName: node + linkType: hard + "character-entities-legacy@npm:^1.0.0": version: 1.1.4 resolution: "character-entities-legacy@npm:1.1.4" @@ -9016,6 +7590,13 @@ __metadata: languageName: node linkType: hard +"character-entities-legacy@npm:^3.0.0": + version: 3.0.0 + resolution: "character-entities-legacy@npm:3.0.0" + checksum: 10c0/ec4b430af873661aa754a896a2b55af089b4e938d3d010fad5219299a6b6d32ab175142699ee250640678cd64bdecd6db3c9af0b8759ab7b155d970d84c4c7d1 + languageName: node + linkType: hard + "character-entities@npm:^1.0.0": version: 1.2.4 resolution: "character-entities@npm:1.2.4" @@ -9023,6 +7604,13 @@ __metadata: languageName: node linkType: hard +"character-entities@npm:^2.0.0": + version: 2.0.2 + resolution: "character-entities@npm:2.0.2" + checksum: 10c0/b0c645a45bcc90ff24f0e0140f4875a8436b8ef13b6bcd31ec02cfb2ca502b680362aa95386f7815bdc04b6464d48cf191210b3840d7c04241a149ede591a308 + languageName: node + linkType: hard + "character-reference-invalid@npm:^1.0.0": version: 1.1.4 resolution: "character-reference-invalid@npm:1.1.4" @@ -9030,6 +7618,13 @@ __metadata: languageName: node linkType: hard +"character-reference-invalid@npm:^2.0.0": + version: 2.0.1 + resolution: "character-reference-invalid@npm:2.0.1" + checksum: 10c0/2ae0dec770cd8659d7e8b0ce24392d83b4c2f0eb4a3395c955dce5528edd4cc030a794cfa06600fcdd700b3f2de2f9b8e40e309c0011c4180e3be64a0b42e6a1 + languageName: node + linkType: hard + "chardet@npm:^0.7.0": version: 0.7.0 resolution: "chardet@npm:0.7.0" @@ -9037,58 +7632,6 @@ __metadata: languageName: node linkType: hard -"cheerio-select@npm:^2.1.0": - version: 2.1.0 - resolution: "cheerio-select@npm:2.1.0" - dependencies: - boolbase: "npm:^1.0.0" - css-select: "npm:^5.1.0" - css-what: "npm:^6.1.0" - domelementtype: "npm:^2.3.0" - domhandler: "npm:^5.0.3" - domutils: "npm:^3.0.1" - checksum: 10c0/2242097e593919dba4aacb97d7b8275def8b9ec70b00aa1f43335456870cfc9e284eae2080bdc832ed232dabb9eefcf56c722d152da4a154813fb8814a55d282 - languageName: node - linkType: hard - -"cheerio@npm:^1.0.0-rc.12, cheerio@npm:^1.0.0-rc.3": - version: 1.1.2 - resolution: "cheerio@npm:1.1.2" - dependencies: - cheerio-select: "npm:^2.1.0" - dom-serializer: "npm:^2.0.0" - domhandler: "npm:^5.0.3" - domutils: "npm:^3.2.2" - encoding-sniffer: "npm:^0.2.1" - htmlparser2: "npm:^10.0.0" - parse5: "npm:^7.3.0" - parse5-htmlparser2-tree-adapter: "npm:^7.1.0" - parse5-parser-stream: "npm:^7.1.2" - undici: "npm:^7.12.0" - whatwg-mimetype: "npm:^4.0.0" - checksum: 10c0/2c6d2274666fe122f54fdca457ee76453e1a993b19563acaa23eb565bf7776f0f01e4c3800092f00e84aa13c83a161f0cf000ac0a8332d1d7f2b2387d6ecc5fc - languageName: node - linkType: hard - -"chokidar@npm:^3.4.2, chokidar@npm:^3.5.3": - version: 3.6.0 - resolution: "chokidar@npm:3.6.0" - dependencies: - anymatch: "npm:~3.1.2" - braces: "npm:~3.0.2" - fsevents: "npm:~2.3.2" - glob-parent: "npm:~5.1.2" - is-binary-path: "npm:~2.1.0" - is-glob: "npm:~4.0.1" - normalize-path: "npm:~3.0.0" - readdirp: "npm:~3.6.0" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 - languageName: node - linkType: hard - "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -9163,36 +7706,13 @@ __metadata: languageName: node linkType: hard -"clean-css@npm:^5.2.2, clean-css@npm:^5.3.0": - version: 5.3.3 - resolution: "clean-css@npm:5.3.3" - dependencies: - source-map: "npm:~0.6.0" - checksum: 10c0/381de7523e23f3762eb180e327dcc0cedafaf8cb1cd8c26b7cc1fc56e0829a92e734729c4f955394d65ed72fb62f82d8baf78af34b33b8a7d41ebad2accdd6fb - languageName: node - linkType: hard - -"clean-stack@npm:^2.0.0": - version: 2.2.0 - resolution: "clean-stack@npm:2.2.0" - checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1 - languageName: node - linkType: hard - -"cli-boxes@npm:^2.2.0, cli-boxes@npm:^2.2.1": +"cli-boxes@npm:^2.2.0": version: 2.2.1 resolution: "cli-boxes@npm:2.2.1" checksum: 10c0/6111352edbb2f62dbc7bfd58f2d534de507afed7f189f13fa894ce5a48badd94b2aa502fda28f1d7dd5f1eb456e7d4033d09a76660013ef50c7f66e7a034f050 languageName: node linkType: hard -"cli-boxes@npm:^3.0.0": - version: 3.0.0 - resolution: "cli-boxes@npm:3.0.0" - checksum: 10c0/4db3e8fbfaf1aac4fb3a6cbe5a2d3fa048bee741a45371b906439b9ffc821c6e626b0f108bdcd3ddf126a4a319409aedcf39a0730573ff050fdd7b6731e99fb9 - languageName: node - linkType: hard - "cli-cursor@npm:^2.1.0": version: 2.1.0 resolution: "cli-cursor@npm:2.1.0" @@ -9218,19 +7738,6 @@ __metadata: languageName: node linkType: hard -"cli-table3@npm:^0.6.2": - version: 0.6.5 - resolution: "cli-table3@npm:0.6.5" - dependencies: - "@colors/colors": "npm:1.5.0" - string-width: "npm:^4.2.0" - dependenciesMeta: - "@colors/colors": - optional: true - checksum: 10c0/d7cc9ed12212ae68241cc7a3133c52b844113b17856e11f4f81308acc3febcea7cc9fd298e70933e294dd642866b29fd5d113c2c098948701d0c35f09455de78 - languageName: node - linkType: hard - "cli-width@npm:^3.0.0": version: 3.0.0 resolution: "cli-width@npm:3.0.0" @@ -9320,10 +7827,10 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.1.1, clsx@npm:^1.2.1": - version: 1.2.1 - resolution: "clsx@npm:1.2.1" - checksum: 10c0/34dead8bee24f5e96f6e7937d711978380647e936a22e76380290e35486afd8634966ce300fc4b74a32f3762c7d4c0303f442c3e259f4ce02374eb0c82834f27 +"clsx@npm:2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 languageName: node linkType: hard @@ -9341,6 +7848,13 @@ __metadata: languageName: node linkType: hard +"collapse-white-space@npm:^2.0.0": + version: 2.1.0 + resolution: "collapse-white-space@npm:2.1.0" + checksum: 10c0/b2e2800f4ab261e62eb27a1fbe853378296e3a726d6695117ed033e82d61fb6abeae4ffc1465d5454499e237005de9cfc52c9562dc7ca4ac759b9a222ef14453 + languageName: node + linkType: hard + "collect-v8-coverage@npm:^1.0.0": version: 1.0.3 resolution: "collect-v8-coverage@npm:1.0.3" @@ -9420,13 +7934,6 @@ __metadata: languageName: node linkType: hard -"colord@npm:^2.9.1": - version: 2.9.3 - resolution: "colord@npm:2.9.3" - checksum: 10c0/9699e956894d8996b28c686afe8988720785f476f59335c80ce852ded76ab3ebe252703aec53d9bef54f6219aea6b960fb3d9a8300058a1d0c0d4026460cd110 - languageName: node - linkType: hard - "colorette@npm:^1.2.2": version: 1.4.0 resolution: "colorette@npm:1.4.0" @@ -9434,20 +7941,6 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.10": - version: 2.0.20 - resolution: "colorette@npm:2.0.20" - checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40 - languageName: node - linkType: hard - -"combine-promises@npm:^1.1.0": - version: 1.2.0 - resolution: "combine-promises@npm:1.2.0" - checksum: 10c0/906ebf056006eff93c11548df0415053b6756145dae1f5a89579e743cb15fceeb0604555791321db4fba5072aa39bb4de6547e9cdf14589fe949b33d1613422c - languageName: node - linkType: hard - "combined-stream@npm:^1.0.8": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" @@ -9464,6 +7957,13 @@ __metadata: languageName: node linkType: hard +"comma-separated-tokens@npm:^2.0.0": + version: 2.0.3 + resolution: "comma-separated-tokens@npm:2.0.3" + checksum: 10c0/91f90f1aae320f1755d6957ef0b864fe4f54737f3313bd95e0802686ee2ca38bff1dd381964d00ae5db42912dd1f4ae5c2709644e82706ffc6f6842a813cdd67 + languageName: node + linkType: hard + "commander@npm:^12.0.0": version: 12.1.0 resolution: "commander@npm:12.1.0" @@ -9478,13 +7978,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^5.1.0": - version: 5.1.0 - resolution: "commander@npm:5.1.0" - checksum: 10c0/da9d71dbe4ce039faf1fe9eac3771dca8c11d66963341f62602f7b66e36d2a3f8883407af4f9a37b1db1a55c59c0c1325f186425764c2e963dc1d67aec2a4b6d - languageName: node - linkType: hard - "commander@npm:^7.2.0": version: 7.2.0 resolution: "commander@npm:7.2.0" @@ -9492,13 +7985,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^8.3.0": - version: 8.3.0 - resolution: "commander@npm:8.3.0" - checksum: 10c0/8b043bb8322ea1c39664a1598a95e0495bfe4ca2fad0d84a92d7d1d8d213e2a155b441d2470c8e08de7c4a28cf2bc6e169211c49e1b21d9f7edc6ae4d9356060 - languageName: node - linkType: hard - "commitlint@npm:^8.3.4": version: 8.3.6 resolution: "commitlint@npm:8.3.6" @@ -9633,6 +8119,13 @@ __metadata: languageName: node linkType: hard +"compute-scroll-into-view@npm:^3.0.2": + version: 3.1.1 + resolution: "compute-scroll-into-view@npm:3.1.1" + checksum: 10c0/59761ed62304a9599b52ad75d0d6fbf0669ee2ab7dd472fdb0ad9da36628414c014dea7b5810046560180ad30ffec52a953d19297f66a1d4f3aa0999b9d2521d + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -9666,13 +8159,6 @@ __metadata: languageName: node linkType: hard -"connect-history-api-fallback@npm:^2.0.0": - version: 2.0.0 - resolution: "connect-history-api-fallback@npm:2.0.0" - checksum: 10c0/90fa8b16ab76e9531646cc70b010b1dbd078153730c510d3142f6cf07479ae8a812c5a3c0e40a28528dd1681a62395d0cfdef67da9e914c4772ac85d69a3ed87 - languageName: node - linkType: hard - "connect@npm:^3.6.5, connect@npm:^3.7.0": version: 3.7.0 resolution: "connect@npm:3.7.0" @@ -9685,13 +8171,6 @@ __metadata: languageName: node linkType: hard -"consola@npm:^2.15.3": - version: 2.15.3 - resolution: "consola@npm:2.15.3" - checksum: 10c0/34a337e6b4a1349ee4d7b4c568484344418da8fdb829d7d71bfefcd724f608f273987633b6eef465e8de510929907a092e13cb7a28a5d3acb3be446fcc79fd5e - languageName: node - linkType: hard - "constant-case@npm:^2.0.0": version: 2.0.0 resolution: "constant-case@npm:2.0.0" @@ -9702,13 +8181,6 @@ __metadata: languageName: node linkType: hard -"content-disposition@npm:0.5.2": - version: 0.5.2 - resolution: "content-disposition@npm:0.5.2" - checksum: 10c0/49eebaa0da1f9609b192e99d7fec31d1178cb57baa9d01f5b63b29787ac31e9d18b5a1033e854c68c9b6cce790e700a6f7fa60e43f95e2e416404e114a8f2f49 - languageName: node - linkType: hard - "content-disposition@npm:~0.5.4": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" @@ -9942,13 +8414,6 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:^1.7.0": - version: 1.9.0 - resolution: "convert-source-map@npm:1.9.0" - checksum: 10c0/281da55454bf8126cbc6625385928c43479f2060984180c42f3a86c8b8c12720a24eac260624a7d1e090004028d2dee78602330578ceec1a08e27cb8bb0a8a5b - languageName: node - linkType: hard - "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -9963,6 +8428,13 @@ __metadata: languageName: node linkType: hard +"cookie@npm:^1.0.1": + version: 1.1.1 + resolution: "cookie@npm:1.1.1" + checksum: 10c0/79c4ddc0fcad9c4f045f826f42edf54bcc921a29586a4558b0898277fa89fb47be95bc384c2253f493af7b29500c830da28341274527328f18eba9f58afa112c + languageName: node + linkType: hard + "cookie@npm:~0.7.1": version: 0.7.2 resolution: "cookie@npm:0.7.2" @@ -9977,26 +8449,12 @@ __metadata: languageName: node linkType: hard -"copy-text-to-clipboard@npm:^3.0.1": - version: 3.2.2 - resolution: "copy-text-to-clipboard@npm:3.2.2" - checksum: 10c0/451796734a380f7da7b0af27c4d94e449719d3a2f2170e99c7e46eeee54cf3c4b4fdeabc185f6d6e8cbdf932e350831d05e8387c4bae8dcedb7fb961c600ddd4 - languageName: node - linkType: hard - -"copy-webpack-plugin@npm:^11.0.0": - version: 11.0.0 - resolution: "copy-webpack-plugin@npm:11.0.0" +"copy-to-clipboard@npm:^3.3.3": + version: 3.3.3 + resolution: "copy-to-clipboard@npm:3.3.3" dependencies: - fast-glob: "npm:^3.2.11" - glob-parent: "npm:^6.0.1" - globby: "npm:^13.1.1" - normalize-path: "npm:^3.0.0" - schema-utils: "npm:^4.0.0" - serialize-javascript: "npm:^6.0.0" - peerDependencies: - webpack: ^5.1.0 - checksum: 10c0/a667dd226b26f148584a35fb705f5af926d872584912cf9fd203c14f2b3a68f473a1f5cf768ec1dd5da23820823b850e5d50458b685c468e4a224b25c12a15b4 + toggle-selection: "npm:^1.0.6" + checksum: 10c0/3ebf5e8ee00601f8c440b83ec08d838e8eabb068c1fae94a9cda6b42f288f7e1b552f3463635f419af44bf7675afc8d0390d30876cf5c2d5d35f86d9c56a3e5f languageName: node linkType: hard @@ -10018,13 +8476,6 @@ __metadata: languageName: node linkType: hard -"core-js-pure@npm:^3.43.0": - version: 3.47.0 - resolution: "core-js-pure@npm:3.47.0" - checksum: 10c0/7eb5f897e532b33e6ea85ec2c60073fc2fe943e4543ec9903340450fc0f3b46b5b118d57d332e9f2c3d681a8b7b219a4cc64ccf548d933f6b79f754b682696dd - languageName: node - linkType: hard - "core-js@npm:3.6.5": version: 3.6.5 resolution: "core-js@npm:3.6.5" @@ -10039,7 +8490,7 @@ __metadata: languageName: node linkType: hard -"core-js@npm:^3.23.3, core-js@npm:^3.6.3": +"core-js@npm:^3.6.3": version: 3.47.0 resolution: "core-js@npm:3.47.0" checksum: 10c0/9b1a7088b7c660c7b8f1d4c90bb1816a8d5352ebdcb7bc742e3a0e4eb803316b5aa17bacb8769522342196351a5430178f46914644f2bfdb94ce0ced3c7fd523 @@ -10078,20 +8529,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^6.0.0": - version: 6.0.0 - resolution: "cosmiconfig@npm:6.0.0" - dependencies: - "@types/parse-json": "npm:^4.0.0" - import-fresh: "npm:^3.1.0" - parse-json: "npm:^5.0.0" - path-type: "npm:^4.0.0" - yaml: "npm:^1.7.2" - checksum: 10c0/666ed8732d0bf7d7fe6f8516c8ee6041e0622032e8fa26201577b883d2767ad105d03f38b34b93d1f02f26b22a89e7bab4443b9d2e7f931f48d0e944ffa038b5 - languageName: node - linkType: hard - -"cosmiconfig@npm:^7.0.0, cosmiconfig@npm:^7.0.1": +"cosmiconfig@npm:^7.0.0": version: 7.1.0 resolution: "cosmiconfig@npm:7.1.0" dependencies: @@ -10104,23 +8542,6 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^8.3.5": - version: 8.3.6 - resolution: "cosmiconfig@npm:8.3.6" - dependencies: - import-fresh: "npm:^3.3.0" - js-yaml: "npm:^4.1.0" - parse-json: "npm:^5.2.0" - path-type: "npm:^4.0.0" - peerDependencies: - typescript: ">=4.9.5" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/0382a9ed13208f8bfc22ca2f62b364855207dffdb73dc26e150ade78c3093f1cf56172df2dd460c8caf2afa91c0ed4ec8a88c62f8f9cd1cf423d26506aa8797a - languageName: node - linkType: hard - "create-jest@npm:^29.7.0": version: 29.7.0 resolution: "create-jest@npm:29.7.0" @@ -10138,16 +8559,6 @@ __metadata: languageName: node linkType: hard -"create-react-class@npm:^15.7.0": - version: 15.7.0 - resolution: "create-react-class@npm:15.7.0" - dependencies: - loose-envify: "npm:^1.3.1" - object-assign: "npm:^4.1.1" - checksum: 10c0/bce4b46e6d85b424cb50ca8089266c7664fcecfd81abaafb829680fae2e2e60dc6999cac88f5a16a38473ed284859f2328935a42fc5cd1b7cc48888fdd8363c9 - languageName: node - linkType: hard - "cross-fetch@npm:^3.1.5": version: 3.2.0 resolution: "cross-fetch@npm:3.2.0" @@ -10157,7 +8568,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^6.0.0, cross-spawn@npm:^6.0.5": +"cross-spawn@npm:^6.0.0": version: 6.0.6 resolution: "cross-spawn@npm:6.0.6" dependencies: @@ -10188,15 +8599,6 @@ __metadata: languageName: node linkType: hard -"css-declaration-sorter@npm:^6.3.1": - version: 6.4.1 - resolution: "css-declaration-sorter@npm:6.4.1" - peerDependencies: - postcss: ^8.0.9 - checksum: 10c0/b8b664338dac528266a1ed9b27927ac51a907fb16bc1954fa9038b5286c442603bd494cc920c6a3616111309d18ee6b5a85b6d9927938efc942af452a5145160 - languageName: node - linkType: hard - "css-hot-loader@npm:^1.4.4": version: 1.4.4 resolution: "css-hot-loader@npm:1.4.4" @@ -10237,102 +8639,6 @@ __metadata: languageName: node linkType: hard -"css-loader@npm:^6.7.1": - version: 6.11.0 - resolution: "css-loader@npm:6.11.0" - dependencies: - icss-utils: "npm:^5.1.0" - postcss: "npm:^8.4.33" - postcss-modules-extract-imports: "npm:^3.1.0" - postcss-modules-local-by-default: "npm:^4.0.5" - postcss-modules-scope: "npm:^3.2.0" - postcss-modules-values: "npm:^4.0.0" - postcss-value-parser: "npm:^4.2.0" - semver: "npm:^7.5.4" - peerDependencies: - "@rspack/core": 0.x || 1.x - webpack: ^5.0.0 - peerDependenciesMeta: - "@rspack/core": - optional: true - webpack: - optional: true - checksum: 10c0/bb52434138085fed06a33e2ffbdae9ee9014ad23bf60f59d6b7ee67f28f26c6b1764024d3030bd19fd884d6ee6ee2224eaed64ad19eb18fbbb23d148d353a965 - languageName: node - linkType: hard - -"css-minimizer-webpack-plugin@npm:^4.0.0": - version: 4.2.2 - resolution: "css-minimizer-webpack-plugin@npm:4.2.2" - dependencies: - cssnano: "npm:^5.1.8" - jest-worker: "npm:^29.1.2" - postcss: "npm:^8.4.17" - schema-utils: "npm:^4.0.0" - serialize-javascript: "npm:^6.0.0" - source-map: "npm:^0.6.1" - peerDependencies: - webpack: ^5.0.0 - peerDependenciesMeta: - "@parcel/css": - optional: true - "@swc/css": - optional: true - clean-css: - optional: true - csso: - optional: true - esbuild: - optional: true - lightningcss: - optional: true - checksum: 10c0/05cd1460b83d9a5f8878fd63d3a80fd100cbb10f48e295a6ad52519761f3390e1e1bc0e269ff28d15b062a1b11379e04608d50ee30424e177c281bd845fef9fb - languageName: node - linkType: hard - -"css-select@npm:^4.1.3": - version: 4.3.0 - resolution: "css-select@npm:4.3.0" - dependencies: - boolbase: "npm:^1.0.0" - css-what: "npm:^6.0.1" - domhandler: "npm:^4.3.1" - domutils: "npm:^2.8.0" - nth-check: "npm:^2.0.1" - checksum: 10c0/a489d8e5628e61063d5a8fe0fa1cc7ae2478cb334a388a354e91cf2908154be97eac9fa7ed4dffe87a3e06cf6fcaa6016553115335c4fd3377e13dac7bd5a8e1 - languageName: node - linkType: hard - -"css-select@npm:^5.1.0": - version: 5.2.2 - resolution: "css-select@npm:5.2.2" - dependencies: - boolbase: "npm:^1.0.0" - css-what: "npm:^6.1.0" - domhandler: "npm:^5.0.2" - domutils: "npm:^3.0.1" - nth-check: "npm:^2.0.1" - checksum: 10c0/d79fffa97106007f2802589f3ed17b8c903f1c961c0fc28aa8a051eee0cbad394d8446223862efd4c1b40445a6034f626bb639cf2035b0bfc468544177593c99 - languageName: node - linkType: hard - -"css-tree@npm:^1.1.2, css-tree@npm:^1.1.3": - version: 1.1.3 - resolution: "css-tree@npm:1.1.3" - dependencies: - mdn-data: "npm:2.0.14" - source-map: "npm:^0.6.1" - checksum: 10c0/499a507bfa39b8b2128f49736882c0dd636b0cd3370f2c69f4558ec86d269113286b7df469afc955de6a68b0dba00bc533e40022a73698081d600072d5d83c1c - languageName: node - linkType: hard - -"css-what@npm:^6.0.1, css-what@npm:^6.1.0": - version: 6.2.2 - resolution: "css-what@npm:6.2.2" - checksum: 10c0/91e24c26fb977b4ccef30d7007d2668c1c10ac0154cc3f42f7304410e9594fb772aea4f30c832d2993b132ca8d99338050866476210316345ec2e7d47b248a56 - languageName: node - linkType: hard - "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -10342,92 +8648,6 @@ __metadata: languageName: node linkType: hard -"cssnano-preset-advanced@npm:^5.3.8": - version: 5.3.10 - resolution: "cssnano-preset-advanced@npm:5.3.10" - dependencies: - autoprefixer: "npm:^10.4.12" - cssnano-preset-default: "npm:^5.2.14" - postcss-discard-unused: "npm:^5.1.0" - postcss-merge-idents: "npm:^5.1.1" - postcss-reduce-idents: "npm:^5.2.0" - postcss-zindex: "npm:^5.1.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/abfa870a6e3ab52cddfea7cac83f49b510efb941c7f2960ca9d41ae11fabbe03e9453cad7f81fd1b35cf6080c94094cd7bb1b58c07fad4cf0453f6e4bb9a07ae - languageName: node - linkType: hard - -"cssnano-preset-default@npm:^5.2.14": - version: 5.2.14 - resolution: "cssnano-preset-default@npm:5.2.14" - dependencies: - css-declaration-sorter: "npm:^6.3.1" - cssnano-utils: "npm:^3.1.0" - postcss-calc: "npm:^8.2.3" - postcss-colormin: "npm:^5.3.1" - postcss-convert-values: "npm:^5.1.3" - postcss-discard-comments: "npm:^5.1.2" - postcss-discard-duplicates: "npm:^5.1.0" - postcss-discard-empty: "npm:^5.1.1" - postcss-discard-overridden: "npm:^5.1.0" - postcss-merge-longhand: "npm:^5.1.7" - postcss-merge-rules: "npm:^5.1.4" - postcss-minify-font-values: "npm:^5.1.0" - postcss-minify-gradients: "npm:^5.1.1" - postcss-minify-params: "npm:^5.1.4" - postcss-minify-selectors: "npm:^5.2.1" - postcss-normalize-charset: "npm:^5.1.0" - postcss-normalize-display-values: "npm:^5.1.0" - postcss-normalize-positions: "npm:^5.1.1" - postcss-normalize-repeat-style: "npm:^5.1.1" - postcss-normalize-string: "npm:^5.1.0" - postcss-normalize-timing-functions: "npm:^5.1.0" - postcss-normalize-unicode: "npm:^5.1.1" - postcss-normalize-url: "npm:^5.1.0" - postcss-normalize-whitespace: "npm:^5.1.1" - postcss-ordered-values: "npm:^5.1.3" - postcss-reduce-initial: "npm:^5.1.2" - postcss-reduce-transforms: "npm:^5.1.0" - postcss-svgo: "npm:^5.1.0" - postcss-unique-selectors: "npm:^5.1.1" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/d125bdb9ac007f97f920e30be953c550a8e7de0cb9298f67e0bc9744f4b920039046b5a6b817e345872836b08689af747f82fbf2189c8bd48da3e6f0c1087b89 - languageName: node - linkType: hard - -"cssnano-utils@npm:^3.1.0": - version: 3.1.0 - resolution: "cssnano-utils@npm:3.1.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/057508645a3e7584decede1045daa5b362dbfa2f5df96c3527c7d52e41e787a3442a56a8ea0c0af6a757f518e79a459ee580a35c323ad0d0eec912afd67d7630 - languageName: node - linkType: hard - -"cssnano@npm:^5.1.12, cssnano@npm:^5.1.8": - version: 5.1.15 - resolution: "cssnano@npm:5.1.15" - dependencies: - cssnano-preset-default: "npm:^5.2.14" - lilconfig: "npm:^2.0.3" - yaml: "npm:^1.10.2" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/4252e4f4edd7a0fbdd4017825c0f8632b7a12ecbfdd432d2ff7ec268d48eb956a0a10bbf209602181f9f84ceeecea4a864719ecde03aa2cc48f5d9636fcf5f9a - languageName: node - linkType: hard - -"csso@npm:^4.2.0": - version: 4.2.0 - resolution: "csso@npm:4.2.0" - dependencies: - css-tree: "npm:^1.1.2" - checksum: 10c0/f8c6b1300efaa0f8855a7905ae3794a29c6496e7f16a71dec31eb6ca7cfb1f058a4b03fd39b66c4deac6cb06bf6b4ba86da7b67d7320389cb9994d52b924b903 - languageName: node - linkType: hard - "csstype@npm:^3.2.2": version: 3.2.3 resolution: "csstype@npm:3.2.3" @@ -10498,14 +8718,7 @@ __metadata: languageName: node linkType: hard -"debounce@npm:^1.2.1": - version: 1.2.1 - resolution: "debounce@npm:1.2.1" - checksum: 10c0/6c9320aa0973fc42050814621a7a8a78146c1975799b5b3cc1becf1f77ba9a5aa583987884230da0842a03f385def452fad5d60db97c3d1c8b824e38a8edf500 - languageName: node - linkType: hard - -"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.0, debug@npm:^2.6.9": +"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" dependencies: @@ -10514,7 +8727,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.2.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -10564,6 +8777,15 @@ __metadata: languageName: node linkType: hard +"decode-named-character-reference@npm:^1.0.0": + version: 1.3.0 + resolution: "decode-named-character-reference@npm:1.3.0" + dependencies: + character-entities: "npm:^2.0.0" + checksum: 10c0/787f4c87f3b82ea342aa7c2d7b1882b6fb9511bb77f72ae44dcaabea0470bacd1e9c6a0080ab886545019fa0cb3a7109573fad6b61a362844c3a0ac52b36e4bb + languageName: node + linkType: hard + "decode-uri-component@npm:^0.2.0": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" @@ -10655,15 +8877,6 @@ __metadata: languageName: node linkType: hard -"default-gateway@npm:^6.0.3": - version: 6.0.3 - resolution: "default-gateway@npm:6.0.3" - dependencies: - execa: "npm:^5.0.0" - checksum: 10c0/5184f9e6e105d24fb44ade9e8741efa54bb75e84625c1ea78c4ef8b81dff09ca52d6dbdd1185cf0dc655bb6b282a64fffaf7ed2dd561b8d9ad6f322b1f039aba - languageName: node - linkType: hard - "defaults@npm:^1.0.3": version: 1.0.4 resolution: "defaults@npm:1.0.4" @@ -10698,13 +8911,6 @@ __metadata: languageName: node linkType: hard -"define-lazy-prop@npm:^2.0.0": - version: 2.0.0 - resolution: "define-lazy-prop@npm:2.0.0" - checksum: 10c0/db6c63864a9d3b7dc9def55d52764968a5af296de87c1b2cc71d8be8142e445208071953649e0386a8cc37cfcf9a2067a47207f1eb9ff250c2a269658fdae422 - languageName: node - linkType: hard - "define-properties@npm:^1.1.3, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" @@ -10744,22 +8950,6 @@ __metadata: languageName: node linkType: hard -"del@npm:^6.1.1": - version: 6.1.1 - resolution: "del@npm:6.1.1" - dependencies: - globby: "npm:^11.0.1" - graceful-fs: "npm:^4.2.4" - is-glob: "npm:^4.0.1" - is-path-cwd: "npm:^2.2.0" - is-path-inside: "npm:^3.0.2" - p-map: "npm:^4.0.0" - rimraf: "npm:^3.0.2" - slash: "npm:^3.0.0" - checksum: 10c0/8a095c5ccade42c867a60252914ae485ec90da243d735d1f63ec1e64c1cfbc2b8810ad69a29ab6326d159d4fddaa2f5bad067808c42072351ec458efff86708f - languageName: node - linkType: hard - "del@npm:^8.0.1": version: 8.0.1 resolution: "del@npm:8.0.1" @@ -10789,13 +8979,6 @@ __metadata: languageName: node linkType: hard -"depd@npm:~1.1.2": - version: 1.1.2 - resolution: "depd@npm:1.1.2" - checksum: 10c0/acb24aaf936ef9a227b6be6d495f0d2eb20108a9a6ad40585c5bda1a897031512fef6484e4fdbb80bd249fdaa82841fa1039f416ece03188e677ba11bcfda249 - languageName: node - linkType: hard - "deprecated-obj@npm:2.0.0": version: 2.0.0 resolution: "deprecated-obj@npm:2.0.0" @@ -10813,6 +8996,13 @@ __metadata: languageName: node linkType: hard +"dequal@npm:^2.0.0": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 10c0/f98860cdf58b64991ae10205137c0e97d384c3a4edc7f807603887b7c4b850af1224a33d88012009f150861cbee4fa2d322c4cc04b9313bee312e47f6ecaa888 + languageName: node + linkType: hard + "destroy@npm:1.2.0, destroy@npm:~1.2.0": version: 1.2.0 resolution: "destroy@npm:1.2.0" @@ -10820,7 +9010,7 @@ __metadata: languageName: node linkType: hard -"detab@npm:2.0.4, detab@npm:^2.0.0": +"detab@npm:^2.0.0": version: 2.0.4 resolution: "detab@npm:2.0.4" dependencies: @@ -10843,39 +9033,6 @@ __metadata: languageName: node linkType: hard -"detect-node@npm:^2.0.4": - version: 2.1.0 - resolution: "detect-node@npm:2.1.0" - checksum: 10c0/f039f601790f2e9d4654e499913259a798b1f5246ae24f86ab5e8bd4aaf3bce50484234c494f11fb00aecb0c6e2733aa7b1cf3f530865640b65fbbd65b2c4e09 - languageName: node - linkType: hard - -"detect-port-alt@npm:^1.1.6": - version: 1.1.6 - resolution: "detect-port-alt@npm:1.1.6" - dependencies: - address: "npm:^1.0.1" - debug: "npm:^2.6.0" - bin: - detect: ./bin/detect-port - detect-port: ./bin/detect-port - checksum: 10c0/7269e6aef7b782d98c77505c07a7a0f5e2ee98a9607dc791035fc0192fc58aa03cc833fae605e10eaf239a2a5a55cd938e0bb141dea764ac6180ca082fd62b23 - languageName: node - linkType: hard - -"detect-port@npm:^1.3.0": - version: 1.6.1 - resolution: "detect-port@npm:1.6.1" - dependencies: - address: "npm:^1.0.1" - debug: "npm:4" - bin: - detect: bin/detect-port.js - detect-port: bin/detect-port.js - checksum: 10c0/4ea9eb46a637cb21220dd0a62b6074792894fc77b2cacbc9de533d1908b2eedafa7bfd7547baaa2ac1e9c7ba7c289b34b17db896dca6da142f4fc6e2060eee17 - languageName: node - linkType: hard - "detect-repo-changelog@npm:1.0.1": version: 1.0.1 resolution: "detect-repo-changelog@npm:1.0.1" @@ -10888,6 +9045,15 @@ __metadata: languageName: node linkType: hard +"devlop@npm:^1.0.0, devlop@npm:^1.1.0": + version: 1.1.0 + resolution: "devlop@npm:1.1.0" + dependencies: + dequal: "npm:^2.0.0" + checksum: 10c0/e0928ab8f94c59417a2b8389c45c55ce0a02d9ac7fd74ef62d01ba48060129e1d594501b77de01f3eeafc7cb00773819b0df74d96251cf20b31c5b3071f45c0e + languageName: node + linkType: hard + "didyoumean@npm:^1.2.1": version: 1.2.2 resolution: "didyoumean@npm:1.2.2" @@ -10918,15 +9084,6 @@ __metadata: languageName: node linkType: hard -"dns-packet@npm:^5.2.2": - version: 5.6.1 - resolution: "dns-packet@npm:5.6.1" - dependencies: - "@leichtgewicht/ip-codec": "npm:^2.0.1" - checksum: 10c0/8948d3d03063fb68e04a1e386875f8c3bcc398fc375f535f2b438fad8f41bf1afa6f5e70893ba44f4ae884c089247e0a31045722fa6ff0f01d228da103f1811d - languageName: node - linkType: hard - "dnssd-advertise@npm:^1.1.4": version: 1.1.4 resolution: "dnssd-advertise@npm:1.1.4" @@ -10939,34 +9096,26 @@ __metadata: resolution: "docs@workspace:docs" dependencies: "@callstack/react-theme-provider": "npm:^3.0.9" - "@docusaurus/core": "npm:^2.3.6" - "@docusaurus/module-type-aliases": "npm:^2.3.6" - "@docusaurus/preset-classic": "npm:^2.3.6" - "@docusaurus/remark-plugin-npm2yarn": "npm:^2.3.6" - "@docusaurus/theme-common": "npm:^2.3.6" - "@easyops-cn/docusaurus-search-local": "npm:^0.33.4" + "@callstack/rspress-preset": "npm:0.6.6" + "@callstack/rspress-theme": "npm:0.6.6" "@material/material-color-utilities": "npm:0.2.4" - "@mdx-js/react": "npm:^1.6.22" "@react-native-vector-icons/material-design-icons": "npm:^12.0.0" - "@tsconfig/docusaurus": "npm:^1.0.6" + "@rspress/core": "npm:2.0.14" "@types/marked": "npm:^4.0.7" + "@types/react": "npm:^19.2.7" "@types/react-color": "npm:^3.0.6" + "@types/react-dom": "npm:^19.2.3" camelcase: "npm:^7.0.1" - clsx: "npm:^1.2.1" color: "npm:^4.2.3" component-docs: "npm:^0.24.0" marked: "npm:^4.1.1" - patch-package: "npm:^6.5.0" - prism-react-renderer: "npm:^1.3.5" - process: "npm:^0.11.10" - react: "npm:17.0.2" + react: "npm:19.2.6" react-color: "npm:^2.19.3" - react-dom: "npm:17.0.2" - react-native-safe-area-context: "npm:^4.5.0" - react-native-web: "npm:^0.18.12" - typescript: "npm:^4.8.4" - use-latest-callback: "npm:^0.1.7" - webpack: "npm:^5.73.0" + react-dom: "npm:19.2.6" + react-native-safe-area-context: "npm:5.7.0" + react-native-web: "npm:^0.21.2" + typescript: "npm:5.8.3" + use-latest-callback: "npm:^0.2.3" languageName: unknown linkType: soft @@ -10988,15 +9137,6 @@ __metadata: languageName: node linkType: hard -"dom-converter@npm:^0.2.0": - version: 0.2.0 - resolution: "dom-converter@npm:0.2.0" - dependencies: - utila: "npm:~0.4" - checksum: 10c0/e96aa63bd8c6ee3cd9ce19c3aecfc2c42e50a460e8087114794d4f5ecf3a4f052b34ea3bf2d73b5d80b4da619073b49905e6d7d788ceb7814ca4c29be5354a11 - languageName: node - linkType: hard - "dom-serializer@npm:^1.0.1": version: 1.4.1 resolution: "dom-serializer@npm:1.4.1" @@ -11008,18 +9148,7 @@ __metadata: languageName: node linkType: hard -"dom-serializer@npm:^2.0.0": - version: 2.0.0 - resolution: "dom-serializer@npm:2.0.0" - dependencies: - domelementtype: "npm:^2.3.0" - domhandler: "npm:^5.0.2" - entities: "npm:^4.2.0" - checksum: 10c0/d5ae2b7110ca3746b3643d3ef60ef823f5f078667baf530cec096433f1627ec4b6fa8c072f09d079d7cda915fd2c7bc1b7b935681e9b09e591e1e15f4040b8e2 - languageName: node - linkType: hard - -"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": +"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": version: 2.3.0 resolution: "domelementtype@npm:2.3.0" checksum: 10c0/686f5a9ef0fff078c1412c05db73a0dce096190036f33e400a07e2a4518e9f56b1e324f5c576a0a747ef0e75b5d985c040b0d51945ce780c0dd3c625a18cd8c9 @@ -11035,7 +9164,7 @@ __metadata: languageName: node linkType: hard -"domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": +"domhandler@npm:^4.2.0": version: 4.3.1 resolution: "domhandler@npm:4.3.1" dependencies: @@ -11044,16 +9173,7 @@ __metadata: languageName: node linkType: hard -"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": - version: 5.0.3 - resolution: "domhandler@npm:5.0.3" - dependencies: - domelementtype: "npm:^2.3.0" - checksum: 10c0/bba1e5932b3e196ad6862286d76adc89a0dbf0c773e5ced1eb01f9af930c50093a084eff14b8de5ea60b895c56a04d5de8bbc4930c5543d029091916770b2d2a - languageName: node - linkType: hard - -"domutils@npm:^2.0.0, domutils@npm:^2.5.2, domutils@npm:^2.8.0": +"domutils@npm:^2.0.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" dependencies: @@ -11064,17 +9184,6 @@ __metadata: languageName: node linkType: hard -"domutils@npm:^3.0.1, domutils@npm:^3.2.1, domutils@npm:^3.2.2": - version: 3.2.2 - resolution: "domutils@npm:3.2.2" - dependencies: - dom-serializer: "npm:^2.0.0" - domelementtype: "npm:^2.3.0" - domhandler: "npm:^5.0.3" - checksum: 10c0/47938f473b987ea71cd59e59626eb8666d3aa8feba5266e45527f3b636c7883cca7e582d901531961f742c519d7514636b7973353b648762b2e3bedbf235fada - languageName: node - linkType: hard - "dot-case@npm:^2.1.0": version: 2.1.1 resolution: "dot-case@npm:2.1.1" @@ -11084,16 +9193,6 @@ __metadata: languageName: node linkType: hard -"dot-case@npm:^3.0.4": - version: 3.0.4 - resolution: "dot-case@npm:3.0.4" - dependencies: - no-case: "npm:^3.0.4" - tslib: "npm:^2.0.3" - checksum: 10c0/5b859ea65097a7ea870e2c91b5768b72ddf7fa947223fd29e167bcdff58fe731d941c48e47a38ec8aa8e43044c8fbd15cd8fa21689a526bc34b6548197cd5b05 - languageName: node - linkType: hard - "dot-prop@npm:^3.0.0": version: 3.0.0 resolution: "dot-prop@npm:3.0.0" @@ -11130,20 +9229,6 @@ __metadata: languageName: node linkType: hard -"duplexer@npm:^0.1.2": - version: 0.1.2 - resolution: "duplexer@npm:0.1.2" - checksum: 10c0/c57bcd4bdf7e623abab2df43a7b5b23d18152154529d166c1e0da6bee341d84c432d157d7e97b32fecb1bf3a8b8857dd85ed81a915789f550637ed25b8e64fc2 - languageName: node - linkType: hard - -"eastasianwidth@npm:^0.2.0": - version: 0.2.0 - resolution: "eastasianwidth@npm:0.2.0" - checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 - languageName: node - linkType: hard - "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -11193,13 +9278,6 @@ __metadata: languageName: node linkType: hard -"emoji-regex@npm:^9.2.2": - version: 9.2.2 - resolution: "emoji-regex@npm:9.2.2" - checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 - languageName: node - linkType: hard - "emojis-list@npm:^3.0.0": version: 3.0.0 resolution: "emojis-list@npm:3.0.0" @@ -11207,13 +9285,6 @@ __metadata: languageName: node linkType: hard -"emoticon@npm:^3.2.0": - version: 3.2.0 - resolution: "emoticon@npm:3.2.0" - checksum: 10c0/ee0078d81f64fd1b5ce928ea88cbc3f7a1b3c4d731de1cabb53e2411eba412ad3b152b0982a184c7d0818b01d704ac5a238f3b9aaf8c187ddc51d8c17a9881b8 - languageName: node - linkType: hard - "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -11228,16 +9299,6 @@ __metadata: languageName: node linkType: hard -"encoding-sniffer@npm:^0.2.1": - version: 0.2.1 - resolution: "encoding-sniffer@npm:0.2.1" - dependencies: - iconv-lite: "npm:^0.6.3" - whatwg-encoding: "npm:^3.1.1" - checksum: 10c0/d6b591880788f3baf8dd1744636dd189d24a1ec93e6f9817267c60ac3458a5191ca70ab1a186fb67731beff1c3489c6527dfdc4718158ed8460ab2f400dd5e7d - languageName: node - linkType: hard - "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -11284,13 +9345,6 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0, entities@npm:^4.4.0": - version: 4.5.0 - resolution: "entities@npm:4.5.0" - checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 - languageName: node - linkType: hard - "entities@npm:^6.0.0": version: 6.0.1 resolution: "entities@npm:6.0.1" @@ -11498,6 +9552,30 @@ __metadata: languageName: node linkType: hard +"esast-util-from-estree@npm:^2.0.0": + version: 2.0.0 + resolution: "esast-util-from-estree@npm:2.0.0" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + devlop: "npm:^1.0.0" + estree-util-visit: "npm:^2.0.0" + unist-util-position-from-estree: "npm:^2.0.0" + checksum: 10c0/6c619bc6963314f8f64b32e3b101b321bf121f659e62b11e70f425619c2db6f1d25f4c594a57fd00908da96c67d9bfbf876eb5172abf9e13f47a71796f6630ff + languageName: node + linkType: hard + +"esast-util-from-js@npm:^2.0.0": + version: 2.0.1 + resolution: "esast-util-from-js@npm:2.0.1" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + acorn: "npm:^8.0.0" + esast-util-from-estree: "npm:^2.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/3a446fb0b0d7bcd7e0157aa44b3b692802a08c93edbea81cc0f7fe4437bfdfb4b72e4563fe63b4e36d390086b71185dba4ac921f4180cc6349985c263cc74421 + languageName: node + linkType: hard + "escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -11921,6 +9999,65 @@ __metadata: languageName: node linkType: hard +"estree-util-attach-comments@npm:^3.0.0": + version: 3.0.0 + resolution: "estree-util-attach-comments@npm:3.0.0" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/ee69bb5c45e2ad074725b90ed181c1c934b29d81bce4b0c7761431e83c4c6ab1b223a6a3d6a4fbeb92128bc5d5ee201d5dd36cf1770aa5e16a40b0cf36e8a1f1 + languageName: node + linkType: hard + +"estree-util-build-jsx@npm:^3.0.0": + version: 3.0.1 + resolution: "estree-util-build-jsx@npm:3.0.1" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + devlop: "npm:^1.0.0" + estree-util-is-identifier-name: "npm:^3.0.0" + estree-walker: "npm:^3.0.0" + checksum: 10c0/274c119817b8e7caa14a9778f1e497fea56cdd2b01df1a1ed037f843178992d3afe85e0d364d485e1e2e239255763553d1b647b15e4a7ba50851bcb43dc6bf80 + languageName: node + linkType: hard + +"estree-util-is-identifier-name@npm:^3.0.0": + version: 3.0.0 + resolution: "estree-util-is-identifier-name@npm:3.0.0" + checksum: 10c0/d1881c6ed14bd588ebd508fc90bf2a541811dbb9ca04dec2f39d27dcaa635f85b5ed9bbbe7fc6fb1ddfca68744a5f7c70456b4b7108b6c4c52780631cc787c5b + languageName: node + linkType: hard + +"estree-util-scope@npm:^1.0.0": + version: 1.0.0 + resolution: "estree-util-scope@npm:1.0.0" + dependencies: + "@types/estree": "npm:^1.0.0" + devlop: "npm:^1.0.0" + checksum: 10c0/ef8a573cc899277c613623a1722f630e2163abbc6e9e2f49e758c59b81b484e248b585df6df09a38c00fbfb6390117997cc80c1347b7a86bc1525d9e462b60d5 + languageName: node + linkType: hard + +"estree-util-to-js@npm:^2.0.0": + version: 2.0.0 + resolution: "estree-util-to-js@npm:2.0.0" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + astring: "npm:^1.8.0" + source-map: "npm:^0.7.0" + checksum: 10c0/ac88cb831401ef99e365f92f4af903755d56ae1ce0e0f0fb8ff66e678141f3d529194f0fb15f6c78cd7554c16fda36854df851d58f9e05cfab15bddf7a97cea0 + languageName: node + linkType: hard + +"estree-util-visit@npm:^2.0.0": + version: 2.0.0 + resolution: "estree-util-visit@npm:2.0.0" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + "@types/unist": "npm:^3.0.0" + checksum: 10c0/acda8b03cc8f890d79c7c7361f6c95331ba84b7ccc0c32b49f447fc30206b20002b37ffdfc97b6ad16e6fe065c63ecbae1622492e2b6b4775c15966606217f39 + languageName: node + linkType: hard + "estree-walker@npm:^0.6.1": version: 0.6.1 resolution: "estree-walker@npm:0.6.1" @@ -11928,6 +10065,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.0": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -11935,13 +10081,6 @@ __metadata: languageName: node linkType: hard -"eta@npm:^2.0.0": - version: 2.2.0 - resolution: "eta@npm:2.2.0" - checksum: 10c0/643b54d9539d2761bf6c5f4f48df1a5ea2d46c7f5a5fdc47a7d4802a8aa2b6262d4d61f724452e226c18cf82db02d48e65293fcc548f26a3f9d75a5ba7c3b859 - languageName: node - linkType: hard - "etag@npm:~1.8.1": version: 1.8.1 resolution: "etag@npm:1.8.1" @@ -11949,16 +10088,6 @@ __metadata: languageName: node linkType: hard -"eval@npm:^0.1.8": - version: 0.1.8 - resolution: "eval@npm:0.1.8" - dependencies: - "@types/node": "npm:*" - require-like: "npm:>= 0.1.1" - checksum: 10c0/258e700bff09e3ce3344273d5b6691b8ec5b043538d84f738f14d8b0aded33d64c00c15b380de725b1401b15f428ab35a9e7ca19a7d25f162c4f877c71586be9 - languageName: node - linkType: hard - "event-target-shim@npm:^5.0.0": version: 5.0.1 resolution: "event-target-shim@npm:5.0.1" @@ -11966,13 +10095,6 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.0": - version: 4.0.7 - resolution: "eventemitter3@npm:4.0.7" - checksum: 10c0/5f6d97cbcbac47be798e6355e3a7639a84ee1f7d9b199a07017f1d2f1e2fe236004d14fa5dfaeba661f94ea57805385e326236a6debbc7145c8877fbc0297c6b - languageName: node - linkType: hard - "events@npm:^3.2.0": version: 3.3.0 resolution: "events@npm:3.3.0" @@ -12425,7 +10547,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.17.1, express@npm:^4.17.3": +"express@npm:^4.17.1": version: 4.22.1 resolution: "express@npm:4.22.1" dependencies: @@ -12531,7 +10653,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.1.1, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0, fast-glob@npm:^3.3.3": +"fast-glob@npm:^3.1.1, fast-glob@npm:^3.3.3": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -12558,13 +10680,6 @@ __metadata: languageName: node linkType: hard -"fast-loops@npm:^1.1.3": - version: 1.1.4 - resolution: "fast-loops@npm:1.1.4" - checksum: 10c0/25e8a608fccc0d84c1d037efa715ab1e6f21576e1070931b3ed966657204c47ed2b1cba16e5c46ddde2d62aba0b4100d86616d995318b7367fa0a902a78ed885 - languageName: node - linkType: hard - "fast-uri@npm:^3.0.1": version: 3.1.0 resolution: "fast-uri@npm:3.1.0" @@ -12581,15 +10696,6 @@ __metadata: languageName: node linkType: hard -"faye-websocket@npm:^0.11.3": - version: 0.11.4 - resolution: "faye-websocket@npm:0.11.4" - dependencies: - websocket-driver: "npm:>=0.5.1" - checksum: 10c0/c6052a0bb322778ce9f89af92890f6f4ce00d5ec92418a35e5f4c6864a4fe736fec0bcebd47eac7c0f0e979b01530746b1c85c83cb04bae789271abf19737420 - languageName: node - linkType: hard - "fb-dotslash@npm:0.5.8": version: 0.5.8 resolution: "fb-dotslash@npm:0.5.8" @@ -12608,15 +10714,6 @@ __metadata: languageName: node linkType: hard -"fbemitter@npm:^3.0.0": - version: 3.0.0 - resolution: "fbemitter@npm:3.0.0" - dependencies: - fbjs: "npm:^3.0.0" - checksum: 10c0/f130dd8e15dc3fc6709a26586b7a589cd994e1d1024b624f2cc8ef1b12401536a94bb30038e68150a24f9ba18863e9a3fe87941ade2c87667bfbd17f4848d5c7 - languageName: node - linkType: hard - "fbjs-css-vars@npm:^1.0.0": version: 1.0.2 resolution: "fbjs-css-vars@npm:1.0.2" @@ -12624,7 +10721,7 @@ __metadata: languageName: node linkType: hard -"fbjs@npm:^3.0.0, fbjs@npm:^3.0.1, fbjs@npm:^3.0.4": +"fbjs@npm:^3.0.4": version: 3.0.5 resolution: "fbjs@npm:3.0.5" dependencies: @@ -12651,15 +10748,6 @@ __metadata: languageName: node linkType: hard -"feed@npm:^4.2.2": - version: 4.2.2 - resolution: "feed@npm:4.2.2" - dependencies: - xml-js: "npm:^1.6.11" - checksum: 10c0/c0849bde569da94493224525db00614fd1855a5d7c2e990f6e8637bd0298e85c3d329efe476cba77e711e438c3fb48af60cd5ef0c409da5bcd1f479790b0a372 - languageName: node - linkType: hard - "fetch-nodeshim@npm:^0.4.10": version: 0.4.10 resolution: "fetch-nodeshim@npm:0.4.10" @@ -12697,18 +10785,6 @@ __metadata: languageName: node linkType: hard -"file-loader@npm:^6.2.0": - version: 6.2.0 - resolution: "file-loader@npm:6.2.0" - dependencies: - loader-utils: "npm:^2.0.0" - schema-utils: "npm:^3.0.0" - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - checksum: 10c0/e176a57c2037ab0f78e5755dbf293a6b7f0f8392350a120bd03cc2ce2525bea017458ba28fea14ca535ff1848055e86d1a3a216bdb2561ef33395b27260a1dd3 - languageName: node - linkType: hard - "filename-reserved-regex@npm:^2.0.0": version: 2.0.0 resolution: "filename-reserved-regex@npm:2.0.0" @@ -12727,13 +10803,6 @@ __metadata: languageName: node linkType: hard -"filesize@npm:^8.0.6": - version: 8.0.7 - resolution: "filesize@npm:8.0.7" - checksum: 10c0/82072d94816484df5365d4d5acbb2327a65dc49704c64e403e8c40d8acb7364de1cf1e65cb512c77a15d353870f73e4fed46dad5c6153d0618d9ce7a64d09cfc - languageName: node - linkType: hard - "fill-range@npm:^4.0.0": version: 4.0.0 resolution: "fill-range@npm:4.0.0" @@ -12880,15 +10949,6 @@ __metadata: languageName: node linkType: hard -"find-yarn-workspace-root@npm:^2.0.0": - version: 2.0.0 - resolution: "find-yarn-workspace-root@npm:2.0.0" - dependencies: - micromatch: "npm:^4.0.2" - checksum: 10c0/b0d3843013fbdaf4e57140e0165889d09fa61745c9e85da2af86e54974f4cc9f1967e40f0d8fc36a79d53091f0829c651d06607d552582e53976f3cd8f4e5689 - languageName: node - linkType: hard - "flat-cache@npm:^4.0.0": version: 4.0.1 resolution: "flat-cache@npm:4.0.1" @@ -12915,6 +10975,13 @@ __metadata: languageName: node linkType: hard +"flexsearch@npm:0.8.212": + version: 0.8.212 + resolution: "flexsearch@npm:0.8.212" + checksum: 10c0/557a320fe028e694579d9f3f4cacd6acab6bcfa9101a63dbacf23ddbc872310a3370586a12007cf080ed2dfe3c99e6b21dd1e83697f2b4734722fbd43285d782 + languageName: node + linkType: hard + "flow-enums-runtime@npm:^0.0.6": version: 0.0.6 resolution: "flow-enums-runtime@npm:0.0.6" @@ -12922,28 +10989,6 @@ __metadata: languageName: node linkType: hard -"flux@npm:^4.0.1": - version: 4.0.4 - resolution: "flux@npm:4.0.4" - dependencies: - fbemitter: "npm:^3.0.0" - fbjs: "npm:^3.0.1" - peerDependencies: - react: ^15.0.2 || ^16.0.0 || ^17.0.0 - checksum: 10c0/948bc01b97ff21babc8bfe5c40543d643ca126b71edd447a9ac051b05d20fd549a6bcc4afe043bcde56201782e688a5eaeda1bd8e3e58915641abdd5b3ea21e0 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.7": - version: 1.15.11 - resolution: "follow-redirects@npm:1.15.11" - peerDependenciesMeta: - debug: - optional: true - checksum: 10c0/d301f430542520a54058d4aeeb453233c564aaccac835d29d15e050beb33f339ad67d9bddbce01739c5dc46a6716dbe3d9d0d5134b1ca203effa11a7ef092343 - languageName: node - linkType: hard - "fontfaceobserver@npm:^2.1.0": version: 2.3.0 resolution: "fontfaceobserver@npm:2.3.0" @@ -12977,37 +11022,6 @@ __metadata: languageName: node linkType: hard -"fork-ts-checker-webpack-plugin@npm:^6.5.0": - version: 6.5.3 - resolution: "fork-ts-checker-webpack-plugin@npm:6.5.3" - dependencies: - "@babel/code-frame": "npm:^7.8.3" - "@types/json-schema": "npm:^7.0.5" - chalk: "npm:^4.1.0" - chokidar: "npm:^3.4.2" - cosmiconfig: "npm:^6.0.0" - deepmerge: "npm:^4.2.2" - fs-extra: "npm:^9.0.0" - glob: "npm:^7.1.6" - memfs: "npm:^3.1.2" - minimatch: "npm:^3.0.4" - schema-utils: "npm:2.7.0" - semver: "npm:^7.3.2" - tapable: "npm:^1.0.0" - peerDependencies: - eslint: ">= 6" - typescript: ">= 2.7" - vue-template-compiler: "*" - webpack: ">= 4" - peerDependenciesMeta: - eslint: - optional: true - vue-template-compiler: - optional: true - checksum: 10c0/0885ea75474de011d4068ca3e2d3ca6e4cd318f5cfa018e28ff8fef23ef3a1f1c130160ef192d3e5d31ef7b6fe9f8fb1d920eab5e9e449fb30ce5cc96647245c - languageName: node - linkType: hard - "form-data@npm:3.0.0": version: 3.0.0 resolution: "form-data@npm:3.0.0" @@ -13026,13 +11040,6 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^5.3.4": - version: 5.3.4 - resolution: "fraction.js@npm:5.3.4" - checksum: 10c0/f90079fe9bfc665e0a07079938e8ff71115bce9462f17b32fc283f163b0540ec34dc33df8ed41bb56f028316b04361b9a9995b9ee9258617f8338e0b05c5f95a - languageName: node - linkType: hard - "fragment-cache@npm:^0.2.1": version: 0.2.1 resolution: "fragment-cache@npm:0.2.1" @@ -13058,17 +11065,6 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^10.0.0, fs-extra@npm:^10.1.0": - version: 10.1.0 - resolution: "fs-extra@npm:10.1.0" - dependencies: - graceful-fs: "npm:^4.2.0" - jsonfile: "npm:^6.0.1" - universalify: "npm:^2.0.0" - checksum: 10c0/5f579466e7109719d162a9249abbeffe7f426eb133ea486e020b89bc6d67a741134076bf439983f2eb79276ceaf6bd7b7c1e43c3fd67fe889863e69072fb0a5e - languageName: node - linkType: hard - "fs-extra@npm:^11.3.4": version: 11.3.5 resolution: "fs-extra@npm:11.3.5" @@ -13091,7 +11087,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^9.0.0, fs-extra@npm:^9.0.1": +"fs-extra@npm:^9.0.1": version: 9.1.0 resolution: "fs-extra@npm:9.1.0" dependencies: @@ -13126,7 +11122,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:^2.3.2": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -13136,7 +11132,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -13180,7 +11176,7 @@ __metadata: languageName: node linkType: hard -"gensync@npm:^1.0.0-beta.1, gensync@npm:^1.0.0-beta.2": +"gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 @@ -13194,7 +11190,7 @@ __metadata: languageName: node linkType: hard -"get-east-asian-width@npm:^1.0.0": +"get-east-asian-width@npm:^1.0.0, get-east-asian-width@npm:^1.4.0": version: 1.6.0 resolution: "get-east-asian-width@npm:1.6.0" checksum: 10c0/7e72e9550fd49ca5b246f9af6bb2afc129c96412845ff6556b3274fd44817a381702ca17028efe9866b261a3d44254cbf21e6c90cf05b4b61675630af776d431 @@ -13222,13 +11218,6 @@ __metadata: languageName: node linkType: hard -"get-own-enumerable-property-symbols@npm:^3.0.0": - version: 3.0.2 - resolution: "get-own-enumerable-property-symbols@npm:3.0.2" - checksum: 10c0/103999855f3d1718c631472437161d76962cbddcd95cc642a34c07bfb661ed41b6c09a9c669ccdff89ee965beb7126b80eec7b2101e20e31e9cc6c4725305e10 - languageName: node - linkType: hard - "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -13382,14 +11371,7 @@ __metadata: languageName: node linkType: hard -"github-slugger@npm:^1.4.0": - version: 1.5.0 - resolution: "github-slugger@npm:1.5.0" - checksum: 10c0/116f99732925f939cbfd6f2e57db1aa7e111a460db0d103e3b3f2fce6909d44311663d4542350706cad806345b9892358cc3b153674f88eeae77f43380b3bfca - languageName: node - linkType: hard - -"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": +"glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" dependencies: @@ -13398,7 +11380,7 @@ __metadata: languageName: node linkType: hard -"glob-parent@npm:^6.0.1, glob-parent@npm:^6.0.2": +"glob-parent@npm:^6.0.2": version: 6.0.2 resolution: "glob-parent@npm:6.0.2" dependencies: @@ -13468,35 +11450,6 @@ __metadata: languageName: node linkType: hard -"global-dirs@npm:^3.0.0": - version: 3.0.1 - resolution: "global-dirs@npm:3.0.1" - dependencies: - ini: "npm:2.0.0" - checksum: 10c0/ef65e2241a47ff978f7006a641302bc7f4c03dfb98783d42bf7224c136e3a06df046e70ee3a010cf30214114755e46c9eb5eb1513838812fbbe0d92b14c25080 - languageName: node - linkType: hard - -"global-modules@npm:^2.0.0": - version: 2.0.0 - resolution: "global-modules@npm:2.0.0" - dependencies: - global-prefix: "npm:^3.0.0" - checksum: 10c0/43b770fe24aa6028f4b9770ea583a47f39750be15cf6e2578f851e4ccc9e4fa674b8541928c0b09c21461ca0763f0d36e4068cec86c914b07fd6e388e66ba5b9 - languageName: node - linkType: hard - -"global-prefix@npm:^3.0.0": - version: 3.0.0 - resolution: "global-prefix@npm:3.0.0" - dependencies: - ini: "npm:^1.3.5" - kind-of: "npm:^6.0.2" - which: "npm:^1.3.1" - checksum: 10c0/510f489fb68d1cc7060f276541709a0ee6d41356ef852de48f7906c648ac223082a1cc8fce86725ca6c0e032bcdc1189ae77b4744a624b29c34a9d0ece498269 - languageName: node - linkType: hard - "globals@npm:^14.0.0": version: 14.0.0 resolution: "globals@npm:14.0.0" @@ -13535,33 +11488,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.0.1, globby@npm:^11.0.4, globby@npm:^11.1.0": - version: 11.1.0 - resolution: "globby@npm:11.1.0" - dependencies: - array-union: "npm:^2.1.0" - dir-glob: "npm:^3.0.1" - fast-glob: "npm:^3.2.9" - ignore: "npm:^5.2.0" - merge2: "npm:^1.4.1" - slash: "npm:^3.0.0" - checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189 - languageName: node - linkType: hard - -"globby@npm:^13.1.1": - version: 13.2.2 - resolution: "globby@npm:13.2.2" - dependencies: - dir-glob: "npm:^3.0.1" - fast-glob: "npm:^3.3.0" - ignore: "npm:^5.2.4" - merge2: "npm:^1.4.1" - slash: "npm:^4.0.0" - checksum: 10c0/a8d7cc7cbe5e1b2d0f81d467bbc5bc2eac35f74eaded3a6c85fc26d7acc8e6de22d396159db8a2fc340b8a342e74cac58de8f4aee74146d3d146921a76062664 - languageName: node - linkType: hard - "globby@npm:^14.0.2": version: 14.1.0 resolution: "globby@npm:14.1.0" @@ -13621,41 +11547,13 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.3, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 languageName: node linkType: hard -"gray-matter@npm:^4.0.3": - version: 4.0.3 - resolution: "gray-matter@npm:4.0.3" - dependencies: - js-yaml: "npm:^3.13.1" - kind-of: "npm:^6.0.2" - section-matter: "npm:^1.0.0" - strip-bom-string: "npm:^1.0.0" - checksum: 10c0/e38489906dad4f162ca01e0dcbdbed96d1a53740cef446b9bf76d80bec66fa799af07776a18077aee642346c5e1365ed95e4c91854a12bf40ba0d4fb43a625a6 - languageName: node - linkType: hard - -"gzip-size@npm:^6.0.0": - version: 6.0.0 - resolution: "gzip-size@npm:6.0.0" - dependencies: - duplexer: "npm:^0.1.2" - checksum: 10c0/4ccb924626c82125897a997d1c84f2377846a6ef57fbee38f7c0e6b41387fba4d00422274440747b58008b5d60114bac2349c2908e9aba55188345281af40a3f - languageName: node - linkType: hard - -"handle-thing@npm:^2.0.0": - version: 2.0.1 - resolution: "handle-thing@npm:2.0.1" - checksum: 10c0/7ae34ba286a3434f1993ebd1cc9c9e6b6d8ea672182db28b1afc0a7119229552fa7031e3e5f3cd32a76430ece4e94b7da6f12af2eb39d6239a7693e4bd63a998 - languageName: node - linkType: hard - "handlebars@npm:^4.7.7": version: 4.7.8 resolution: "handlebars@npm:4.7.8" @@ -13805,21 +11703,6 @@ __metadata: languageName: node linkType: hard -"hast-to-hyperscript@npm:^9.0.0": - version: 9.0.1 - resolution: "hast-to-hyperscript@npm:9.0.1" - dependencies: - "@types/unist": "npm:^2.0.3" - comma-separated-tokens: "npm:^1.0.0" - property-information: "npm:^5.3.0" - space-separated-tokens: "npm:^1.0.0" - style-to-object: "npm:^0.3.0" - unist-util-is: "npm:^4.0.0" - web-namespaces: "npm:^1.0.0" - checksum: 10c0/630f0db8e1c78d8d6e4f8bd19dec4b6ff6c3048ba0b07b8e34bb812dfbbdc96f4c16abca16c3bfc64e7757921f42790a7bd4a693d6ce99375f99dead65a19a12 - languageName: node - linkType: hard - "hast-util-from-parse5@npm:^5.0.0": version: 5.0.3 resolution: "hast-util-from-parse5@npm:5.0.3" @@ -13847,6 +11730,31 @@ __metadata: languageName: node linkType: hard +"hast-util-from-parse5@npm:^8.0.0": + version: 8.0.3 + resolution: "hast-util-from-parse5@npm:8.0.3" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + devlop: "npm:^1.0.0" + hastscript: "npm:^9.0.0" + property-information: "npm:^7.0.0" + vfile: "npm:^6.0.0" + vfile-location: "npm:^5.0.0" + web-namespaces: "npm:^2.0.0" + checksum: 10c0/40ace6c0ad43c26f721c7499fe408e639cde917b2350c9299635e6326559855896dae3c3ebf7440df54766b96c4276a7823e8f376a2b6a28b37b591f03412545 + languageName: node + linkType: hard + +"hast-util-heading-rank@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-heading-rank@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/1879c84f629e73f1f13247ab349324355cd801363b44e3d46f763aa5c0ea3b42dcd47b46e5643a0502cf01a6b1fdb9208fd12852e44ca6c671b3e4bccf9369a1 + languageName: node + linkType: hard + "hast-util-is-element@npm:^1.0.0": version: 1.1.0 resolution: "hast-util-is-element@npm:1.1.0" @@ -13854,6 +11762,15 @@ __metadata: languageName: node linkType: hard +"hast-util-is-element@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-is-element@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/f5361e4c9859c587ca8eb0d8343492f3077ccaa0f58a44cd09f35d5038f94d65152288dcd0c19336ef2c9491ec4d4e45fde2176b05293437021570aa0bc3613b + languageName: node + linkType: hard + "hast-util-parse-selector@npm:^2.0.0": version: 2.2.5 resolution: "hast-util-parse-selector@npm:2.2.5" @@ -13861,21 +11778,12 @@ __metadata: languageName: node linkType: hard -"hast-util-raw@npm:6.0.1": - version: 6.0.1 - resolution: "hast-util-raw@npm:6.0.1" +"hast-util-parse-selector@npm:^4.0.0": + version: 4.0.0 + resolution: "hast-util-parse-selector@npm:4.0.0" dependencies: - "@types/hast": "npm:^2.0.0" - hast-util-from-parse5: "npm:^6.0.0" - hast-util-to-parse5: "npm:^6.0.0" - html-void-elements: "npm:^1.0.0" - parse5: "npm:^6.0.0" - unist-util-position: "npm:^3.0.0" - vfile: "npm:^4.0.0" - web-namespaces: "npm:^1.0.0" - xtend: "npm:^4.0.0" - zwitch: "npm:^1.0.0" - checksum: 10c0/0ed0a2731251a4853710eda38e0bb79ee1ad8ccea69b391c16eb20895895818bced1c2c9eaf8853280f0aa6dc71d22b9eb6c9aab770dd1a225bb44d522eef1ef + "@types/hast": "npm:^3.0.0" + checksum: 10c0/5e98168cb44470dc274aabf1a28317e4feb09b1eaf7a48bbaa8c1de1b43a89cd195cb1284e535698e658e3ec26ad91bc5e52c9563c36feb75abbc68aaf68fb9f languageName: node linkType: hard @@ -13895,6 +11803,51 @@ __metadata: languageName: node linkType: hard +"hast-util-raw@npm:^9.0.0": + version: 9.1.0 + resolution: "hast-util-raw@npm:9.1.0" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + hast-util-from-parse5: "npm:^8.0.0" + hast-util-to-parse5: "npm:^8.0.0" + html-void-elements: "npm:^3.0.0" + mdast-util-to-hast: "npm:^13.0.0" + parse5: "npm:^7.0.0" + unist-util-position: "npm:^5.0.0" + unist-util-visit: "npm:^5.0.0" + vfile: "npm:^6.0.0" + web-namespaces: "npm:^2.0.0" + zwitch: "npm:^2.0.0" + checksum: 10c0/d0d909d2aedecef6a06f0005cfae410d6475e6e182d768bde30c3af9fcbbe4f9beb0522bdc21d0679cb3c243c0df40385797ed255148d68b3d3f12e82d12aacc + languageName: node + linkType: hard + +"hast-util-to-estree@npm:^3.0.0": + version: 3.1.3 + resolution: "hast-util-to-estree@npm:3.1.3" + dependencies: + "@types/estree": "npm:^1.0.0" + "@types/estree-jsx": "npm:^1.0.0" + "@types/hast": "npm:^3.0.0" + comma-separated-tokens: "npm:^2.0.0" + devlop: "npm:^1.0.0" + estree-util-attach-comments: "npm:^3.0.0" + estree-util-is-identifier-name: "npm:^3.0.0" + hast-util-whitespace: "npm:^3.0.0" + mdast-util-mdx-expression: "npm:^2.0.0" + mdast-util-mdx-jsx: "npm:^3.0.0" + mdast-util-mdxjs-esm: "npm:^2.0.0" + property-information: "npm:^7.0.0" + space-separated-tokens: "npm:^2.0.0" + style-to-js: "npm:^1.0.0" + unist-util-position: "npm:^5.0.0" + zwitch: "npm:^2.0.0" + checksum: 10c0/8e86c075319082c8a6304c5bcdf24ec02466074571e993f58bfa2cfd70850ef46d33b5c402208597a87fe0f02f1e620bda5958217efb1b7396c81c486373b75f + languageName: node + linkType: hard + "hast-util-to-html@npm:^7.1.1, hast-util-to-html@npm:^7.1.2": version: 7.1.3 resolution: "hast-util-to-html@npm:7.1.3" @@ -13913,6 +11866,48 @@ __metadata: languageName: node linkType: hard +"hast-util-to-html@npm:^9.0.5": + version: 9.0.5 + resolution: "hast-util-to-html@npm:9.0.5" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + ccount: "npm:^2.0.0" + comma-separated-tokens: "npm:^2.0.0" + hast-util-whitespace: "npm:^3.0.0" + html-void-elements: "npm:^3.0.0" + mdast-util-to-hast: "npm:^13.0.0" + property-information: "npm:^7.0.0" + space-separated-tokens: "npm:^2.0.0" + stringify-entities: "npm:^4.0.0" + zwitch: "npm:^2.0.4" + checksum: 10c0/b7a08c30bab4371fc9b4a620965c40b270e5ae7a8e94cf885f43b21705179e28c8e43b39c72885d1647965fb3738654e6962eb8b58b0c2a84271655b4d748836 + languageName: node + linkType: hard + +"hast-util-to-jsx-runtime@npm:^2.0.0, hast-util-to-jsx-runtime@npm:^2.3.6": + version: 2.3.6 + resolution: "hast-util-to-jsx-runtime@npm:2.3.6" + dependencies: + "@types/estree": "npm:^1.0.0" + "@types/hast": "npm:^3.0.0" + "@types/unist": "npm:^3.0.0" + comma-separated-tokens: "npm:^2.0.0" + devlop: "npm:^1.0.0" + estree-util-is-identifier-name: "npm:^3.0.0" + hast-util-whitespace: "npm:^3.0.0" + mdast-util-mdx-expression: "npm:^2.0.0" + mdast-util-mdx-jsx: "npm:^3.0.0" + mdast-util-mdxjs-esm: "npm:^2.0.0" + property-information: "npm:^7.0.0" + space-separated-tokens: "npm:^2.0.0" + style-to-js: "npm:^1.0.0" + unist-util-position: "npm:^5.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/27297e02848fe37ef219be04a26ce708d17278a175a807689e94a821dcffc88aa506d62c3a85beed1f9a8544f7211bdcbcde0528b7b456a57c2e342c3fd11056 + languageName: node + linkType: hard + "hast-util-to-parse5@npm:^5.0.0": version: 5.1.2 resolution: "hast-util-to-parse5@npm:5.1.2" @@ -13926,16 +11921,18 @@ __metadata: languageName: node linkType: hard -"hast-util-to-parse5@npm:^6.0.0": - version: 6.0.0 - resolution: "hast-util-to-parse5@npm:6.0.0" +"hast-util-to-parse5@npm:^8.0.0": + version: 8.0.1 + resolution: "hast-util-to-parse5@npm:8.0.1" dependencies: - hast-to-hyperscript: "npm:^9.0.0" - property-information: "npm:^5.0.0" - web-namespaces: "npm:^1.0.0" - xtend: "npm:^4.0.0" - zwitch: "npm:^1.0.0" - checksum: 10c0/49d6c2389fd3170741cdb0483666bccd7e9e436fe386bcbd3931b019e4c006b5bb48022e07967e1021336e744e901082d6479cfa4bc2082efa3b1e5bdab2a36f + "@types/hast": "npm:^3.0.0" + comma-separated-tokens: "npm:^2.0.0" + devlop: "npm:^1.0.0" + property-information: "npm:^7.0.0" + space-separated-tokens: "npm:^2.0.0" + web-namespaces: "npm:^2.0.0" + zwitch: "npm:^2.0.0" + checksum: 10c0/8e8a1817c7ff8906ac66e7201b1b8d19d9e1b705e695a6e71620270d498d982ec1ecc0e227bd517f723e91e7fdfb90ef75f9ae64d14b3b65239a7d5e1194d7dd languageName: node linkType: hard @@ -13946,6 +11943,15 @@ __metadata: languageName: node linkType: hard +"hast-util-to-string@npm:^3.0.1": + version: 3.0.1 + resolution: "hast-util-to-string@npm:3.0.1" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/b5fa1912a6ba6131affae52a0f4394406c4c0d23c2b0307f1d69988f1030c7bb830289303e67c5ad8f674f5f23a454c1dcd492c39e45a22c1f46d3c9bce5bd0c + languageName: node + linkType: hard + "hast-util-whitespace@npm:^1.0.0": version: 1.0.4 resolution: "hast-util-whitespace@npm:1.0.4" @@ -13953,6 +11959,15 @@ __metadata: languageName: node linkType: hard +"hast-util-whitespace@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-whitespace@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + checksum: 10c0/b898bc9fe27884b272580d15260b6bbdabe239973a147e97fa98c45fa0ffec967a481aaa42291ec34fb56530dc2d484d473d7e2bae79f39c83f3762307edfea8 + languageName: node + linkType: hard + "hastscript@npm:^5.0.0": version: 5.1.2 resolution: "hastscript@npm:5.1.2" @@ -13978,12 +11993,16 @@ __metadata: languageName: node linkType: hard -"he@npm:^1.2.0": - version: 1.2.0 - resolution: "he@npm:1.2.0" - bin: - he: bin/he - checksum: 10c0/a27d478befe3c8192f006cdd0639a66798979dfa6e2125c6ac582a19a5ebfec62ad83e8382e6036170d873f46e4536a7e795bf8b95bf7c247f4cc0825ccc8c17 +"hastscript@npm:^9.0.0": + version: 9.0.1 + resolution: "hastscript@npm:9.0.1" + dependencies: + "@types/hast": "npm:^3.0.0" + comma-separated-tokens: "npm:^2.0.0" + hast-util-parse-selector: "npm:^4.0.0" + property-information: "npm:^7.0.0" + space-separated-tokens: "npm:^2.0.0" + checksum: 10c0/18dc8064e5c3a7a2ae862978e626b97a254e1c8a67ee9d0c9f06d373bba155ed805fc5b5ce21b990fb7bc174624889e5e1ce1cade264f1b1d58b48f994bc85ce languageName: node linkType: hard @@ -14052,20 +12071,6 @@ __metadata: languageName: node linkType: hard -"history@npm:^4.9.0": - version: 4.10.1 - resolution: "history@npm:4.10.1" - dependencies: - "@babel/runtime": "npm:^7.1.2" - loose-envify: "npm:^1.2.0" - resolve-pathname: "npm:^3.0.0" - tiny-invariant: "npm:^1.0.2" - tiny-warning: "npm:^1.0.0" - value-equal: "npm:^1.0.1" - checksum: 10c0/35377694e4f10f2cf056a9cb1a8ee083e04e4b4717a63baeee4afd565658a62c7e73700bf9e82aa53dbe1ec94e0a25a83c080d63bad8ee6b274a98d2fbc5ed4c - languageName: node - linkType: hard - "history@npm:^5.0.0": version: 5.3.0 resolution: "history@npm:5.3.0" @@ -14075,7 +12080,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.1.0, hoist-non-react-statics@npm:^3.3.0": +"hoist-non-react-statics@npm:^3.3.0": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -14084,6 +12089,13 @@ __metadata: languageName: node linkType: hard +"hookable@npm:^6.0.1": + version: 6.1.1 + resolution: "hookable@npm:6.1.1" + checksum: 10c0/bb46cd9ffc0a997af21febd97835da4e59a6989adec73dc3c215fcc44c7ac01de4781f251c3d420bf45862d2592a695f5f0de095b6f5df52db8afb5166938212 + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -14109,56 +12121,20 @@ __metadata: languageName: node linkType: hard -"hpack.js@npm:^2.1.6": - version: 2.1.6 - resolution: "hpack.js@npm:2.1.6" - dependencies: - inherits: "npm:^2.0.1" - obuf: "npm:^1.0.0" - readable-stream: "npm:^2.0.1" - wbuf: "npm:^1.1.0" - checksum: 10c0/55b9e824430bab82a19d079cb6e33042d7d0640325678c9917fcc020c61d8a08ca671b6c942c7f0aae9bb6e4b67ffb50734a72f9e21d66407c3138c1983b70f0 - languageName: node - linkType: hard - -"html-entities@npm:^2.1.0, html-entities@npm:^2.3.2": +"html-entities@npm:^2.1.0": version: 2.6.0 resolution: "html-entities@npm:2.6.0" checksum: 10c0/7c8b15d9ea0cd00dc9279f61bab002ba6ca8a7a0f3c36ed2db3530a67a9621c017830d1d2c1c65beb9b8e3436ea663e9cf8b230472e0e413359399413b27c8b7 languageName: node linkType: hard -"html-escaper@npm:^2.0.0, html-escaper@npm:^2.0.2": +"html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0 languageName: node linkType: hard -"html-minifier-terser@npm:^6.0.2, html-minifier-terser@npm:^6.1.0": - version: 6.1.0 - resolution: "html-minifier-terser@npm:6.1.0" - dependencies: - camel-case: "npm:^4.1.2" - clean-css: "npm:^5.2.2" - commander: "npm:^8.3.0" - he: "npm:^1.2.0" - param-case: "npm:^3.0.4" - relateurl: "npm:^0.2.7" - terser: "npm:^5.10.0" - bin: - html-minifier-terser: cli.js - checksum: 10c0/1aa4e4f01cf7149e3ac5ea84fb7a1adab86da40d38d77a6fff42852b5ee3daccb78b615df97264e3a6a5c33e57f0c77f471d607ca1e1debd1dab9b58286f4b5a - languageName: node - linkType: hard - -"html-tags@npm:^3.2.0": - version: 3.3.1 - resolution: "html-tags@npm:3.3.1" - checksum: 10c0/680165e12baa51bad7397452d247dbcc5a5c29dac0e6754b1187eee3bf26f514bc1907a431dd2f7eb56207611ae595ee76a0acc8eaa0d931e72c791dd6463d79 - languageName: node - linkType: hard - "html-void-elements@npm:^1.0.0": version: 1.0.5 resolution: "html-void-elements@npm:1.0.5" @@ -14166,36 +12142,10 @@ __metadata: languageName: node linkType: hard -"html-webpack-plugin@npm:^5.5.0": - version: 5.6.5 - resolution: "html-webpack-plugin@npm:5.6.5" - dependencies: - "@types/html-minifier-terser": "npm:^6.0.0" - html-minifier-terser: "npm:^6.0.2" - lodash: "npm:^4.17.21" - pretty-error: "npm:^4.0.0" - tapable: "npm:^2.0.0" - peerDependencies: - "@rspack/core": 0.x || 1.x - webpack: ^5.20.0 - peerDependenciesMeta: - "@rspack/core": - optional: true - webpack: - optional: true - checksum: 10c0/4ae0ae48fec6337e4eb055e730e46340172ec1967bd383d897d03cb3c4e385a8128e8d5179c4658536b00e432c2d3f026d97eb5fdb4cf9dc710498d2e871b84e - languageName: node - linkType: hard - -"htmlparser2@npm:^10.0.0": - version: 10.0.0 - resolution: "htmlparser2@npm:10.0.0" - dependencies: - domelementtype: "npm:^2.3.0" - domhandler: "npm:^5.0.3" - domutils: "npm:^3.2.1" - entities: "npm:^6.0.0" - checksum: 10c0/47cfa37e529c86a7ba9a1e0e6f951ad26ef8ca5af898ab6e8916fa02c0264c1453b4a65f28b7b8a7f9d0d29b5a70abead8203bf8b3f07bc69407e85e7d9a68e4 +"html-void-elements@npm:^3.0.0": + version: 3.0.0 + resolution: "html-void-elements@npm:3.0.0" + checksum: 10c0/a8b9ec5db23b7c8053876dad73a0336183e6162bf6d2677376d8b38d654fdc59ba74fdd12f8812688f7db6fad451210c91b300e472afc0909224e0a44c8610d2 languageName: node linkType: hard @@ -14211,18 +12161,6 @@ __metadata: languageName: node linkType: hard -"htmlparser2@npm:^6.1.0": - version: 6.1.0 - resolution: "htmlparser2@npm:6.1.0" - dependencies: - domelementtype: "npm:^2.0.1" - domhandler: "npm:^4.0.0" - domutils: "npm:^2.5.2" - entities: "npm:^2.0.0" - checksum: 10c0/3058499c95634f04dc66be8c2e0927cd86799413b2d6989d8ae542ca4dbf5fa948695d02c27d573acf44843af977aec6d9a7bdd0f6faa6b2d99e2a729b2a31b6 - languageName: node - linkType: hard - "http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.1": version: 4.2.0 resolution: "http-cache-semantics@npm:4.2.0" @@ -14230,25 +12168,6 @@ __metadata: languageName: node linkType: hard -"http-deceiver@npm:^1.2.7": - version: 1.2.7 - resolution: "http-deceiver@npm:1.2.7" - checksum: 10c0/8bb9b716f5fc55f54a451da7f49b9c695c3e45498a789634daec26b61e4add7c85613a4a9e53726c39d09de7a163891ecd6eb5809adb64500a840fd86fe81d03 - languageName: node - linkType: hard - -"http-errors@npm:~1.6.2": - version: 1.6.3 - resolution: "http-errors@npm:1.6.3" - dependencies: - depd: "npm:~1.1.2" - inherits: "npm:2.0.3" - setprototypeof: "npm:1.1.0" - statuses: "npm:>= 1.4.0 < 2" - checksum: 10c0/17ec4046ee974477778bfdd525936c254b872054703ec2caa4d6f099566b8adade636ae6aeeacb39302c5cd6e28fb407ebd937f500f5010d0b6850750414ff78 - languageName: node - linkType: hard - "http-errors@npm:~2.0.0, http-errors@npm:~2.0.1": version: 2.0.1 resolution: "http-errors@npm:2.0.1" @@ -14262,13 +12181,6 @@ __metadata: languageName: node linkType: hard -"http-parser-js@npm:>=0.5.1": - version: 0.5.10 - resolution: "http-parser-js@npm:0.5.10" - checksum: 10c0/8bbcf1832a8d70b2bd515270112116333add88738a2cc05bfb94ba6bde3be4b33efee5611584113818d2bcf654fdc335b652503be5a6b4c0b95e46f214187d93 - languageName: node - linkType: hard - "http-proxy-agent@npm:^7.0.0": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" @@ -14279,35 +12191,6 @@ __metadata: languageName: node linkType: hard -"http-proxy-middleware@npm:^2.0.3": - version: 2.0.9 - resolution: "http-proxy-middleware@npm:2.0.9" - dependencies: - "@types/http-proxy": "npm:^1.17.8" - http-proxy: "npm:^1.18.1" - is-glob: "npm:^4.0.1" - is-plain-obj: "npm:^3.0.0" - micromatch: "npm:^4.0.2" - peerDependencies: - "@types/express": ^4.17.13 - peerDependenciesMeta: - "@types/express": - optional: true - checksum: 10c0/8e9032af625f7c9f2f0d318f6cdb14eb725cc16ffe7b4ccccea25cf591fa819bb7c3bb579e0b543e0ae9c73059b505a6d728290c757bff27bae526a6ed11c05e - languageName: node - linkType: hard - -"http-proxy@npm:^1.18.1": - version: 1.18.1 - resolution: "http-proxy@npm:1.18.1" - dependencies: - eventemitter3: "npm:^4.0.0" - follow-redirects: "npm:^1.0.0" - requires-port: "npm:^1.0.0" - checksum: 10c0/148dfa700a03fb421e383aaaf88ac1d94521dfc34072f6c59770528c65250983c2e4ec996f2f03aa9f3fe46cd1270a593126068319311e3e8d9e610a37533e94 - languageName: node - linkType: hard - "http2-wrapper@npm:^1.0.0-beta.5.2": version: 1.0.3 resolution: "http2-wrapper@npm:1.0.3" @@ -14349,21 +12232,21 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": - version: 0.6.3 - resolution: "iconv-lite@npm:0.6.3" +"iconv-lite@npm:^0.4.24, iconv-lite@npm:~0.4.24": + version: 0.4.24 + resolution: "iconv-lite@npm:0.4.24" dependencies: - safer-buffer: "npm:>= 2.1.2 < 3.0.0" - checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 + safer-buffer: "npm:>= 2.1.2 < 3" + checksum: 10c0/c6886a24cc00f2a059767440ec1bc00d334a89f250db8e0f7feb4961c8727118457e27c495ba94d082e51d3baca378726cd110aaf7ded8b9bbfd6a44760cf1d4 languageName: node linkType: hard -"iconv-lite@npm:^0.4.24, iconv-lite@npm:~0.4.24": - version: 0.4.24 - resolution: "iconv-lite@npm:0.4.24" +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" dependencies: - safer-buffer: "npm:>= 2.1.2 < 3" - checksum: 10c0/c6886a24cc00f2a059767440ec1bc00d334a89f250db8e0f7feb4961c8727118457e27c495ba94d082e51d3baca378726cd110aaf7ded8b9bbfd6a44760cf1d4 + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 languageName: node linkType: hard @@ -14390,7 +12273,7 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.1.4, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": +"ignore@npm:^5.1.4, ignore@npm:^5.2.0, ignore@npm:^5.3.1": version: 5.3.2 resolution: "ignore@npm:5.3.2" checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 @@ -14404,7 +12287,7 @@ __metadata: languageName: node linkType: hard -"image-size@npm:^1.0.1, image-size@npm:^1.0.2": +"image-size@npm:^1.0.2": version: 1.2.1 resolution: "image-size@npm:1.2.1" dependencies: @@ -14415,20 +12298,6 @@ __metadata: languageName: node linkType: hard -"immediate@npm:^3.2.3": - version: 3.3.0 - resolution: "immediate@npm:3.3.0" - checksum: 10c0/40eab095d5944ad79af054700beee97000271fde8743720932d8eb41ccbf2cb8c855ff95b128cf9a7fec523a4f11ee2e392b9f2fa6456b055b1160f1b4ad3e3b - languageName: node - linkType: hard - -"immer@npm:^9.0.7": - version: 9.0.21 - resolution: "immer@npm:9.0.21" - checksum: 10c0/03ea3ed5d4d72e8bd428df4a38ad7e483ea8308e9a113d3b42e0ea2cc0cc38340eb0a6aca69592abbbf047c685dbda04e3d34bf2ff438ab57339ed0a34cc0a05 - languageName: node - linkType: hard - "import-cwd@npm:3.0.0": version: 3.0.0 resolution: "import-cwd@npm:3.0.0" @@ -14448,7 +12317,7 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": +"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1": version: 3.3.1 resolution: "import-fresh@npm:3.3.1" dependencies: @@ -14507,13 +12376,6 @@ __metadata: languageName: node linkType: hard -"infima@npm:0.2.0-alpha.43": - version: 0.2.0-alpha.43 - resolution: "infima@npm:0.2.0-alpha.43" - checksum: 10c0/d248958713a97e1c9f73ace27ceff726ba86a9b534efb0ebdec3e72b785d8edb36db922e38ce09bbeb98a17b657e61357f22edc3a58f02ad51b7ae2ebd96e4e4 - languageName: node - linkType: hard - "inflight@npm:^1.0.4": version: 1.0.6 resolution: "inflight@npm:1.0.6" @@ -14524,20 +12386,13 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.0, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4": +"inherits@npm:2, inherits@npm:^2.0.0, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 languageName: node linkType: hard -"inherits@npm:2.0.3": - version: 2.0.3 - resolution: "inherits@npm:2.0.3" - checksum: 10c0/6e56402373149ea076a434072671f9982f5fad030c7662be0332122fe6c0fa490acb3cc1010d90b6eff8d640b1167d77674add52dfd1bb85d545cf29e80e73e7 - languageName: node - linkType: hard - "ini@npm:1.3.7": version: 1.3.7 resolution: "ini@npm:1.3.7" @@ -14545,14 +12400,7 @@ __metadata: languageName: node linkType: hard -"ini@npm:2.0.0": - version: 2.0.0 - resolution: "ini@npm:2.0.0" - checksum: 10c0/2e0c8f386369139029da87819438b20a1ff3fe58372d93fb1a86e9d9344125ace3a806b8ec4eb160a46e64cbc422fe68251869441676af49b7fc441af2389c25 - languageName: node - linkType: hard - -"ini@npm:^1.3.2, ini@npm:^1.3.4, ini@npm:^1.3.5, ini@npm:~1.3.0": +"ini@npm:^1.3.2, ini@npm:^1.3.4, ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a @@ -14566,13 +12414,10 @@ __metadata: languageName: node linkType: hard -"inline-style-prefixer@npm:^6.0.1": - version: 6.0.4 - resolution: "inline-style-prefixer@npm:6.0.4" - dependencies: - css-in-js-utils: "npm:^3.1.0" - fast-loops: "npm:^1.1.3" - checksum: 10c0/d3d42bf0c48d621ea4bcfb077b5d370b106995422300a3a472674f96c9b489d96b4aac6f29dea3bb26ff2dfd7293e4752098bc2b53407769eafdb66c6c4c1764 +"inline-style-parser@npm:0.2.7": + version: 0.2.7 + resolution: "inline-style-parser@npm:0.2.7" + checksum: 10c0/d884d76f84959517430ae6c22f0bda59bb3f58f539f99aac75a8d786199ec594ed648c6ab4640531f9fc244b0ed5cd8c458078e592d016ef06de793beb1debff languageName: node linkType: hard @@ -14647,10 +12492,10 @@ __metadata: languageName: node linkType: hard -"ipaddr.js@npm:^2.0.1": - version: 2.3.0 - resolution: "ipaddr.js@npm:2.3.0" - checksum: 10c0/084bab99e2f6875d7a62adc3325e1c64b038a12c9521e35fb967b5e263a8b3afb1b8884dd77c276092331f5d63298b767491e10997ef147c62da01b143780bbd +"is-absolute-url@npm:^4.0.0": + version: 4.0.1 + resolution: "is-absolute-url@npm:4.0.1" + checksum: 10c0/6f8f603945bd9f2c6031758bbc12352fc647bd5d807cad10d96cc6300fd0e15240cc091521a61db767e4ec0bacff257b4f1015fd5249c147bbb4a4497356c72e languageName: node linkType: hard @@ -14663,13 +12508,20 @@ __metadata: languageName: node linkType: hard -"is-alphabetical@npm:1.0.4, is-alphabetical@npm:^1.0.0": +"is-alphabetical@npm:^1.0.0": version: 1.0.4 resolution: "is-alphabetical@npm:1.0.4" checksum: 10c0/1505b1de5a1fd74022c05fb21b0e683a8f5229366bac8dc4d34cf6935bcfd104d1125a5e6b083fb778847629f76e5bdac538de5367bdf2b927a1356164e23985 languageName: node linkType: hard +"is-alphabetical@npm:^2.0.0": + version: 2.0.1 + resolution: "is-alphabetical@npm:2.0.1" + checksum: 10c0/932367456f17237533fd1fc9fe179df77957271020b83ea31da50e5cc472d35ef6b5fb8147453274ffd251134472ce24eb6f8d8398d96dee98237cdb81a6c9a7 + languageName: node + linkType: hard + "is-alphanumerical@npm:^1.0.0": version: 1.0.4 resolution: "is-alphanumerical@npm:1.0.4" @@ -14680,6 +12532,16 @@ __metadata: languageName: node linkType: hard +"is-alphanumerical@npm:^2.0.0": + version: 2.0.1 + resolution: "is-alphanumerical@npm:2.0.1" + dependencies: + is-alphabetical: "npm:^2.0.0" + is-decimal: "npm:^2.0.0" + checksum: 10c0/4b35c42b18e40d41378293f82a3ecd9de77049b476f748db5697c297f686e1e05b072a6aaae2d16f54d2a57f85b00cbbe755c75f6d583d1c77d6657bd0feb5a2 + languageName: node + linkType: hard + "is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": version: 3.0.5 resolution: "is-array-buffer@npm:3.0.5" @@ -14727,15 +12589,6 @@ __metadata: languageName: node linkType: hard -"is-binary-path@npm:~2.1.0": - version: 2.1.0 - resolution: "is-binary-path@npm:2.1.0" - dependencies: - binary-extensions: "npm:^2.0.0" - checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 - languageName: node - linkType: hard - "is-boolean-object@npm:^1.2.1": version: 1.2.2 resolution: "is-boolean-object@npm:1.2.2" @@ -14824,6 +12677,13 @@ __metadata: languageName: node linkType: hard +"is-decimal@npm:^2.0.0": + version: 2.0.1 + resolution: "is-decimal@npm:2.0.1" + checksum: 10c0/8085dd66f7d82f9de818fba48b9e9c0429cb4291824e6c5f2622e96b9680b54a07a624cfc663b24148b8e853c62a1c987cfe8b0b5a13f5156991afaf6736e334 + languageName: node + linkType: hard + "is-descriptor@npm:^0.1.0": version: 0.1.7 resolution: "is-descriptor@npm:0.1.7" @@ -14851,7 +12711,7 @@ __metadata: languageName: node linkType: hard -"is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": +"is-docker@npm:^2.0.0": version: 2.2.1 resolution: "is-docker@npm:2.2.1" bin: @@ -14926,7 +12786,7 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": version: 4.0.3 resolution: "is-glob@npm:4.0.3" dependencies: @@ -14942,6 +12802,13 @@ __metadata: languageName: node linkType: hard +"is-hexadecimal@npm:^2.0.0": + version: 2.0.1 + resolution: "is-hexadecimal@npm:2.0.1" + checksum: 10c0/3eb60fe2f1e2bbc760b927dcad4d51eaa0c60138cf7fc671803f66353ad90c301605b502c7ea4c6bb0548e1c7e79dfd37b73b632652e3b76030bba603a7e9626 + languageName: node + linkType: hard + "is-installed-globally@npm:^0.3.1": version: 0.3.2 resolution: "is-installed-globally@npm:0.3.2" @@ -14952,16 +12819,6 @@ __metadata: languageName: node linkType: hard -"is-installed-globally@npm:^0.4.0": - version: 0.4.0 - resolution: "is-installed-globally@npm:0.4.0" - dependencies: - global-dirs: "npm:^3.0.0" - is-path-inside: "npm:^3.0.2" - checksum: 10c0/f3e6220ee5824b845c9ed0d4b42c24272701f1f9926936e30c0e676254ca5b34d1b92c6205cae11b283776f9529212c0cdabb20ec280a6451677d6493ca9c22d - languageName: node - linkType: hard - "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -14999,13 +12856,6 @@ __metadata: languageName: node linkType: hard -"is-npm@npm:^5.0.0": - version: 5.0.0 - resolution: "is-npm@npm:5.0.0" - checksum: 10c0/8ded3ae1119bbbda22395fe1c64d2d79d3b3baeb2635c90f9a9dca4b8ce19a67b55fda178269b63421b257b361892fd545807fb5ac212f06776f544d9fcc3ab0 - languageName: node - linkType: hard - "is-number-object@npm:^1.1.1": version: 1.1.1 resolution: "is-number-object@npm:1.1.1" @@ -15032,7 +12882,7 @@ __metadata: languageName: node linkType: hard -"is-obj@npm:^1.0.0, is-obj@npm:^1.0.1": +"is-obj@npm:^1.0.0": version: 1.0.1 resolution: "is-obj@npm:1.0.1" checksum: 10c0/5003acba0af7aa47dfe0760e545a89bbac89af37c12092c3efadc755372cdaec034f130e7a3653a59eb3c1843cfc72ca71eaf1a6c3bafe5a0bab3611a47f9945 @@ -15046,13 +12896,6 @@ __metadata: languageName: node linkType: hard -"is-path-cwd@npm:^2.2.0": - version: 2.2.0 - resolution: "is-path-cwd@npm:2.2.0" - checksum: 10c0/afce71533a427a759cd0329301c18950333d7589533c2c90205bd3fdcf7b91eb92d1940493190567a433134d2128ec9325de2fd281e05be1920fbee9edd22e0a - languageName: node - linkType: hard - "is-path-cwd@npm:^3.0.0": version: 3.0.0 resolution: "is-path-cwd@npm:3.0.0" @@ -15060,7 +12903,7 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.1, is-path-inside@npm:^3.0.2": +"is-path-inside@npm:^3.0.1": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 @@ -15088,10 +12931,10 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^3.0.0": - version: 3.0.0 - resolution: "is-plain-obj@npm:3.0.0" - checksum: 10c0/8e6483bfb051d42ec9c704c0ede051a821c6b6f9a6c7a3e3b55aa855e00981b0580c8f3b1f5e2e62649b39179b1abfee35d6f8086d999bfaa32c1908d29b07bc +"is-plain-obj@npm:^4.0.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e languageName: node linkType: hard @@ -15123,13 +12966,6 @@ __metadata: languageName: node linkType: hard -"is-regexp@npm:^1.0.0": - version: 1.0.0 - resolution: "is-regexp@npm:1.0.0" - checksum: 10c0/34cacda1901e00f6e44879378f1d2fa96320ea956c1bec27713130aaf1d44f6e7bd963eed28945bfe37e600cb27df1cf5207302680dad8bdd27b9baff8ecf611 - languageName: node - linkType: hard - "is-regular-file@npm:^1.0.1": version: 1.1.1 resolution: "is-regular-file@npm:1.1.1" @@ -15137,13 +12973,6 @@ __metadata: languageName: node linkType: hard -"is-root@npm:^2.1.0": - version: 2.1.0 - resolution: "is-root@npm:2.1.0" - checksum: 10c0/83d3f5b052c3f28fbdbdf0d564bdd34fa14933f5694c78704f85cd1871255bc017fbe3fe2bc2fff2d227c6be5927ad2149b135c0a7c0060e7ac4e610d81a4f01 - languageName: node - linkType: hard - "is-set@npm:^2.0.3": version: 2.0.3 resolution: "is-set@npm:2.0.3" @@ -15315,13 +13144,6 @@ __metadata: languageName: node linkType: hard -"isarray@npm:0.0.1": - version: 0.0.1 - resolution: "isarray@npm:0.0.1" - checksum: 10c0/ed1e62da617f71fe348907c71743b5ed550448b455f8d269f89a7c7ddb8ae6e962de3dab6a74a237b06f5eb7f6ece7a45ada8ce96d87fe972926530f91ae3311 - languageName: node - linkType: hard - "isarray@npm:1.0.0, isarray@npm:~1.0.0": version: 1.0.0 resolution: "isarray@npm:1.0.0" @@ -15926,7 +13748,7 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.1.2, jest-worker@npm:^29.7.0": +"jest-worker@npm:^29.7.0": version: 29.7.0 resolution: "jest-worker@npm:29.7.0" dependencies: @@ -15964,28 +13786,6 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^1.20.0": - version: 1.21.7 - resolution: "jiti@npm:1.21.7" - bin: - jiti: bin/jiti.js - checksum: 10c0/77b61989c758ff32407cdae8ddc77f85e18e1a13fc4977110dbd2e05fc761842f5f71bce684d9a01316e1c4263971315a111385759951080bbfe17cbb5de8f7a - languageName: node - linkType: hard - -"joi@npm:^17.6.0": - version: 17.13.3 - resolution: "joi@npm:17.13.3" - dependencies: - "@hapi/hoek": "npm:^9.3.0" - "@hapi/topo": "npm:^5.1.0" - "@sideway/address": "npm:^4.1.5" - "@sideway/formula": "npm:^3.0.1" - "@sideway/pinpoint": "npm:^2.0.0" - checksum: 10c0/9262aef1da3f1bec5b03caf50c46368899fe03b8ff26cbe3d53af4584dd1049079fc97230bbf1500b6149db7cc765b9ee45f0deb24bb6fc3fa06229d7148c17f - languageName: node - linkType: hard - "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -16210,22 +14010,13 @@ __metadata: languageName: node linkType: hard -"kind-of@npm:^6.0.0, kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": +"kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" checksum: 10c0/61cdff9623dabf3568b6445e93e31376bee1cdb93f8ba7033d86022c2a9b1791a1d9510e026e6465ebd701a6dd2f7b0808483ad8838341ac52f003f512e0b4c4 languageName: node linkType: hard -"klaw-sync@npm:^6.0.0": - version: 6.0.0 - resolution: "klaw-sync@npm:6.0.0" - dependencies: - graceful-fs: "npm:^4.1.11" - checksum: 10c0/00d8e4c48d0d699b743b3b028e807295ea0b225caf6179f51029e19783a93ad8bb9bccde617d169659fbe99559d73fb35f796214de031d0023c26b906cecd70a - languageName: node - linkType: hard - "kleur@npm:^3.0.3": version: 3.0.3 resolution: "kleur@npm:3.0.3" @@ -16249,7 +14040,7 @@ __metadata: languageName: node linkType: hard -"latest-version@npm:^5.0.0, latest-version@npm:^5.1.0": +"latest-version@npm:^5.0.0": version: 5.1.0 resolution: "latest-version@npm:5.1.0" dependencies: @@ -16258,16 +14049,6 @@ __metadata: languageName: node linkType: hard -"launch-editor@npm:^2.6.0": - version: 2.12.0 - resolution: "launch-editor@npm:2.12.0" - dependencies: - picocolors: "npm:^1.1.1" - shell-quote: "npm:^1.8.3" - checksum: 10c0/fac5e7ad90bf185594cad4c831a52419eef50e667c4eddb5b0a58eb5f944e16d947636ee767b9896ffd46a51db34925edd3b854c48efb47f6d767ffd7d904e71 - languageName: node - linkType: hard - "lefthook-darwin-arm64@npm:2.1.9": version: 2.1.9 resolution: "lefthook-darwin-arm64@npm:2.1.9" @@ -16526,13 +14307,6 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:^2.0.3": - version: 2.1.0 - resolution: "lilconfig@npm:2.1.0" - checksum: 10c0/64645641aa8d274c99338e130554abd6a0190533c0d9eb2ce7ebfaf2e05c7d9961f3ffe2bfa39efd3b60c521ba3dd24fa236fe2775fc38501bf82bf49d4678b8 - languageName: node - linkType: hard - "linaria@npm:^1.3.3": version: 1.4.1 resolution: "linaria@npm:1.4.1" @@ -16618,13 +14392,6 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:^3.2.0": - version: 3.3.1 - resolution: "loader-utils@npm:3.3.1" - checksum: 10c0/f2af4eb185ac5bf7e56e1337b666f90744e9f443861ac521b48f093fb9e8347f191c8960b4388a3365147d218913bc23421234e7788db69f385bacfefa0b4758 - languageName: node - linkType: hard - "locate-path@npm:^2.0.0": version: 2.0.0 resolution: "locate-path@npm:2.0.0" @@ -16679,13 +14446,6 @@ __metadata: languageName: node linkType: hard -"lodash.curry@npm:^4.0.1": - version: 4.1.1 - resolution: "lodash.curry@npm:4.1.1" - checksum: 10c0/f0431947dc9236df879fc13eb40c31a2839c958bd0eaa39170a5758c25a7d85d461716a851ab45a175371950b283480615cdd4b07fb0dd1afff7a2914a90696f - languageName: node - linkType: hard - "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -16700,13 +14460,6 @@ __metadata: languageName: node linkType: hard -"lodash.flow@npm:^3.3.0": - version: 3.5.0 - resolution: "lodash.flow@npm:3.5.0" - checksum: 10c0/b3202ddbb79e5aab41719806d0d5ae969f64ae6b59e6bdaaecaa96ec68d6ba429e544017fe0e71ecf5b7ee3cea7b45d43c46b7d67ca159d6cca86fca76c61a31 - languageName: node - linkType: hard - "lodash.ismatch@npm:^4.4.0": version: 4.4.0 resolution: "lodash.ismatch@npm:4.4.0" @@ -16714,13 +14467,6 @@ __metadata: languageName: node linkType: hard -"lodash.memoize@npm:^4.1.2": - version: 4.1.2 - resolution: "lodash.memoize@npm:4.1.2" - checksum: 10c0/c8713e51eccc650422716a14cece1809cfe34bc5ab5e242b7f8b4e2241c2483697b971a604252807689b9dd69bfe3a98852e19a5b89d506b000b4187a1285df8 - languageName: node - linkType: hard - "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -16735,13 +14481,6 @@ __metadata: languageName: node linkType: hard -"lodash.uniq@npm:4.5.0, lodash.uniq@npm:^4.5.0": - version: 4.5.0 - resolution: "lodash.uniq@npm:4.5.0" - checksum: 10c0/262d400bb0952f112162a320cc4a75dea4f66078b9e7e3075ffbc9c6aa30b3e9df3cf20e7da7d566105e1ccf7804e4fbd7d804eee0b53de05d83f16ffbf41c5e - languageName: node - linkType: hard - "lodash@npm:4.17.21, lodash@npm:^4.0.1, lodash@npm:^4.11.2, lodash@npm:^4.17.11, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.5, lodash@npm:^4.2.1": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -16768,7 +14507,14 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.2.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0": +"longest-streak@npm:^3.0.0": + version: 3.1.0 + resolution: "longest-streak@npm:3.1.0" + checksum: 10c0/7c2f02d0454b52834d1bcedef79c557bd295ee71fdabb02d041ff3aa9da48a90b5df7c0409156dedbc4df9b65da18742652aaea4759d6ece01f08971af6a7eaa + languageName: node + linkType: hard + +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -16805,15 +14551,6 @@ __metadata: languageName: node linkType: hard -"lower-case@npm:^2.0.2": - version: 2.0.2 - resolution: "lower-case@npm:2.0.2" - dependencies: - tslib: "npm:^2.0.3" - checksum: 10c0/3d925e090315cf7dc1caa358e0477e186ffa23947740e4314a7429b6e62d72742e0bbe7536a5ae56d19d7618ce998aba05caca53c2902bd5742fdca5fc57fd7b - languageName: node - linkType: hard - "lowercase-keys@npm:^1.0.0, lowercase-keys@npm:^1.0.1": version: 1.0.1 resolution: "lowercase-keys@npm:1.0.1" @@ -16860,20 +14597,6 @@ __metadata: languageName: node linkType: hard -"lunr-languages@npm:^1.4.0": - version: 1.14.0 - resolution: "lunr-languages@npm:1.14.0" - checksum: 10c0/5dc26fa75c8f3f14a69b3d54ae1228907b3552bc26727a14c5f302aab05d2547a924d095f075c9d3439756a38e2dafb78d1b74fc862dc290a13ddce236a55e87 - languageName: node - linkType: hard - -"lunr@npm:^2.3.9": - version: 2.3.9 - resolution: "lunr@npm:2.3.9" - checksum: 10c0/77d7dbb4fbd602aac161e2b50887d8eda28c0fa3b799159cee380fbb311f1e614219126ecbbd2c3a9c685f1720a8109b3c1ca85cc893c39b6c9cc6a62a1d8a8b - languageName: node - linkType: hard - "macos-release@npm:^2.2.0": version: 2.5.1 resolution: "macos-release@npm:2.5.1" @@ -16983,13 +14706,6 @@ __metadata: languageName: node linkType: hard -"mark.js@npm:^8.11.1": - version: 8.11.1 - resolution: "mark.js@npm:8.11.1" - checksum: 10c0/5e69e776db61abdd857b5cbb7070c8a3b1b0e5c12bf077fcd5a8c6f17b1f85ed65275aba5662b57136d1b9f82b54bb34d4ef4220f7703c9a7ab806ae1e208cff - languageName: node - linkType: hard - "markdown-escapes@npm:^1.0.0": version: 1.0.4 resolution: "markdown-escapes@npm:1.0.4" @@ -16997,6 +14713,20 @@ __metadata: languageName: node linkType: hard +"markdown-extensions@npm:^2.0.0": + version: 2.0.0 + resolution: "markdown-extensions@npm:2.0.0" + checksum: 10c0/406139da2aa0d5ebad86195c8e8c02412f873c452b4c087ae7bc767af37956141be449998223bb379eea179b5fd38dfa610602b6f29c22ddab5d51e627a7e41d + languageName: node + linkType: hard + +"markdown-table@npm:^3.0.0": + version: 3.0.4 + resolution: "markdown-table@npm:3.0.4" + checksum: 10c0/1257b31827629a54c24a5030a3dac952256c559174c95ce3ef89bebd6bff0cb1444b1fd667b1a1bb53307f83278111505b3e26f0c4e7b731e0060d435d2d930b + languageName: node + linkType: hard + "marked@npm:^1.2.2": version: 1.2.9 resolution: "marked@npm:1.2.9" @@ -17045,15 +14775,6 @@ __metadata: languageName: node linkType: hard -"mdast-squeeze-paragraphs@npm:^4.0.0": - version: 4.0.0 - resolution: "mdast-squeeze-paragraphs@npm:4.0.0" - dependencies: - unist-util-remove: "npm:^2.0.0" - checksum: 10c0/0b44a85d7e6d98772b1dbb28a46a35c74c2791c6cf057bfd2e590a4e011d626627e5bf82d4497706f0dae03da02a63a9279aca17c4c23a9c7173792adba8e6fc - languageName: node - linkType: hard - "mdast-util-definitions@npm:^1.2.0": version: 1.2.5 resolution: "mdast-util-definitions@npm:1.2.5" @@ -17063,28 +14784,200 @@ __metadata: languageName: node linkType: hard -"mdast-util-definitions@npm:^4.0.0": - version: 4.0.0 - resolution: "mdast-util-definitions@npm:4.0.0" +"mdast-util-find-and-replace@npm:^3.0.0": + version: 3.0.2 + resolution: "mdast-util-find-and-replace@npm:3.0.2" + dependencies: + "@types/mdast": "npm:^4.0.0" + escape-string-regexp: "npm:^5.0.0" + unist-util-is: "npm:^6.0.0" + unist-util-visit-parents: "npm:^6.0.0" + checksum: 10c0/c8417a35605d567772ff5c1aa08363ff3010b0d60c8ea68c53cba09bf25492e3dd261560425c1756535f3b7107f62e7ff3857cdd8fb1e62d1b2cc2ea6e074ca2 + languageName: node + linkType: hard + +"mdast-util-from-markdown@npm:^2.0.0": + version: 2.0.3 + resolution: "mdast-util-from-markdown@npm:2.0.3" dependencies: - unist-util-visit: "npm:^2.0.0" - checksum: 10c0/d81bb0b702f99878c8e8e4f66dd7f6f673ab341f061b3d9487ba47dad28b584e02f16b4c42df23714eaac8a7dd8544ba7d77308fad8d4a9fd0ac92e2a7f56be9 + "@types/mdast": "npm:^4.0.0" + "@types/unist": "npm:^3.0.0" + decode-named-character-reference: "npm:^1.0.0" + devlop: "npm:^1.0.0" + mdast-util-to-string: "npm:^4.0.0" + micromark: "npm:^4.0.0" + micromark-util-decode-numeric-character-reference: "npm:^2.0.0" + micromark-util-decode-string: "npm:^2.0.0" + micromark-util-normalize-identifier: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + unist-util-stringify-position: "npm:^4.0.0" + checksum: 10c0/d3eac9ac2b88e3b41fb85aa81c7bfd1f4f8a2fde497ad805e66fea7b2abfe486ffd94d2a20f9fd2951dcdebe4916f3bdcf851319891dd62d343e26c2f02583ba languageName: node linkType: hard -"mdast-util-to-hast@npm:10.0.1": - version: 10.0.1 - resolution: "mdast-util-to-hast@npm:10.0.1" +"mdast-util-gfm-autolink-literal@npm:^2.0.0": + version: 2.0.1 + resolution: "mdast-util-gfm-autolink-literal@npm:2.0.1" dependencies: - "@types/mdast": "npm:^3.0.0" - "@types/unist": "npm:^2.0.0" - mdast-util-definitions: "npm:^4.0.0" - mdurl: "npm:^1.0.0" - unist-builder: "npm:^2.0.0" - unist-util-generated: "npm:^1.0.0" - unist-util-position: "npm:^3.0.0" - unist-util-visit: "npm:^2.0.0" - checksum: 10c0/08d0977c60ee951cb5e2e84bc821a842da463c37f7bbb79abf0be0894120ed5e2fc1d003d072d3bb968d8e813a916e132a094166d5562deb424acc45e1c661f4 + "@types/mdast": "npm:^4.0.0" + ccount: "npm:^2.0.0" + devlop: "npm:^1.0.0" + mdast-util-find-and-replace: "npm:^3.0.0" + micromark-util-character: "npm:^2.0.0" + checksum: 10c0/963cd22bd42aebdec7bdd0a527c9494d024d1ad0739c43dc040fee35bdfb5e29c22564330a7418a72b5eab51d47a6eff32bc0255ef3ccb5cebfe8970e91b81b6 + languageName: node + linkType: hard + +"mdast-util-gfm-footnote@npm:^2.0.0": + version: 2.1.0 + resolution: "mdast-util-gfm-footnote@npm:2.1.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + devlop: "npm:^1.1.0" + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + micromark-util-normalize-identifier: "npm:^2.0.0" + checksum: 10c0/8ab965ee6be3670d76ec0e95b2ba3101fc7444eec47564943ab483d96ac17d29da2a4e6146a2a288be30c21b48c4f3938a1e54b9a46fbdd321d49a5bc0077ed0 + languageName: node + linkType: hard + +"mdast-util-gfm-strikethrough@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-strikethrough@npm:2.0.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + checksum: 10c0/b053e93d62c7545019bd914271ea9e5667ad3b3b57d16dbf68e56fea39a7e19b4a345e781312714eb3d43fdd069ff7ee22a3ca7f6149dfa774554f19ce3ac056 + languageName: node + linkType: hard + +"mdast-util-gfm-table@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-table@npm:2.0.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + devlop: "npm:^1.0.0" + markdown-table: "npm:^3.0.0" + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + checksum: 10c0/128af47c503a53bd1c79f20642561e54a510ad5e2db1e418d28fefaf1294ab839e6c838e341aef5d7e404f9170b9ca3d1d89605f234efafde93ee51174a6e31e + languageName: node + linkType: hard + +"mdast-util-gfm-task-list-item@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-task-list-item@npm:2.0.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + devlop: "npm:^1.0.0" + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + checksum: 10c0/258d725288482b636c0a376c296431390c14b4f29588675297cb6580a8598ed311fc73ebc312acfca12cc8546f07a3a285a53a3b082712e2cbf5c190d677d834 + languageName: node + linkType: hard + +"mdast-util-gfm@npm:^3.0.0": + version: 3.1.0 + resolution: "mdast-util-gfm@npm:3.1.0" + dependencies: + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-gfm-autolink-literal: "npm:^2.0.0" + mdast-util-gfm-footnote: "npm:^2.0.0" + mdast-util-gfm-strikethrough: "npm:^2.0.0" + mdast-util-gfm-table: "npm:^2.0.0" + mdast-util-gfm-task-list-item: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + checksum: 10c0/4bedcfb6a20e39901c8772f0d2bb2d7a64ae87a54c13cbd92eec062cf470fbb68c2ad754e149af5b30794e2de61c978ab1de1ace03c0c40f443ca9b9b8044f81 + languageName: node + linkType: hard + +"mdast-util-mdx-expression@npm:^2.0.0": + version: 2.0.1 + resolution: "mdast-util-mdx-expression@npm:2.0.1" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + "@types/hast": "npm:^3.0.0" + "@types/mdast": "npm:^4.0.0" + devlop: "npm:^1.0.0" + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + checksum: 10c0/9a1e57940f66431f10312fa239096efa7627f375e7933b5d3162c0b5c1712a72ac87447aff2b6838d2bbd5c1311b188718cc90b33b67dc67a88550e0a6ef6183 + languageName: node + linkType: hard + +"mdast-util-mdx-jsx@npm:^3.0.0": + version: 3.2.0 + resolution: "mdast-util-mdx-jsx@npm:3.2.0" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + "@types/hast": "npm:^3.0.0" + "@types/mdast": "npm:^4.0.0" + "@types/unist": "npm:^3.0.0" + ccount: "npm:^2.0.0" + devlop: "npm:^1.1.0" + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + parse-entities: "npm:^4.0.0" + stringify-entities: "npm:^4.0.0" + unist-util-stringify-position: "npm:^4.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/3acadaf3b962254f7ad2990fed4729961dc0217ca31fde9917986e880843f3ecf3392b1f22d569235cacd180d50894ad266db7af598aedca69d330d33c7ac613 + languageName: node + linkType: hard + +"mdast-util-mdx@npm:^3.0.0": + version: 3.0.0 + resolution: "mdast-util-mdx@npm:3.0.0" + dependencies: + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-mdx-expression: "npm:^2.0.0" + mdast-util-mdx-jsx: "npm:^3.0.0" + mdast-util-mdxjs-esm: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + checksum: 10c0/4faea13f77d6bc9aa64ee41a5e4779110b73444a17fda363df6ebe880ecfa58b321155b71f8801c3faa6d70d6222a32a00cbd6dbf5fad8db417f4688bc9c74e1 + languageName: node + linkType: hard + +"mdast-util-mdxjs-esm@npm:^2.0.0, mdast-util-mdxjs-esm@npm:^2.0.1": + version: 2.0.1 + resolution: "mdast-util-mdxjs-esm@npm:2.0.1" + dependencies: + "@types/estree-jsx": "npm:^1.0.0" + "@types/hast": "npm:^3.0.0" + "@types/mdast": "npm:^4.0.0" + devlop: "npm:^1.0.0" + mdast-util-from-markdown: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + checksum: 10c0/5bda92fc154141705af2b804a534d891f28dac6273186edf1a4c5e3f045d5b01dbcac7400d27aaf91b7e76e8dce007c7b2fdf136c11ea78206ad00bdf9db46bc + languageName: node + linkType: hard + +"mdast-util-phrasing@npm:^4.0.0": + version: 4.1.0 + resolution: "mdast-util-phrasing@npm:4.1.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + unist-util-is: "npm:^6.0.0" + checksum: 10c0/bf6c31d51349aa3d74603d5e5a312f59f3f65662ed16c58017169a5fb0f84ca98578f626c5ee9e4aa3e0a81c996db8717096705521bddb4a0185f98c12c9b42f + languageName: node + linkType: hard + +"mdast-util-to-hast@npm:^13.0.0": + version: 13.2.1 + resolution: "mdast-util-to-hast@npm:13.2.1" + dependencies: + "@types/hast": "npm:^3.0.0" + "@types/mdast": "npm:^4.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-util-sanitize-uri: "npm:^2.0.0" + trim-lines: "npm:^3.0.0" + unist-util-position: "npm:^5.0.0" + unist-util-visit: "npm:^5.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/3eeaf28a5e84e1e08e6d54a1a8a06c0fca88cb5d36f4cf8086f0177248d1ce6e4e751f4ad0da19a3dea1c6ea61bd80784acc3ae021e44ceeb21aa5413a375e43 languageName: node linkType: hard @@ -17107,21 +15000,66 @@ __metadata: languageName: node linkType: hard -"mdast-util-to-string@npm:^2.0.0": - version: 2.0.0 - resolution: "mdast-util-to-string@npm:2.0.0" - checksum: 10c0/a4231085133cdfec24644b694c13661e5a01d26716be0105b6792889faa04b8030e4abbf72d4be3363098b2b38b2b98f1f1f1f0858eb6580dc04e2aca1436a37 +"mdast-util-to-markdown-cjk-friendly-gfm-strikethrough@npm:1.0.0": + version: 1.0.0 + resolution: "mdast-util-to-markdown-cjk-friendly-gfm-strikethrough@npm:1.0.0" + dependencies: + mdast-util-gfm-strikethrough: "npm:^2.0.0" + mdast-util-to-markdown: "npm:^2.1.2" + micromark-extension-cjk-friendly-util: "npm:3.0.1" + micromark-util-symbol: "npm:^2.0.1" + peerDependencies: + "@types/mdast": "*" + peerDependenciesMeta: + "@types/mdast": + optional: true + checksum: 10c0/dc03d1001d8f5d2b33134935daa162d8f5bd307f1dc93af5b257487bdb216a0b60434d238c8f4f762f77c99767838a2f1f0790d355b711318df1de4f95072953 languageName: node linkType: hard -"mdn-data@npm:2.0.14": - version: 2.0.14 - resolution: "mdn-data@npm:2.0.14" - checksum: 10c0/67241f8708c1e665a061d2b042d2d243366e93e5bf1f917693007f6d55111588b952dcbfd3ea9c2d0969fb754aad81b30fdcfdcc24546495fc3b24336b28d4bd +"mdast-util-to-markdown-cjk-friendly@npm:1.0.0": + version: 1.0.0 + resolution: "mdast-util-to-markdown-cjk-friendly@npm:1.0.0" + dependencies: + mdast-util-to-markdown: "npm:^2.1.2" + micromark-extension-cjk-friendly-util: "npm:3.0.1" + micromark-util-symbol: "npm:^2.0.1" + peerDependencies: + "@types/mdast": "*" + peerDependenciesMeta: + "@types/mdast": + optional: true + checksum: 10c0/5fedb40578f136f74921abd3457626637328ddec8d0e60f490ee7c69b10308d5a580faa627a06a11303985d2c714ad46a883d4a436015c0e9639bd333b54ec6b + languageName: node + linkType: hard + +"mdast-util-to-markdown@npm:^2.0.0, mdast-util-to-markdown@npm:^2.1.2": + version: 2.1.2 + resolution: "mdast-util-to-markdown@npm:2.1.2" + dependencies: + "@types/mdast": "npm:^4.0.0" + "@types/unist": "npm:^3.0.0" + longest-streak: "npm:^3.0.0" + mdast-util-phrasing: "npm:^4.0.0" + mdast-util-to-string: "npm:^4.0.0" + micromark-util-classify-character: "npm:^2.0.0" + micromark-util-decode-string: "npm:^2.0.0" + unist-util-visit: "npm:^5.0.0" + zwitch: "npm:^2.0.0" + checksum: 10c0/4649722a6099f12e797bd8d6469b2b43b44e526b5182862d9c7766a3431caad2c0112929c538a972f214e63c015395e5d3f54bd81d9ac1b16e6d8baaf582f749 + languageName: node + linkType: hard + +"mdast-util-to-string@npm:^4.0.0": + version: 4.0.0 + resolution: "mdast-util-to-string@npm:4.0.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + checksum: 10c0/2d3c1af29bf3fe9c20f552ee9685af308002488f3b04b12fa66652c9718f66f41a32f8362aa2d770c3ff464c034860b41715902ada2306bb0a055146cef064d7 languageName: node linkType: hard -"mdurl@npm:^1.0.0, mdurl@npm:^1.0.1": +"mdurl@npm:^1.0.1": version: 1.0.1 resolution: "mdurl@npm:1.0.1" checksum: 10c0/ea8534341eb002aaa532a722daef6074cd8ca66202e10a2b4cda46722c1ebdb1da92197ac300bc953d3ef1bf41cd6561ef2cc69d82d5d0237dae00d4a61a4eee @@ -17135,6 +15073,13 @@ __metadata: languageName: node linkType: hard +"medium-zoom@npm:1.1.0": + version: 1.1.0 + resolution: "medium-zoom@npm:1.1.0" + checksum: 10c0/7d1f05e8eab045c33d7c04d4ee7bf04f5246cf7a720d7b5f5a51c36ab23666e363bcbb6bffae50b5948d5eb19361914cb0e26a1fce5c1fff7a266bc0217893f3 + languageName: node + linkType: hard + "mem@npm:^8.1.1": version: 8.1.1 resolution: "mem@npm:8.1.1" @@ -17145,7 +15090,7 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.1.2, memfs@npm:^3.2.2, memfs@npm:^3.4.3": +"memfs@npm:^3.2.2": version: 3.5.3 resolution: "memfs@npm:3.5.3" dependencies: @@ -17237,7 +15182,7 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.3.0, merge2@npm:^1.4.1": +"merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1" checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb @@ -17475,15 +15420,506 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^3.1.4": - version: 3.1.10 - resolution: "micromatch@npm:3.1.10" - dependencies: - arr-diff: "npm:^4.0.0" - array-unique: "npm:^0.3.2" - braces: "npm:^2.3.1" - define-property: "npm:^2.0.2" - extend-shallow: "npm:^3.0.2" +"micromark-core-commonmark@npm:^2.0.0": + version: 2.0.3 + resolution: "micromark-core-commonmark@npm:2.0.3" + dependencies: + decode-named-character-reference: "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-factory-destination: "npm:^2.0.0" + micromark-factory-label: "npm:^2.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-factory-title: "npm:^2.0.0" + micromark-factory-whitespace: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-chunked: "npm:^2.0.0" + micromark-util-classify-character: "npm:^2.0.0" + micromark-util-html-tag-name: "npm:^2.0.0" + micromark-util-normalize-identifier: "npm:^2.0.0" + micromark-util-resolve-all: "npm:^2.0.0" + micromark-util-subtokenize: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/bd4a794fdc9e88dbdf59eaf1c507ddf26e5f7ddf4e52566c72239c0f1b66adbcd219ba2cd42350debbe24471434d5f5e50099d2b3f4e5762ca222ba8e5b549ee + languageName: node + linkType: hard + +"micromark-extension-cjk-friendly-gfm-strikethrough@npm:2.0.1": + version: 2.0.1 + resolution: "micromark-extension-cjk-friendly-gfm-strikethrough@npm:2.0.1" + dependencies: + devlop: "npm:^1.1.0" + get-east-asian-width: "npm:^1.4.0" + micromark-extension-cjk-friendly-util: "npm:3.0.1" + micromark-util-character: "npm:^2.1.1" + micromark-util-chunked: "npm:^2.0.1" + micromark-util-resolve-all: "npm:^2.0.1" + micromark-util-symbol: "npm:^2.0.1" + peerDependencies: + micromark: ^4.0.0 + micromark-util-types: ^2.0.0 + peerDependenciesMeta: + micromark-util-types: + optional: true + checksum: 10c0/6e51e6101fc287322b12379b32052fb6cbfdb1f427f87671d1b5a3b9d4fc685b60eb8a01f897bb5fc1aeae6197ec668e8f0a3179db80870b11e295cc2fe4bbb3 + languageName: node + linkType: hard + +"micromark-extension-cjk-friendly-util@npm:3.0.1": + version: 3.0.1 + resolution: "micromark-extension-cjk-friendly-util@npm:3.0.1" + dependencies: + get-east-asian-width: "npm:^1.4.0" + micromark-util-character: "npm:^2.1.1" + micromark-util-symbol: "npm:^2.0.1" + peerDependenciesMeta: + micromark-util-types: + optional: true + checksum: 10c0/5a9210b21fcf38b6b6f17dd49587eb70cc17001294e49a81cc128975be8fcb7654d70ed198280c7d56e8664338c1cbe0ca9d4ecbe57348e380f0552b7178602c + languageName: node + linkType: hard + +"micromark-extension-cjk-friendly@npm:2.0.1": + version: 2.0.1 + resolution: "micromark-extension-cjk-friendly@npm:2.0.1" + dependencies: + devlop: "npm:^1.1.0" + micromark-extension-cjk-friendly-util: "npm:3.0.1" + micromark-util-chunked: "npm:^2.0.1" + micromark-util-resolve-all: "npm:^2.0.1" + micromark-util-symbol: "npm:^2.0.1" + peerDependencies: + micromark: ^4.0.0 + micromark-util-types: ^2.0.0 + peerDependenciesMeta: + micromark-util-types: + optional: true + checksum: 10c0/23bd7aabecc83f58508e29a181cf1baa5ad7e0612e1a227cdbd445214afbfd47e9bb2100e740d15728078eaeedd7820cdc70585f36c62ecb70e38bd6e017afa4 + languageName: node + linkType: hard + +"micromark-extension-gfm-autolink-literal@npm:^2.0.0": + version: 2.1.0 + resolution: "micromark-extension-gfm-autolink-literal@npm:2.1.0" + dependencies: + micromark-util-character: "npm:^2.0.0" + micromark-util-sanitize-uri: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/84e6fbb84ea7c161dfa179665dc90d51116de4c28f3e958260c0423e5a745372b7dcbc87d3cde98213b532e6812f847eef5ae561c9397d7f7da1e59872ef3efe + languageName: node + linkType: hard + +"micromark-extension-gfm-footnote@npm:^2.0.0": + version: 2.1.0 + resolution: "micromark-extension-gfm-footnote@npm:2.1.0" + dependencies: + devlop: "npm:^1.0.0" + micromark-core-commonmark: "npm:^2.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-normalize-identifier: "npm:^2.0.0" + micromark-util-sanitize-uri: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/d172e4218968b7371b9321af5cde8c77423f73b233b2b0fcf3ff6fd6f61d2e0d52c49123a9b7910612478bf1f0d5e88c75a3990dd68f70f3933fe812b9f77edc + languageName: node + linkType: hard + +"micromark-extension-gfm-strikethrough@npm:^2.0.0": + version: 2.1.0 + resolution: "micromark-extension-gfm-strikethrough@npm:2.1.0" + dependencies: + devlop: "npm:^1.0.0" + micromark-util-chunked: "npm:^2.0.0" + micromark-util-classify-character: "npm:^2.0.0" + micromark-util-resolve-all: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/ef4f248b865bdda71303b494671b7487808a340b25552b11ca6814dff3fcfaab9be8d294643060bbdb50f79313e4a686ab18b99cbe4d3ee8a4170fcd134234fb + languageName: node + linkType: hard + +"micromark-extension-gfm-table@npm:^2.0.0": + version: 2.1.1 + resolution: "micromark-extension-gfm-table@npm:2.1.1" + dependencies: + devlop: "npm:^1.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/04bc00e19b435fa0add62cd029d8b7eb6137522f77832186b1d5ef34544a9bd030c9cf85e92ddfcc5c31f6f0a58a43d4b96dba4fc21316037c734630ee12c912 + languageName: node + linkType: hard + +"micromark-extension-gfm-tagfilter@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-gfm-tagfilter@npm:2.0.0" + dependencies: + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/995558843fff137ae4e46aecb878d8a4691cdf23527dcf1e2f0157d66786be9f7bea0109c52a8ef70e68e3f930af811828ba912239438e31a9cfb9981f44d34d + languageName: node + linkType: hard + +"micromark-extension-gfm-task-list-item@npm:^2.0.0": + version: 2.1.0 + resolution: "micromark-extension-gfm-task-list-item@npm:2.1.0" + dependencies: + devlop: "npm:^1.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/78aa537d929e9309f076ba41e5edc99f78d6decd754b6734519ccbbfca8abd52e1c62df68d41a6ae64d2a3fc1646cea955893c79680b0b4385ced4c52296181f + languageName: node + linkType: hard + +"micromark-extension-gfm@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-gfm@npm:3.0.0" + dependencies: + micromark-extension-gfm-autolink-literal: "npm:^2.0.0" + micromark-extension-gfm-footnote: "npm:^2.0.0" + micromark-extension-gfm-strikethrough: "npm:^2.0.0" + micromark-extension-gfm-table: "npm:^2.0.0" + micromark-extension-gfm-tagfilter: "npm:^2.0.0" + micromark-extension-gfm-task-list-item: "npm:^2.0.0" + micromark-util-combine-extensions: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/970e28df6ebdd7c7249f52a0dda56e0566fbfa9ae56c8eeeb2445d77b6b89d44096880cd57a1c01e7821b1f4e31009109fbaca4e89731bff7b83b8519690e5d9 + languageName: node + linkType: hard + +"micromark-extension-mdx-expression@npm:^3.0.0": + version: 3.0.1 + resolution: "micromark-extension-mdx-expression@npm:3.0.1" + dependencies: + "@types/estree": "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-factory-mdx-expression: "npm:^2.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-events-to-acorn: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/4d8cc5353b083b06bd51c98389de9c198261a5b2b440b75e85000a18d10511f21ba77538d6dfde0e0589df9de3fba9a1d14c2448d30c92d6b461c26d86e397f4 + languageName: node + linkType: hard + +"micromark-extension-mdx-jsx@npm:^3.0.0": + version: 3.0.2 + resolution: "micromark-extension-mdx-jsx@npm:3.0.2" + dependencies: + "@types/estree": "npm:^1.0.0" + devlop: "npm:^1.0.0" + estree-util-is-identifier-name: "npm:^3.0.0" + micromark-factory-mdx-expression: "npm:^2.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-events-to-acorn: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/5693b2e51934ac29a6aab521eaa2151f891d1fe092550bbd4ce24e4dd7567c1421a54f5e585a57dfa1769a79570f6df57ddd7a98bf0889dd11d495847a266dd7 + languageName: node + linkType: hard + +"micromark-extension-mdx-md@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-mdx-md@npm:2.0.0" + dependencies: + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/bae91c61273de0e5ba80a980c03470e6cd9d7924aa936f46fbda15d780704d9386e945b99eda200e087b96254fbb4271a9545d5ce02676cd6ae67886a8bf82df + languageName: node + linkType: hard + +"micromark-extension-mdxjs-esm@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-mdxjs-esm@npm:3.0.0" + dependencies: + "@types/estree": "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-core-commonmark: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-events-to-acorn: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + unist-util-position-from-estree: "npm:^2.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/13e3f726495a960650cdedcba39198ace5bdc953ccb12c14d71fc9ed9bb88e40cc3ba9231e973f6984da3b3573e7ddb23ce409f7c16f52a8d57b608bf46c748d + languageName: node + linkType: hard + +"micromark-extension-mdxjs@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-mdxjs@npm:3.0.0" + dependencies: + acorn: "npm:^8.0.0" + acorn-jsx: "npm:^5.0.0" + micromark-extension-mdx-expression: "npm:^3.0.0" + micromark-extension-mdx-jsx: "npm:^3.0.0" + micromark-extension-mdx-md: "npm:^2.0.0" + micromark-extension-mdxjs-esm: "npm:^3.0.0" + micromark-util-combine-extensions: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/fd84f036ddad0aabbc12e7f1b3e9dcfe31573bbc413c5ae903779ef0366d7a4c08193547e7ba75718c9f45654e45f52e575cfc2f23a5f89205a8a70d9a506aea + languageName: node + linkType: hard + +"micromark-factory-destination@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-factory-destination@npm:2.0.1" + dependencies: + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/bbafcf869cee5bf511161354cb87d61c142592fbecea051000ff116068dc85216e6d48519d147890b9ea5d7e2864a6341c0c09d9948c203bff624a80a476023c + languageName: node + linkType: hard + +"micromark-factory-label@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-factory-label@npm:2.0.1" + dependencies: + devlop: "npm:^1.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/0137716b4ecb428114165505e94a2f18855c8bbea21b07a8b5ce514b32a595ed789d2b967125718fc44c4197ceaa48f6609d58807a68e778138d2e6b91b824e8 + languageName: node + linkType: hard + +"micromark-factory-mdx-expression@npm:^2.0.0": + version: 2.0.3 + resolution: "micromark-factory-mdx-expression@npm:2.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-events-to-acorn: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + unist-util-position-from-estree: "npm:^2.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/a6004ef6272dd01a5d718f2affd7bfb5e08f0849340f5fd96ac823fbc5e9d3b3343acedda50805873ccda5e3b8af4d5fbb302abc874544044ac90c217345cf97 + languageName: node + linkType: hard + +"micromark-factory-space@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-factory-space@npm:2.0.1" + dependencies: + micromark-util-character: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/f9ed43f1c0652d8d898de0ac2be3f77f776fffe7dd96bdbba1e02d7ce33d3853c6ff5daa52568fc4fa32cdf3a62d86b85ead9b9189f7211e1d69ff2163c450fb + languageName: node + linkType: hard + +"micromark-factory-title@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-factory-title@npm:2.0.1" + dependencies: + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/e72fad8d6e88823514916890099a5af20b6a9178ccf78e7e5e05f4de99bb8797acb756257d7a3a57a53854cb0086bf8aab15b1a9e9db8982500dd2c9ff5948b6 + languageName: node + linkType: hard + +"micromark-factory-whitespace@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-factory-whitespace@npm:2.0.1" + dependencies: + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/20a1ec58698f24b766510a309b23a10175034fcf1551eaa9da3adcbed3e00cd53d1ebe5f030cf873f76a1cec3c34eb8c50cc227be3344caa9ed25d56cf611224 + languageName: node + linkType: hard + +"micromark-util-character@npm:^2.0.0, micromark-util-character@npm:^2.1.1": + version: 2.1.1 + resolution: "micromark-util-character@npm:2.1.1" + dependencies: + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/d3fe7a5e2c4060fc2a076f9ce699c82a2e87190a3946e1e5eea77f563869b504961f5668d9c9c014724db28ac32fa909070ea8b30c3a39bd0483cc6c04cc76a1 + languageName: node + linkType: hard + +"micromark-util-chunked@npm:^2.0.0, micromark-util-chunked@npm:^2.0.1": + version: 2.0.1 + resolution: "micromark-util-chunked@npm:2.0.1" + dependencies: + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/b68c0c16fe8106949537bdcfe1be9cf36c0ccd3bc54c4007003cb0984c3750b6cdd0fd77d03f269a3382b85b0de58bde4f6eedbe7ecdf7244759112289b1ab56 + languageName: node + linkType: hard + +"micromark-util-classify-character@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-classify-character@npm:2.0.1" + dependencies: + micromark-util-character: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/8a02e59304005c475c332f581697e92e8c585bcd45d5d225a66c1c1b14ab5a8062705188c2ccec33cc998d33502514121478b2091feddbc751887fc9c290ed08 + languageName: node + linkType: hard + +"micromark-util-combine-extensions@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-combine-extensions@npm:2.0.1" + dependencies: + micromark-util-chunked: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/f15e282af24c8372cbb10b9b0b3e2c0aa681fea0ca323a44d6bc537dc1d9382c819c3689f14eaa000118f5a163245358ce6276b2cda9a84439cdb221f5d86ae7 + languageName: node + linkType: hard + +"micromark-util-decode-numeric-character-reference@npm:^2.0.0": + version: 2.0.2 + resolution: "micromark-util-decode-numeric-character-reference@npm:2.0.2" + dependencies: + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/9c8a9f2c790e5593ffe513901c3a110e9ec8882a08f466da014112a25e5059b51551ca0aeb7ff494657d86eceb2f02ee556c6558b8d66aadc61eae4a240da0df + languageName: node + linkType: hard + +"micromark-util-decode-string@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-decode-string@npm:2.0.1" + dependencies: + decode-named-character-reference: "npm:^1.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-decode-numeric-character-reference: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/f24d75b2e5310be6e7b6dee532e0d17d3bf46996841d6295f2a9c87a2046fff4ab603c52ab9d7a7a6430a8b787b1574ae895849c603d262d1b22eef71736b5cb + languageName: node + linkType: hard + +"micromark-util-encode@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-encode@npm:2.0.1" + checksum: 10c0/b2b29f901093845da8a1bf997ea8b7f5e061ffdba85070dfe14b0197c48fda64ffcf82bfe53c90cf9dc185e69eef8c5d41cae3ba918b96bc279326921b59008a + languageName: node + linkType: hard + +"micromark-util-events-to-acorn@npm:^2.0.0": + version: 2.0.3 + resolution: "micromark-util-events-to-acorn@npm:2.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + "@types/unist": "npm:^3.0.0" + devlop: "npm:^1.0.0" + estree-util-visit: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/a4e0716e943ffdd16a918edf51d4f8291ec2692f5c4d04693dbef3358716fba891f288197afd102c14f4d98dac09d52351046ab7aad1d50b74677bdd5fa683c0 + languageName: node + linkType: hard + +"micromark-util-html-tag-name@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-html-tag-name@npm:2.0.1" + checksum: 10c0/ae80444db786fde908e9295f19a27a4aa304171852c77414516418650097b8afb401961c9edb09d677b06e97e8370cfa65638dde8438ebd41d60c0a8678b85b9 + languageName: node + linkType: hard + +"micromark-util-normalize-identifier@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-normalize-identifier@npm:2.0.1" + dependencies: + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/5299265fa360769fc499a89f40142f10a9d4a5c3dd8e6eac8a8ef3c2e4a6570e4c009cf75ea46dce5ee31c01f25587bde2f4a5cc0a935584ae86dd857f2babbd + languageName: node + linkType: hard + +"micromark-util-resolve-all@npm:^2.0.0, micromark-util-resolve-all@npm:^2.0.1": + version: 2.0.1 + resolution: "micromark-util-resolve-all@npm:2.0.1" + dependencies: + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/bb6ca28764696bb479dc44a2d5b5fe003e7177aeae1d6b0d43f24cc223bab90234092d9c3ce4a4d2b8df095ccfd820537b10eb96bb7044d635f385d65a4c984a + languageName: node + linkType: hard + +"micromark-util-sanitize-uri@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-sanitize-uri@npm:2.0.1" + dependencies: + micromark-util-character: "npm:^2.0.0" + micromark-util-encode: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + checksum: 10c0/60e92166e1870fd4f1961468c2651013ff760617342918e0e0c3c4e872433aa2e60c1e5a672bfe5d89dc98f742d6b33897585cf86ae002cda23e905a3c02527c + languageName: node + linkType: hard + +"micromark-util-subtokenize@npm:^2.0.0": + version: 2.1.0 + resolution: "micromark-util-subtokenize@npm:2.1.0" + dependencies: + devlop: "npm:^1.0.0" + micromark-util-chunked: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/bee69eece4393308e657c293ba80d92ebcb637e5f55e21dcf9c3fa732b91a8eda8ac248d76ff375e675175bfadeae4712e5158ef97eef1111789da1ce7ab5067 + languageName: node + linkType: hard + +"micromark-util-symbol@npm:^2.0.0, micromark-util-symbol@npm:^2.0.1": + version: 2.0.1 + resolution: "micromark-util-symbol@npm:2.0.1" + checksum: 10c0/f2d1b207771e573232436618e78c5e46cd4b5c560dd4a6d63863d58018abbf49cb96ec69f7007471e51434c60de3c9268ef2bf46852f26ff4aacd10f9da16fe9 + languageName: node + linkType: hard + +"micromark-util-types@npm:^2.0.0": + version: 2.0.2 + resolution: "micromark-util-types@npm:2.0.2" + checksum: 10c0/c8c15b96c858db781c4393f55feec10004bf7df95487636c9a9f7209e51002a5cca6a047c5d2a5dc669ff92da20e57aaa881e81a268d9ccadb647f9dce305298 + languageName: node + linkType: hard + +"micromark@npm:^4.0.0": + version: 4.0.2 + resolution: "micromark@npm:4.0.2" + dependencies: + "@types/debug": "npm:^4.0.0" + debug: "npm:^4.0.0" + decode-named-character-reference: "npm:^1.0.0" + devlop: "npm:^1.0.0" + micromark-core-commonmark: "npm:^2.0.0" + micromark-factory-space: "npm:^2.0.0" + micromark-util-character: "npm:^2.0.0" + micromark-util-chunked: "npm:^2.0.0" + micromark-util-combine-extensions: "npm:^2.0.0" + micromark-util-decode-numeric-character-reference: "npm:^2.0.0" + micromark-util-encode: "npm:^2.0.0" + micromark-util-normalize-identifier: "npm:^2.0.0" + micromark-util-resolve-all: "npm:^2.0.0" + micromark-util-sanitize-uri: "npm:^2.0.0" + micromark-util-subtokenize: "npm:^2.0.0" + micromark-util-symbol: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + checksum: 10c0/07462287254219d6eda6eac8a3cebaff2994e0575499e7088027b825105e096e4f51e466b14b2a81b71933a3b6c48ee069049d87bc2c2127eee50d9cc69e8af6 + languageName: node + linkType: hard + +"micromatch@npm:^3.1.4": + version: 3.1.10 + resolution: "micromatch@npm:3.1.10" + dependencies: + arr-diff: "npm:^4.0.0" + array-unique: "npm:^0.3.2" + braces: "npm:^2.3.1" + define-property: "npm:^2.0.2" + extend-shallow: "npm:^3.0.2" extglob: "npm:^2.0.4" fragment-cache: "npm:^0.2.1" kind-of: "npm:^6.0.2" @@ -17496,7 +15932,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -17527,22 +15963,6 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:~1.33.0": - version: 1.33.0 - resolution: "mime-db@npm:1.33.0" - checksum: 10c0/79172ce5468c8503b49dddfdddc18d3f5fe2599f9b5fe1bc321a8cbee14c96730fc6db22f907b23701b05b2936f865795f62ec3a78a7f3c8cb2450bb68c6763e - languageName: node - linkType: hard - -"mime-types@npm:2.1.18": - version: 2.1.18 - resolution: "mime-types@npm:2.1.18" - dependencies: - mime-db: "npm:~1.33.0" - checksum: 10c0/a96a8d12f4bb98bc7bfac6a8ccbd045f40368fc1030d9366050c3613825d3715d1c1f393e10a75a885d2cdc1a26cd6d5e11f3a2a0d5c4d361f00242139430a0f - languageName: node - linkType: hard - "mime-types@npm:2.1.31": version: 2.1.31 resolution: "mime-types@npm:2.1.31" @@ -17552,7 +15972,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.30, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.30, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -17634,34 +16054,6 @@ __metadata: languageName: node linkType: hard -"mini-css-extract-plugin@npm:^2.6.1": - version: 2.9.4 - resolution: "mini-css-extract-plugin@npm:2.9.4" - dependencies: - schema-utils: "npm:^4.0.0" - tapable: "npm:^2.2.1" - peerDependencies: - webpack: ^5.0.0 - checksum: 10c0/76f9e471784d52435ea766ce576ad23d37d0ea51c32ddc56414c8fdf14f7de44202dbc772cdf7549b7e54a5e56f569af93cfbd036d62d13ff8fd9571e53353b7 - languageName: node - linkType: hard - -"minimalistic-assert@npm:^1.0.0": - version: 1.0.1 - resolution: "minimalistic-assert@npm:1.0.1" - checksum: 10c0/96730e5601cd31457f81a296f521eb56036e6f69133c0b18c13fe941109d53ad23a4204d946a0d638d7f3099482a0cec8c9bb6d642604612ce43ee536be3dddd - languageName: node - linkType: hard - -"minimatch@npm:3.1.2, minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 - languageName: node - linkType: hard - "minimatch@npm:^10.1.1": version: 10.1.1 resolution: "minimatch@npm:10.1.1" @@ -17680,6 +16072,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + languageName: node + linkType: hard + "minimatch@npm:^3.1.5": version: 3.1.5 resolution: "minimatch@npm:3.1.5" @@ -17837,13 +16238,6 @@ __metadata: languageName: node linkType: hard -"mrmime@npm:^2.0.0": - version: 2.0.1 - resolution: "mrmime@npm:2.0.1" - checksum: 10c0/af05afd95af202fdd620422f976ad67dc18e6ee29beb03dd1ce950ea6ef664de378e44197246df4c7cdd73d47f2e7143a6e26e473084b9e4aa2095c0ad1e1761 - languageName: node - linkType: hard - "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -17908,18 +16302,6 @@ __metadata: languageName: node linkType: hard -"multicast-dns@npm:^7.2.5": - version: 7.2.5 - resolution: "multicast-dns@npm:7.2.5" - dependencies: - dns-packet: "npm:^5.2.2" - thunky: "npm:^1.0.2" - bin: - multicast-dns: cli.js - checksum: 10c0/5120171d4bdb1577764c5afa96e413353bff530d1b37081cb29cccc747f989eb1baf40574fe8e27060fc1aef72b59c042f72b9b208413de33bcf411343c69057 - languageName: node - linkType: hard - "multitars@npm:^1.0.0": version: 1.0.0 resolution: "multitars@npm:1.0.0" @@ -18022,16 +16404,6 @@ __metadata: languageName: node linkType: hard -"no-case@npm:^3.0.4": - version: 3.0.4 - resolution: "no-case@npm:3.0.4" - dependencies: - lower-case: "npm:^2.0.2" - tslib: "npm:^2.0.3" - checksum: 10c0/8ef545f0b3f8677c848f86ecbd42ca0ff3cd9dd71c158527b344c69ba14710d816d8489c746b6ca225e7b615108938a0bda0a54706f8c255933703ac1cf8e703 - languageName: node - linkType: hard - "node-dir@npm:^0.1.10": version: 0.1.17 resolution: "node-dir@npm:0.1.17" @@ -18041,15 +16413,6 @@ __metadata: languageName: node linkType: hard -"node-emoji@npm:^1.10.0": - version: 1.11.0 - resolution: "node-emoji@npm:1.11.0" - dependencies: - lodash: "npm:^4.17.21" - checksum: 10c0/5dac6502dbef087092d041fcc2686d8be61168593b3a9baf964d62652f55a3a9c2277f171b81cccb851ccef33f2d070f45e633fab1fda3264f8e1ae9041c673f - languageName: node - linkType: hard - "node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.7, node-fetch@npm:^2.7.0": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" @@ -18064,7 +16427,7 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:^1, node-forge@npm:^1.3.3": +"node-forge@npm:^1.3.3": version: 1.3.3 resolution: "node-forge@npm:1.3.3" checksum: 10c0/9c6f53b0ebb34865872cf62a35b0aef8fb337e2efc766626c2e3a0040f4c02933bf29a62ba999eb44a2aca73bd512c4eda22705a47b94654b9fb8ed53db9a1db @@ -18136,13 +16499,6 @@ __metadata: languageName: node linkType: hard -"normalize-css-color@npm:^1.0.2": - version: 1.0.2 - resolution: "normalize-css-color@npm:1.0.2" - checksum: 10c0/48655c9113d35c9e49d71e63ef636556b807692fdc536cfb3de2b51827ab9d75998c76d671576268e40dc3d89af91b750b2d1c889aa98856dd6181924add1712 - languageName: node - linkType: hard - "normalize-package-data@npm:^2.3.2, normalize-package-data@npm:^2.3.4, normalize-package-data@npm:^2.5.0": version: 2.5.0 resolution: "normalize-package-data@npm:2.5.0" @@ -18176,7 +16532,7 @@ __metadata: languageName: node linkType: hard -"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": +"normalize-path@npm:^3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 @@ -18239,13 +16595,6 @@ __metadata: languageName: node linkType: hard -"npm-to-yarn@npm:^2.0.0": - version: 2.2.1 - resolution: "npm-to-yarn@npm:2.2.1" - checksum: 10c0/65c696a3e595facad802b6b13c04e504806ea88fd4f87ab758f8042c19f65b4c4822815a47095df944b0809a95e574c27323c33cca5533f8454515eaa6e14fac - languageName: node - linkType: hard - "nprogress@npm:^0.2.0": version: 0.2.0 resolution: "nprogress@npm:0.2.0" @@ -18253,15 +16602,6 @@ __metadata: languageName: node linkType: hard -"nth-check@npm:^2.0.1": - version: 2.1.1 - resolution: "nth-check@npm:2.1.1" - dependencies: - boolbase: "npm:^1.0.0" - checksum: 10c0/5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479 - languageName: node - linkType: hard - "nullthrows@npm:^1.1.1": version: 1.1.1 resolution: "nullthrows@npm:1.1.1" @@ -18319,7 +16659,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.1.0, object.assign@npm:^4.1.4, object.assign@npm:^4.1.7": +"object.assign@npm:^4.1.4, object.assign@npm:^4.1.7": version: 4.1.7 resolution: "object.assign@npm:4.1.7" dependencies: @@ -18389,13 +16729,6 @@ __metadata: languageName: node linkType: hard -"obuf@npm:^1.0.0, obuf@npm:^1.1.2": - version: 1.1.2 - resolution: "obuf@npm:1.1.2" - checksum: 10c0/520aaac7ea701618eacf000fc96ae458e20e13b0569845800fc582f81b386731ab22d55354b4915d58171db00e79cfcd09c1638c02f89577ef092b38c65b7d81 - languageName: node - linkType: hard - "on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -18448,33 +16781,31 @@ __metadata: languageName: node linkType: hard -"open@npm:^7.0.3, open@npm:^7.4.2": - version: 7.4.2 - resolution: "open@npm:7.4.2" - dependencies: - is-docker: "npm:^2.0.0" - is-wsl: "npm:^2.1.1" - checksum: 10c0/77573a6a68f7364f3a19a4c80492712720746b63680ee304555112605ead196afe91052bd3c3d165efdf4e9d04d255e87de0d0a77acec11ef47fd5261251813f +"oniguruma-parser@npm:^0.12.2": + version: 0.12.2 + resolution: "oniguruma-parser@npm:0.12.2" + checksum: 10c0/fe5255d2cd5a6b845d5a0abe1725898ef40cea5522290dba6ccc08cc388891e9e8007af4baa5059942b786ad44b2b677ef25948039c9ba3f057bd35d2c52076a languageName: node linkType: hard -"open@npm:^8.0.9, open@npm:^8.4.0": - version: 8.4.2 - resolution: "open@npm:8.4.2" +"oniguruma-to-es@npm:^4.3.6": + version: 4.3.6 + resolution: "oniguruma-to-es@npm:4.3.6" dependencies: - define-lazy-prop: "npm:^2.0.0" - is-docker: "npm:^2.1.1" - is-wsl: "npm:^2.2.0" - checksum: 10c0/bb6b3a58401dacdb0aad14360626faf3fb7fba4b77816b373495988b724fb48941cad80c1b65d62bb31a17609b2cd91c41a181602caea597ca80dfbcc27e84c9 + oniguruma-parser: "npm:^0.12.2" + regex: "npm:^6.1.0" + regex-recursion: "npm:^6.0.2" + checksum: 10c0/044e08b98e706987c2882ccf228de2a671de4aff33e3bd370da137ba599c0d520549625ad26ecc8898502445c991e19f106f584e94382262ba2af02c908ca3cf languageName: node linkType: hard -"opener@npm:^1.5.2": - version: 1.5.2 - resolution: "opener@npm:1.5.2" - bin: - opener: bin/opener-bin.js - checksum: 10c0/dd56256ab0cf796585617bc28e06e058adf09211781e70b264c76a1dbe16e90f868c974e5bf5309c93469157c7d14b89c35dc53fe7293b0e40b4d2f92073bc79 +"open@npm:^7.0.3": + version: 7.4.2 + resolution: "open@npm:7.4.2" + dependencies: + is-docker: "npm:^2.0.0" + is-wsl: "npm:^2.1.1" + checksum: 10c0/77573a6a68f7364f3a19a4c80492712720746b63680ee304555112605ead196afe91052bd3c3d165efdf4e9d04d255e87de0d0a77acec11ef47fd5261251813f languageName: node linkType: hard @@ -18669,15 +17000,6 @@ __metadata: languageName: node linkType: hard -"p-map@npm:^4.0.0": - version: 4.0.0 - resolution: "p-map@npm:4.0.0" - dependencies: - aggregate-error: "npm:^3.0.0" - checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75 - languageName: node - linkType: hard - "p-map@npm:^7.0.2": version: 7.0.4 resolution: "p-map@npm:7.0.4" @@ -18685,16 +17007,6 @@ __metadata: languageName: node linkType: hard -"p-retry@npm:^4.5.0": - version: 4.6.2 - resolution: "p-retry@npm:4.6.2" - dependencies: - "@types/retry": "npm:0.12.0" - retry: "npm:^0.13.1" - checksum: 10c0/d58512f120f1590cfedb4c2e0c42cb3fa66f3cea8a4646632fcb834c56055bb7a6f138aa57b20cc236fb207c9d694e362e0b5c2b14d9b062f67e8925580c73b0 - languageName: node - linkType: hard - "p-try@npm:^1.0.0": version: 1.0.0 resolution: "p-try@npm:1.0.0" @@ -18730,16 +17042,6 @@ __metadata: languageName: node linkType: hard -"param-case@npm:^3.0.4": - version: 3.0.4 - resolution: "param-case@npm:3.0.4" - dependencies: - dot-case: "npm:^3.0.4" - tslib: "npm:^2.0.3" - checksum: 10c0/ccc053f3019f878eca10e70ec546d92f51a592f762917dafab11c8b532715dcff58356118a6f350976e4ab109e321756f05739643ed0ca94298e82291e6f9e76 - languageName: node - linkType: hard - "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -18777,6 +17079,21 @@ __metadata: languageName: node linkType: hard +"parse-entities@npm:^4.0.0": + version: 4.0.2 + resolution: "parse-entities@npm:4.0.2" + dependencies: + "@types/unist": "npm:^2.0.0" + character-entities-legacy: "npm:^3.0.0" + character-reference-invalid: "npm:^2.0.0" + decode-named-character-reference: "npm:^1.0.0" + is-alphanumerical: "npm:^2.0.0" + is-decimal: "npm:^2.0.0" + is-hexadecimal: "npm:^2.0.0" + checksum: 10c0/a13906b1151750b78ed83d386294066daf5fb559e08c5af9591b2d98cc209123103016a01df776f65f8219ad26652d6d6b210d0974d452049cddfc53a8916c34 + languageName: node + linkType: hard + "parse-json@npm:5.2.0, parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" @@ -18799,13 +17116,6 @@ __metadata: languageName: node linkType: hard -"parse-numeric-range@npm:^1.3.0": - version: 1.3.0 - resolution: "parse-numeric-range@npm:1.3.0" - checksum: 10c0/53465afaa92111e86697281b684aa4574427360889cc23a1c215488c06b72441febdbf09f47ab0bef9a0c701e059629f3eebd2fe6fb241a254ad7a7a642aebe8 - languageName: node - linkType: hard - "parse-path@npm:^4.0.0": version: 4.0.4 resolution: "parse-path@npm:4.0.4" @@ -18846,25 +17156,6 @@ __metadata: languageName: node linkType: hard -"parse5-htmlparser2-tree-adapter@npm:^7.1.0": - version: 7.1.0 - resolution: "parse5-htmlparser2-tree-adapter@npm:7.1.0" - dependencies: - domhandler: "npm:^5.0.3" - parse5: "npm:^7.0.0" - checksum: 10c0/e5a4e0b834c84c9e244b5749f8d007f4baaeafac7a1da2c54be3421ffd9ef8fdec4f198bf55cda22e88e6ba95e9943f6ed5aa3ae5900b39972ebf5dc8c3f4722 - languageName: node - linkType: hard - -"parse5-parser-stream@npm:^7.1.2": - version: 7.1.2 - resolution: "parse5-parser-stream@npm:7.1.2" - dependencies: - parse5: "npm:^7.0.0" - checksum: 10c0/e236c61000d38ecad369e725a48506b051cebad8abb00e6d4e8bff7aa85c183820fcb45db1559cc90955bdbbdbd665ea94c41259594e74566fff411478dc7fcb - languageName: node - linkType: hard - "parse5@npm:^5.0.0": version: 5.1.1 resolution: "parse5@npm:5.1.1" @@ -18879,7 +17170,7 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^7.0.0, parse5@npm:^7.3.0": +"parse5@npm:^7.0.0": version: 7.3.0 resolution: "parse5@npm:7.3.0" dependencies: @@ -18888,7 +17179,7 @@ __metadata: languageName: node linkType: hard -"parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": +"parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5 @@ -18905,16 +17196,6 @@ __metadata: languageName: node linkType: hard -"pascal-case@npm:^3.1.2": - version: 3.1.2 - resolution: "pascal-case@npm:3.1.2" - dependencies: - no-case: "npm:^3.0.4" - tslib: "npm:^2.0.3" - checksum: 10c0/05ff7c344809fd272fc5030ae0ee3da8e4e63f36d47a1e0a4855ca59736254192c5a27b5822ed4bae96e54048eec5f6907713cfcfff7cdf7a464eaf7490786d8 - languageName: node - linkType: hard - "pascalcase@npm:^0.1.1": version: 0.1.1 resolution: "pascalcase@npm:0.1.1" @@ -18922,30 +17203,6 @@ __metadata: languageName: node linkType: hard -"patch-package@npm:^6.5.0": - version: 6.5.1 - resolution: "patch-package@npm:6.5.1" - dependencies: - "@yarnpkg/lockfile": "npm:^1.1.0" - chalk: "npm:^4.1.2" - cross-spawn: "npm:^6.0.5" - find-yarn-workspace-root: "npm:^2.0.0" - fs-extra: "npm:^9.0.0" - is-ci: "npm:^2.0.0" - klaw-sync: "npm:^6.0.0" - minimist: "npm:^1.2.6" - open: "npm:^7.4.2" - rimraf: "npm:^2.6.3" - semver: "npm:^5.6.0" - slash: "npm:^2.0.0" - tmp: "npm:^0.0.33" - yaml: "npm:^1.10.2" - bin: - patch-package: index.js - checksum: 10c0/0f74d6099b05431c88a60308bd9ec0b1f9d3ae436026f488cfe99476ae74e7a464be4a16a7c83c7b89c23764502c79d37227cf27b17c30b9b2e4d577f8aecedb - languageName: node - linkType: hard - "path-case@npm:^2.1.0": version: 2.1.1 resolution: "path-case@npm:2.1.1" @@ -18983,7 +17240,7 @@ __metadata: languageName: node linkType: hard -"path-is-inside@npm:1.0.2, path-is-inside@npm:^1.0.2": +"path-is-inside@npm:^1.0.2": version: 1.0.2 resolution: "path-is-inside@npm:1.0.2" checksum: 10c0/7fdd4b41672c70461cce734fc222b33e7b447fa489c7c4377c95e7e6852d83d69741f307d88ec0cc3b385b41cb4accc6efac3c7c511cd18512e95424f5fa980c @@ -19031,22 +17288,6 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:3.3.0": - version: 3.3.0 - resolution: "path-to-regexp@npm:3.3.0" - checksum: 10c0/ffa0ebe7088d38d435a8d08b0fe6e8c93ceb2a81a65d4dd1d9a538f52e09d5e3474ed5f553cb3b180d894b0caa10698a68737ab599fd1e56b4663d1a64c9f77b - languageName: node - linkType: hard - -"path-to-regexp@npm:^1.7.0": - version: 1.9.0 - resolution: "path-to-regexp@npm:1.9.0" - dependencies: - isarray: "npm:0.0.1" - checksum: 10c0/de9ddb01b84d9c2c8e2bed18630d8d039e2d6f60a6538595750fa08c7a6482512257464c8da50616f266ab2cdd2428387e85f3b089e4c3f25d0c537e898a0751 - languageName: node - linkType: hard - "path-to-regexp@npm:~0.1.12": version: 0.1.12 resolution: "path-to-regexp@npm:0.1.12" @@ -19093,14 +17334,14 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": +"picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be @@ -19174,15 +17415,6 @@ __metadata: languageName: node linkType: hard -"pkg-up@npm:^3.1.0": - version: 3.1.0 - resolution: "pkg-up@npm:3.1.0" - dependencies: - find-up: "npm:^3.0.0" - checksum: 10c0/ecb60e1f8e1f611c0bdf1a0b6a474d6dfb51185567dc6f29cdef37c8d480ecba5362e006606bb290519bbb6f49526c403fabea93c3090c20368d98bb90c999ab - languageName: node - linkType: hard - "plist@npm:^3.0.5, plist@npm:^3.1.0": version: 3.1.0 resolution: "plist@npm:3.1.0" @@ -19215,192 +17447,7 @@ __metadata: languageName: node linkType: hard -"postcss-calc@npm:^8.2.3": - version: 8.2.4 - resolution: "postcss-calc@npm:8.2.4" - dependencies: - postcss-selector-parser: "npm:^6.0.9" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.2 - checksum: 10c0/8518a429488c3283ff1560c83a511f6f772329bc61d88875eb7c83e13a8683b7ccbdccaa9946024cf1553da3eacd2f40fcbcebf1095f7fdeb432bf86bc6ba6ba - languageName: node - linkType: hard - -"postcss-colormin@npm:^5.3.1": - version: 5.3.1 - resolution: "postcss-colormin@npm:5.3.1" - dependencies: - browserslist: "npm:^4.21.4" - caniuse-api: "npm:^3.0.0" - colord: "npm:^2.9.1" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/c4ca6f335dd992dc8e3df24bffc3495c4e504eba8489c81cb6836fdce3203f423cf4c0b640c4b63c586f588c59d82adb5313c3c5d1a68113896d18ed71caa462 - languageName: node - linkType: hard - -"postcss-convert-values@npm:^5.1.3": - version: 5.1.3 - resolution: "postcss-convert-values@npm:5.1.3" - dependencies: - browserslist: "npm:^4.21.4" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/cd10a81781a12487b2921ff84a1a068e948a1956b9539a284c202abecf4cacdd3e106eb026026b22dbf70933f4315c824c111f6b71f56c355e47b842ca9b1dec - languageName: node - linkType: hard - -"postcss-discard-comments@npm:^5.1.2": - version: 5.1.2 - resolution: "postcss-discard-comments@npm:5.1.2" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/cb5ba81623c498e18d406138e7d27d69fc668802a1139a8de69d28e80b3fe222cda7b634940512cae78d04f0c78afcd15d92bcf80e537c6c85fa8ff9cd61d00f - languageName: node - linkType: hard - -"postcss-discard-duplicates@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-discard-duplicates@npm:5.1.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/3d3a49536c56097c06b4f085412e0cda0854fac1c559563ccb922d9fab6305ff13058cd6fee422aa66c1d7e466add4e7672d7ae2ff551a4af6f1a8d2142d471f - languageName: node - linkType: hard - -"postcss-discard-empty@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-discard-empty@npm:5.1.1" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/36c8b2197af836dbd93168c72cde4edc1f10fe00e564824119da076d3764909745bb60e4ada04052322e26872d1bce6a37c56815f1c48c813a21adca1a41fbdc - languageName: node - linkType: hard - -"postcss-discard-overridden@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-discard-overridden@npm:5.1.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/7d3fc0b0d90599606fc083327a7c24390f90270a94a0119af4b74815d518948581579281f63b9bfa62e2644edf59bc9e725dc04ea5ba213f697804f3fb4dd8dc - languageName: node - linkType: hard - -"postcss-discard-unused@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-discard-unused@npm:5.1.0" - dependencies: - postcss-selector-parser: "npm:^6.0.5" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/eb7649eae1ef9987c397f4f533eb83f4245686317a5a0b468affd875d4d22778b62134e638198750efbaa41b7b7767995a91e5eb58d5fbbfe097506a3311102b - languageName: node - linkType: hard - -"postcss-loader@npm:^7.0.0": - version: 7.3.4 - resolution: "postcss-loader@npm:7.3.4" - dependencies: - cosmiconfig: "npm:^8.3.5" - jiti: "npm:^1.20.0" - semver: "npm:^7.5.4" - peerDependencies: - postcss: ^7.0.0 || ^8.0.1 - webpack: ^5.0.0 - checksum: 10c0/1bf7614aeea9ad1f8ee6be3a5451576c059391688ea67f825aedc2674056369597faeae4e4a81fe10843884c9904a71403d9a54197e1f560e8fbb9e61f2a2680 - languageName: node - linkType: hard - -"postcss-merge-idents@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-merge-idents@npm:5.1.1" - dependencies: - cssnano-utils: "npm:^3.1.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/97552b831330a8055641d3aea7b9840c60922d22d7fefdaf109daa7dee543e48a93ea6189a5549798b3f29e66657bc5c520e76493a04f8f999b94a2c8fee6060 - languageName: node - linkType: hard - -"postcss-merge-longhand@npm:^5.1.7": - version: 5.1.7 - resolution: "postcss-merge-longhand@npm:5.1.7" - dependencies: - postcss-value-parser: "npm:^4.2.0" - stylehacks: "npm:^5.1.1" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/4d9f44b03f19522cc81ae4f5b1f2a9ef2db918dbd8b3042d4f1b2461b2230b8ec1269334db6a67a863ba68f64cabd712e6e45340ddb22a3fc03cd34df69d2bf0 - languageName: node - linkType: hard - -"postcss-merge-rules@npm:^5.1.4": - version: 5.1.4 - resolution: "postcss-merge-rules@npm:5.1.4" - dependencies: - browserslist: "npm:^4.21.4" - caniuse-api: "npm:^3.0.0" - cssnano-utils: "npm:^3.1.0" - postcss-selector-parser: "npm:^6.0.5" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/e7686cdda052071bf98810ad381e26145c43a2286f9540f04f97ef93101604b78d478dd555db91e5f73751bb353c283ba75c2fcb16a3751ac7d93dc6a0130c41 - languageName: node - linkType: hard - -"postcss-minify-font-values@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-minify-font-values@npm:5.1.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/7aa4f93a853b657f79a8b28d0e924cafce3720086d9da02ce04b8b2f8de42e18ce32c8f7f1078390fb5ec82468e2d8e771614387cea3563f05fd9fa1798e1c59 - languageName: node - linkType: hard - -"postcss-minify-gradients@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-minify-gradients@npm:5.1.1" - dependencies: - colord: "npm:^2.9.1" - cssnano-utils: "npm:^3.1.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/bcb2802d7c8f0f76c7cff089884844f26c24b95f35c3ec951d7dec8c212495d1873d6ba62d6225ce264570e8e0668e271f9bc79bb6f5d2429c1f8933f4e3021d - languageName: node - linkType: hard - -"postcss-minify-params@npm:^5.1.4": - version: 5.1.4 - resolution: "postcss-minify-params@npm:5.1.4" - dependencies: - browserslist: "npm:^4.21.4" - cssnano-utils: "npm:^3.1.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/debce6f0f7dd9af69b4bb9e467ea1ccccff2d849b6020461a2b9741c0c137340e6076c245dc2e83880180eb2e82936280fa31dfe8608e5a2e3618f3d864314c5 - languageName: node - linkType: hard - -"postcss-minify-selectors@npm:^5.2.1": - version: 5.2.1 - resolution: "postcss-minify-selectors@npm:5.2.1" - dependencies: - postcss-selector-parser: "npm:^6.0.5" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/f3f4ec110f5f697cfc9dde3e491ff10aa07509bf33cc940aa539e4b5b643d1b9f8bb97f8bb83d05fc96f5eeb220500ebdeffbde513bd176c0671e21c2c96fab9 - languageName: node - linkType: hard - -"postcss-modules-extract-imports@npm:^3.0.0, postcss-modules-extract-imports@npm:^3.1.0": +"postcss-modules-extract-imports@npm:^3.0.0": version: 3.1.0 resolution: "postcss-modules-extract-imports@npm:3.1.0" peerDependencies: @@ -19409,237 +17456,48 @@ __metadata: languageName: node linkType: hard -"postcss-modules-local-by-default@npm:^4.0.0, postcss-modules-local-by-default@npm:^4.0.5": +"postcss-modules-local-by-default@npm:^4.0.0": version: 4.2.0 resolution: "postcss-modules-local-by-default@npm:4.2.0" dependencies: - icss-utils: "npm:^5.0.0" - postcss-selector-parser: "npm:^7.0.0" - postcss-value-parser: "npm:^4.1.0" - peerDependencies: - postcss: ^8.1.0 - checksum: 10c0/b0b83feb2a4b61f5383979d37f23116c99bc146eba1741ca3cf1acca0e4d0dbf293ac1810a6ab4eccbe1ee76440dd0a9eb2db5b3bba4f99fc1b3ded16baa6358 - languageName: node - linkType: hard - -"postcss-modules-scope@npm:^3.0.0, postcss-modules-scope@npm:^3.2.0": - version: 3.2.1 - resolution: "postcss-modules-scope@npm:3.2.1" - dependencies: - postcss-selector-parser: "npm:^7.0.0" - peerDependencies: - postcss: ^8.1.0 - checksum: 10c0/bd2d81f79e3da0ef6365b8e2c78cc91469d05b58046b4601592cdeef6c4050ed8fe1478ae000a1608042fc7e692cb51fecbd2d9bce3f4eace4d32e883ffca10b - languageName: node - linkType: hard - -"postcss-modules-values@npm:^4.0.0": - version: 4.0.0 - resolution: "postcss-modules-values@npm:4.0.0" - dependencies: - icss-utils: "npm:^5.0.0" - peerDependencies: - postcss: ^8.1.0 - checksum: 10c0/dd18d7631b5619fb9921b198c86847a2a075f32e0c162e0428d2647685e318c487a2566cc8cc669fc2077ef38115cde7a068e321f46fb38be3ad49646b639dbc - languageName: node - linkType: hard - -"postcss-normalize-charset@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-charset@npm:5.1.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/aa481584d4db48e0dbf820f992fa235e6c41ff3d4701a62d349f33c1ad4c5c7dcdea3096db9ff2a5c9497e9bed2186d594ccdb1b42d57b30f58affba5829ad9c - languageName: node - linkType: hard - -"postcss-normalize-display-values@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-display-values@npm:5.1.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/70b164fda885c097c02c98914fba4cd19b2382ff5f85f77e5315d88a1d477b4803f0f271d95a38e044e2a6c3b781c5c9bfb83222fc577199f2aeb0b8f4254e2f - languageName: node - linkType: hard - -"postcss-normalize-positions@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-positions@npm:5.1.1" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/910d58991fd38a7cf6ed6471e6fa4a96349690ad1a99a02e8cac46d76ba5045f2fca453088b68b05ff665afd96dc617c4674c68acaeabbe83f502e4963fb78b1 - languageName: node - linkType: hard - -"postcss-normalize-repeat-style@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-repeat-style@npm:5.1.1" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/57c3817a2107ebb17e4ceee3831d230c72a3ccc7650f4d5f12aa54f6ea766777401f4f63b2615b721350b2e8c7ae0b0bbc3f1c5ad4e7fa737c9efb92cfa0cbb0 - languageName: node - linkType: hard - -"postcss-normalize-string@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-string@npm:5.1.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/a5e9979998f478d385ddff865bdd8a4870af69fa8c91c9398572a299ff39b39a6bda922a48fab0d2cddc639f30159c39baaed880ed7d13cd27cc64eaa9400b3b - languageName: node - linkType: hard - -"postcss-normalize-timing-functions@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-timing-functions@npm:5.1.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/afb34d8e313004ae8cd92910bf1a6eb9885f29ae803cd9032b6dfe7b67a9ad93f800976f10e55170b2b08fe9484825e9272629971186812c2764c73843268237 - languageName: node - linkType: hard - -"postcss-normalize-unicode@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-unicode@npm:5.1.1" - dependencies: - browserslist: "npm:^4.21.4" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/c102888d488d05c53ab10ffcd4e0efb892ef0cc2f9b0abe9c9b175a2d7a9c226981ca6806ed9e5c1b82a8190f2b3a8342a6de800f019b417130661b0787ff6d7 - languageName: node - linkType: hard - -"postcss-normalize-url@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-normalize-url@npm:5.1.0" - dependencies: - normalize-url: "npm:^6.0.1" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/a016cefd1ef80f74ef9dbed50593d3b533101e93aaadfc292896fddd8d6c3eb732a9fc5cb2e0d27f79c1f60f0fdfc40b045a494b514451e9610c6acf9392eb98 - languageName: node - linkType: hard - -"postcss-normalize-whitespace@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-normalize-whitespace@npm:5.1.1" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/d7b53dd90fe369bfb9838a40096db904a41f50dadfd04247ec07d7ab5588c3d4e70d1c7f930523bd061cb74e6683cef45c6e6c4eb57ea174ee3fc99f3de222d1 - languageName: node - linkType: hard - -"postcss-ordered-values@npm:^5.1.3": - version: 5.1.3 - resolution: "postcss-ordered-values@npm:5.1.3" - dependencies: - cssnano-utils: "npm:^3.1.0" - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/55abfbd2c7267eefed62a881ed0b5c0c98409c50a589526a3ebb9f8d879979203e523b8888fa84732bdd1ac887f721287a037002fa70c27c8d33f1bcbae9d9c6 - languageName: node - linkType: hard - -"postcss-reduce-idents@npm:^5.2.0": - version: 5.2.0 - resolution: "postcss-reduce-idents@npm:5.2.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/f7a6bc0caa531e7983c98a79d796e078ff8d02df1bb38357a5d7f11ddb5842d6777ab090fd811e889ab1a5e92ba2644c9a9e5e353f7c9f7ce85dbf1e07001c29 - languageName: node - linkType: hard - -"postcss-reduce-initial@npm:^5.1.2": - version: 5.1.2 - resolution: "postcss-reduce-initial@npm:5.1.2" - dependencies: - browserslist: "npm:^4.21.4" - caniuse-api: "npm:^3.0.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/ddb2ce61c8d0997184f08200eafdf32b3c67e88228fee960f5e2010c32da0c1d8ea07712585bf2b3aaa15f583066401d45db2c1131527c5116ca6794ebebd865 - languageName: node - linkType: hard - -"postcss-reduce-transforms@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-reduce-transforms@npm:5.1.0" - dependencies: - postcss-value-parser: "npm:^4.2.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/caefaeb78652ad8701b94e91500e38551255e4899fa298a7357519a36cbeebae088eab4535e00f17675a1230f448c4a7077045639d496da4614a46bc41df4add - languageName: node - linkType: hard - -"postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.0.5, postcss-selector-parser@npm:^6.0.9": - version: 6.1.2 - resolution: "postcss-selector-parser@npm:6.1.2" - dependencies: - cssesc: "npm:^3.0.0" - util-deprecate: "npm:^1.0.2" - checksum: 10c0/523196a6bd8cf660bdf537ad95abd79e546d54180f9afb165a4ab3e651ac705d0f8b8ce6b3164fb9e3279ce482c5f751a69eb2d3a1e8eb0fd5e82294fb3ef13e - languageName: node - linkType: hard - -"postcss-selector-parser@npm:^7.0.0": - version: 7.1.1 - resolution: "postcss-selector-parser@npm:7.1.1" - dependencies: - cssesc: "npm:^3.0.0" - util-deprecate: "npm:^1.0.2" - checksum: 10c0/02d3b1589ddcddceed4b583b098b95a7266dacd5135f041e5d913ebb48e874fd333a36e564cc9a2ec426a464cb18db11cb192ac76247aced5eba8c951bf59507 + icss-utils: "npm:^5.0.0" + postcss-selector-parser: "npm:^7.0.0" + postcss-value-parser: "npm:^4.1.0" + peerDependencies: + postcss: ^8.1.0 + checksum: 10c0/b0b83feb2a4b61f5383979d37f23116c99bc146eba1741ca3cf1acca0e4d0dbf293ac1810a6ab4eccbe1ee76440dd0a9eb2db5b3bba4f99fc1b3ded16baa6358 languageName: node linkType: hard -"postcss-sort-media-queries@npm:^4.2.1": - version: 4.4.1 - resolution: "postcss-sort-media-queries@npm:4.4.1" +"postcss-modules-scope@npm:^3.0.0": + version: 3.2.1 + resolution: "postcss-modules-scope@npm:3.2.1" dependencies: - sort-css-media-queries: "npm:2.1.0" + postcss-selector-parser: "npm:^7.0.0" peerDependencies: - postcss: ^8.4.16 - checksum: 10c0/8bbc604daee29dc3e1f5090df972599c3c0eb08b37650e16c134a040cc1357484a48bbe03dac2977d616be1d490cde2934226fa1e6f7e52f4f5e7bf8f57e98d6 + postcss: ^8.1.0 + checksum: 10c0/bd2d81f79e3da0ef6365b8e2c78cc91469d05b58046b4601592cdeef6c4050ed8fe1478ae000a1608042fc7e692cb51fecbd2d9bce3f4eace4d32e883ffca10b languageName: node linkType: hard -"postcss-svgo@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-svgo@npm:5.1.0" +"postcss-modules-values@npm:^4.0.0": + version: 4.0.0 + resolution: "postcss-modules-values@npm:4.0.0" dependencies: - postcss-value-parser: "npm:^4.2.0" - svgo: "npm:^2.7.0" + icss-utils: "npm:^5.0.0" peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/309634a587e38fef244648bc9cd1817e12144868d24f1173d87b1edc14a4a7fca614962b2cb9d93f4801e11bd8d676083986ad40ebab4438cb84731ce1571994 + postcss: ^8.1.0 + checksum: 10c0/dd18d7631b5619fb9921b198c86847a2a075f32e0c162e0428d2647685e318c487a2566cc8cc669fc2077ef38115cde7a068e321f46fb38be3ad49646b639dbc languageName: node linkType: hard -"postcss-unique-selectors@npm:^5.1.1": - version: 5.1.1 - resolution: "postcss-unique-selectors@npm:5.1.1" +"postcss-selector-parser@npm:^7.0.0": + version: 7.1.1 + resolution: "postcss-selector-parser@npm:7.1.1" dependencies: - postcss-selector-parser: "npm:^6.0.5" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/484f6409346d6244c134c5cdcd62f4f2751b269742f95222f13d8bac5fb224471ffe04e28a354670cbe0bdc2707778ead034fc1b801b473ffcbea5436807de30 + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/02d3b1589ddcddceed4b583b098b95a7266dacd5135f041e5d913ebb48e874fd333a36e564cc9a2ec426a464cb18db11cb192ac76247aced5eba8c951bf59507 languageName: node linkType: hard @@ -19650,15 +17508,6 @@ __metadata: languageName: node linkType: hard -"postcss-zindex@npm:^5.1.0": - version: 5.1.0 - resolution: "postcss-zindex@npm:5.1.0" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/f739d3c0dec3875c770d6a101bc6ac1ccf545b0121e75b7fade911ad6ce454a2af87e6c3587c80a76a00f217e1761778f2083c2807eb78c17bfc53624b625ded - languageName: node - linkType: hard - "postcss@npm:^7.0.14, postcss@npm:^7.0.27": version: 7.0.39 resolution: "postcss@npm:7.0.39" @@ -19669,7 +17518,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.2.15, postcss@npm:^8.3.11, postcss@npm:^8.4.14, postcss@npm:^8.4.17, postcss@npm:^8.4.33": +"postcss@npm:^8.2.15": version: 8.5.6 resolution: "postcss@npm:8.5.6" dependencies: @@ -19755,16 +17604,6 @@ __metadata: languageName: node linkType: hard -"pretty-error@npm:^4.0.0": - version: 4.0.0 - resolution: "pretty-error@npm:4.0.0" - dependencies: - lodash: "npm:^4.17.20" - renderkid: "npm:^3.0.0" - checksum: 10c0/dc292c087e2857b2e7592784ab31e37a40f3fa918caa11eba51f9fb2853e1d4d6e820b219917e35f5721d833cfd20fdf4f26ae931a90fd1ad0cae2125c345138 - languageName: node - linkType: hard - "pretty-format@npm:30.4.1, pretty-format@npm:^30.4.1": version: 30.4.1 resolution: "pretty-format@npm:30.4.1" @@ -19800,29 +17639,6 @@ __metadata: languageName: node linkType: hard -"pretty-time@npm:^1.1.0": - version: 1.1.0 - resolution: "pretty-time@npm:1.1.0" - checksum: 10c0/ba9d7af19cd43838fb2b147654990949575e400dc2cc24bf71ec4a6c4033a38ba8172b1014b597680c6d4d3c075e94648b2c13a7206c5f0c90b711c7388726f3 - languageName: node - linkType: hard - -"prism-react-renderer@npm:^1.3.5": - version: 1.3.5 - resolution: "prism-react-renderer@npm:1.3.5" - peerDependencies: - react: ">=0.14.9" - checksum: 10c0/9caada97fa7325fc99484cff409a84ed947a061615851bd0aedf4fcfd4b3496e2eff4b252dbfd4465dd6ea7310134ed67d737cabf0c78b192969c3c7da383237 - languageName: node - linkType: hard - -"prismjs@npm:^1.28.0": - version: 1.30.0 - resolution: "prismjs@npm:1.30.0" - checksum: 10c0/f56205bfd58ef71ccfcbcb691fd0eb84adc96c6ff21b0b69fc6fdcf02be42d6ef972ba4aed60466310de3d67733f6a746f89f2fb79c00bf217406d465b3e8f23 - languageName: node - linkType: hard - "prismjs@npm:~1.27.0": version: 1.27.0 resolution: "prismjs@npm:1.27.0" @@ -19851,13 +17667,6 @@ __metadata: languageName: node linkType: hard -"process@npm:^0.11.10": - version: 0.11.10 - resolution: "process@npm:0.11.10" - checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 - languageName: node - linkType: hard - "progress@npm:^2.0.3": version: 2.0.3 resolution: "progress@npm:2.0.3" @@ -19903,7 +17712,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.5.10, prop-types@npm:^15.5.4, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.5.10, prop-types@npm:^15.5.4, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -19923,6 +17732,13 @@ __metadata: languageName: node linkType: hard +"property-information@npm:^7.0.0": + version: 7.2.0 + resolution: "property-information@npm:7.2.0" + checksum: 10c0/03662c8f9e1544510914c5e594ae72963f67d261027a5fdc06c4134742584fe40dd49b959470d30e757e9fb70b297d8546ab1362a040364144029926ad4e07c4 + languageName: node + linkType: hard + "protocols@npm:^1.4.0": version: 1.4.8 resolution: "protocols@npm:1.4.8" @@ -19978,7 +17794,7 @@ __metadata: languageName: node linkType: hard -"pupa@npm:^2.0.1, pupa@npm:^2.1.1": +"pupa@npm:^2.0.1": version: 2.1.1 resolution: "pupa@npm:2.1.1" dependencies: @@ -19987,13 +17803,6 @@ __metadata: languageName: node linkType: hard -"pure-color@npm:^1.2.0": - version: 1.3.0 - resolution: "pure-color@npm:1.3.0" - checksum: 10c0/50d0e088ad0349bdd508cddf7c7afbb2d14ba3c047628dbfcfddf467a98f10462caf91f3227172ada88f64afaf761c499ecba0d4053b06926f0f914769be24b9 - languageName: node - linkType: hard - "pure-rand@npm:^6.0.0": version: 6.1.0 resolution: "pure-rand@npm:6.1.0" @@ -20096,13 +17905,6 @@ __metadata: languageName: node linkType: hard -"range-parser@npm:1.2.0": - version: 1.2.0 - resolution: "range-parser@npm:1.2.0" - checksum: 10c0/c7aef4f6588eb974c475649c157f197d07437d8c6c8ff7e36280a141463fb5ab7a45918417334ebd7b665c6b8321cf31c763f7631dd5f5db9372249261b8b02a - languageName: node - linkType: hard - "range-parser@npm:^1.2.1, range-parser@npm:~1.2.1": version: 1.2.1 resolution: "range-parser@npm:1.2.1" @@ -20136,18 +17938,6 @@ __metadata: languageName: node linkType: hard -"react-base16-styling@npm:^0.6.0": - version: 0.6.0 - resolution: "react-base16-styling@npm:0.6.0" - dependencies: - base16: "npm:^1.0.0" - lodash.curry: "npm:^4.0.1" - lodash.flow: "npm:^3.3.0" - pure-color: "npm:^1.2.0" - checksum: 10c0/4887ac57b36fedc7e1ebc99ae431c5feb07d60a9150770d0ca3a59f4ae7059434ea8813ca4f915e7434d4d8d8529b9ba072ceb85041fd52ca1cd6289c57c9621 - languageName: node - linkType: hard - "react-color@npm:^2.19.3": version: 2.19.3 resolution: "react-color@npm:2.19.3" @@ -20165,38 +17955,6 @@ __metadata: languageName: node linkType: hard -"react-dev-utils@npm:^12.0.1": - version: 12.0.1 - resolution: "react-dev-utils@npm:12.0.1" - dependencies: - "@babel/code-frame": "npm:^7.16.0" - address: "npm:^1.1.2" - browserslist: "npm:^4.18.1" - chalk: "npm:^4.1.2" - cross-spawn: "npm:^7.0.3" - detect-port-alt: "npm:^1.1.6" - escape-string-regexp: "npm:^4.0.0" - filesize: "npm:^8.0.6" - find-up: "npm:^5.0.0" - fork-ts-checker-webpack-plugin: "npm:^6.5.0" - global-modules: "npm:^2.0.0" - globby: "npm:^11.0.4" - gzip-size: "npm:^6.0.0" - immer: "npm:^9.0.7" - is-root: "npm:^2.1.0" - loader-utils: "npm:^3.2.0" - open: "npm:^8.4.0" - pkg-up: "npm:^3.1.0" - prompts: "npm:^2.4.2" - react-error-overlay: "npm:^6.0.11" - recursive-readdir: "npm:^2.2.2" - shell-quote: "npm:^1.7.3" - strip-ansi: "npm:^6.0.1" - text-table: "npm:^0.2.0" - checksum: 10c0/94bc4ee5014290ca47a025e53ab2205c5dc0299670724d46a0b1bacbdd48904827b5ae410842d0a3a92481509097ae032e4a9dc7ca70db437c726eaba6411e82 - languageName: node - linkType: hard - "react-devtools-core@npm:^6.1.5": version: 6.1.5 resolution: "react-devtools-core@npm:6.1.5" @@ -20227,19 +17985,6 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:17.0.2, react-dom@npm:~17.0.1": - version: 17.0.2 - resolution: "react-dom@npm:17.0.2" - dependencies: - loose-envify: "npm:^1.1.0" - object-assign: "npm:^4.1.1" - scheduler: "npm:^0.20.2" - peerDependencies: - react: 17.0.2 - checksum: 10c0/51abbcb72450fe527ebf978c3bc989ba266630faaa53f47a2fae5392369729e8de62b2e4683598cbe651ea7873cd34ec7d5127e2f50bf4bfe6bd0c3ad9bddcb0 - languageName: node - linkType: hard - "react-dom@npm:19.2.3": version: 19.2.3 resolution: "react-dom@npm:19.2.3" @@ -20251,55 +17996,47 @@ __metadata: languageName: node linkType: hard -"react-error-overlay@npm:^6.0.11": - version: 6.1.0 - resolution: "react-error-overlay@npm:6.1.0" - checksum: 10c0/2b52308b9e489dfaa25df85c7ed5c200371bc214245161a0833b729f9c6b1a2e591e1d1e07c3d3859ffa19b84a386c219d93df9ba13d819be34c20f40e71a555 - languageName: node - linkType: hard - -"react-fast-compare@npm:^3.2.0, react-fast-compare@npm:^3.2.2": - version: 3.2.2 - resolution: "react-fast-compare@npm:3.2.2" - checksum: 10c0/0bbd2f3eb41ab2ff7380daaa55105db698d965c396df73e6874831dbafec8c4b5b08ba36ff09df01526caa3c61595247e3269558c284e37646241cba2b90a367 +"react-dom@npm:19.2.6": + version: 19.2.6 + resolution: "react-dom@npm:19.2.6" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.6 + checksum: 10c0/dbf2aef67857c03a612bc9316a4562e5066f43fa04bf28f7f0734963fc1350da2c9f8eb973930655453281079f30cc8222bab383dbec4b5097084e2c081e3334 languageName: node linkType: hard -"react-freeze@npm:^1.0.0": - version: 1.0.4 - resolution: "react-freeze@npm:1.0.4" +"react-dom@npm:^19.2.6": + version: 19.2.7 + resolution: "react-dom@npm:19.2.7" + dependencies: + scheduler: "npm:^0.27.0" peerDependencies: - react: ">=17.0.0" - checksum: 10c0/8f51257c261bfefff86f618e958683536248f708019632d309ee5ebdd52f25d3c130660d06fb6f0f4fdef79f00f8ec7177233a872c2321f7d46b7e77ccc522a1 + react: ^19.2.7 + checksum: 10c0/970ff600f6e80d47d39e2f226f12f226173b3cba3382efc97c5f0cd663de9af38c7a4c11c213fb936094faeac83060d660247accaa96b752180d5b951b9cfecb languageName: node linkType: hard -"react-helmet-async@npm:*": - version: 2.0.5 - resolution: "react-helmet-async@npm:2.0.5" +"react-dom@npm:~17.0.1": + version: 17.0.2 + resolution: "react-dom@npm:17.0.2" dependencies: - invariant: "npm:^2.2.4" - react-fast-compare: "npm:^3.2.2" - shallowequal: "npm:^1.1.0" + loose-envify: "npm:^1.1.0" + object-assign: "npm:^4.1.1" + scheduler: "npm:^0.20.2" peerDependencies: - react: ^16.6.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/f390ea8bf13c2681850e5f8eb5b73d8613f407c245a5fd23e9db9b2cc14a3700dd1ce992d3966632886d1d613083294c2aeee009193f49dfa7d145d9f13ea2b0 + react: 17.0.2 + checksum: 10c0/51abbcb72450fe527ebf978c3bc989ba266630faaa53f47a2fae5392369729e8de62b2e4683598cbe651ea7873cd34ec7d5127e2f50bf4bfe6bd0c3ad9bddcb0 languageName: node linkType: hard -"react-helmet-async@npm:^1.3.0": - version: 1.3.0 - resolution: "react-helmet-async@npm:1.3.0" - dependencies: - "@babel/runtime": "npm:^7.12.5" - invariant: "npm:^2.2.4" - prop-types: "npm:^15.7.2" - react-fast-compare: "npm:^3.2.0" - shallowequal: "npm:^1.1.0" +"react-freeze@npm:^1.0.0": + version: 1.0.4 + resolution: "react-freeze@npm:1.0.4" peerDependencies: - react: ^16.6.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/8f3e6d26beff61d2ed18f7b41561df3e4d83a7582914c7196aa65158c7f3cce939276547d7a0b8987952d9d44131406df74efba02d1f8fa8a3940b49e6ced70b + react: ">=17.0.0" + checksum: 10c0/8f51257c261bfefff86f618e958683536248f708019632d309ee5ebdd52f25d3c130660d06fb6f0f4fdef79f00f8ec7177233a872c2321f7d46b7e77ccc522a1 languageName: node linkType: hard @@ -20317,7 +18054,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0, react-is@npm:^16.8.3, react-is@npm:^16.8.4": +"react-is@npm:^16.13.1, react-is@npm:^16.7.0, react-is@npm:^16.8.3, react-is@npm:^16.8.4": version: 16.13.1 resolution: "react-is@npm:16.13.1" checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1 @@ -20331,37 +18068,10 @@ __metadata: languageName: node linkType: hard -"react-json-view@npm:^1.21.3": - version: 1.21.3 - resolution: "react-json-view@npm:1.21.3" - dependencies: - flux: "npm:^4.0.1" - react-base16-styling: "npm:^0.6.0" - react-lifecycles-compat: "npm:^3.0.4" - react-textarea-autosize: "npm:^8.3.2" - peerDependencies: - react: ^17.0.0 || ^16.3.0 || ^15.5.4 - react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4 - checksum: 10c0/f41b38e599f148cf922f60390e56bb821f17a091373b08310fd82ebc526428683011751aa023687041481a46b20aeb1c47f660979d43db77674486aec9dc1d3f - languageName: node - linkType: hard - -"react-lifecycles-compat@npm:^3.0.4": - version: 3.0.4 - resolution: "react-lifecycles-compat@npm:3.0.4" - checksum: 10c0/1d0df3c85af79df720524780f00c064d53a9dd1899d785eddb7264b378026979acbddb58a4b7e06e7d0d12aa1494fd5754562ee55d32907b15601068dae82c27 - languageName: node - linkType: hard - -"react-loadable-ssr-addon-v5-slorber@npm:^1.0.1": - version: 1.0.1 - resolution: "react-loadable-ssr-addon-v5-slorber@npm:1.0.1" - dependencies: - "@babel/runtime": "npm:^7.10.3" - peerDependencies: - react-loadable: "*" - webpack: ">=4.41.1 || 5.x" - checksum: 10c0/7b0645f66adec56646f985ba8094c66a1c0a4627d96ad80eea32431d773ef1f79aa47d3247a8f21db3b064a0c6091653c5b5d3483b7046722eb64e55bffe635c +"react-lazy-with-preload@npm:^2.2.1": + version: 2.2.1 + resolution: "react-lazy-with-preload@npm:2.2.1" + checksum: 10c0/57c36baedc24a2c8fa148681dc7c5123ac183542964063b4a0433856a06d4edead27deb893012a43f75905af51a479b4409e688b97be3ef70cffff926954f6d2 languageName: node linkType: hard @@ -20573,16 +18283,6 @@ __metadata: languageName: node linkType: hard -"react-native-safe-area-context@npm:^4.5.0": - version: 4.14.1 - resolution: "react-native-safe-area-context@npm:4.14.1" - peerDependencies: - react: "*" - react-native: "*" - checksum: 10c0/c34c6fe9002a89c80ddb206fc68db99f1e432e19c7cf5aaa93b430584051b7e7caf889093224029437dc5b66fe434b09e9950d755aa40b58e1e3a1f976c2fec1 - languageName: node - linkType: hard - "react-native-screens@npm:4.25.1": version: 4.25.1 resolution: "react-native-screens@npm:4.25.1" @@ -20596,25 +18296,7 @@ __metadata: languageName: node linkType: hard -"react-native-web@npm:^0.18.12": - version: 0.18.12 - resolution: "react-native-web@npm:0.18.12" - dependencies: - "@babel/runtime": "npm:^7.18.6" - create-react-class: "npm:^15.7.0" - fbjs: "npm:^3.0.4" - inline-style-prefixer: "npm:^6.0.1" - normalize-css-color: "npm:^1.0.2" - postcss-value-parser: "npm:^4.2.0" - styleq: "npm:^0.1.2" - peerDependencies: - react: ^17.0.2 || ^18.0.0 - react-dom: ^17.0.2 || ^18.0.0 - checksum: 10c0/845bbe1061e0c80cc998a75345ca4ebee4972fb39c8344fb96faec1c8961c38c015df047d9c3a7ebab2f74151aba9fe930b0da49c55b710549ba93bf577a1696 - languageName: node - linkType: hard - -"react-native-web@npm:^0.21.0": +"react-native-web@npm:^0.21.0, react-native-web@npm:^0.21.2": version: 0.21.2 resolution: "react-native-web@npm:0.21.2" dependencies: @@ -20708,7 +18390,7 @@ __metadata: languageName: node linkType: hard -"react-reconciler@npm:~0.33.0": +"react-reconciler@npm:0.33.0, react-reconciler@npm:~0.33.0": version: 0.33.0 resolution: "react-reconciler@npm:0.33.0" dependencies: @@ -20726,68 +18408,74 @@ __metadata: languageName: node linkType: hard -"react-router-config@npm:^5.1.1": - version: 5.1.1 - resolution: "react-router-config@npm:5.1.1" - dependencies: - "@babel/runtime": "npm:^7.1.2" - peerDependencies: - react: ">=15" - react-router: ">=5" - checksum: 10c0/1f8f4e55ca68b7b012293e663eb0ee4d670a3df929b78928f713ef98cd9d62c7f5c30a098d6668e64bbb11c7d6bb24e9e6b9c985a8b82465a1858dc7ba663f2b +"react-refresh@npm:^0.18.0": + version: 0.18.0 + resolution: "react-refresh@npm:0.18.0" + checksum: 10c0/34a262f7fd803433a534f50deb27a148112a81adcae440c7d1cbae7ef14d21ea8f2b3d783e858cb7698968183b77755a38b4d4b5b1d79b4f4689c2f6d358fff2 languageName: node linkType: hard -"react-router-dom@npm:^5.3.3": - version: 5.3.4 - resolution: "react-router-dom@npm:5.3.4" +"react-render-to-markdown@npm:19.1.0": + version: 19.1.0 + resolution: "react-render-to-markdown@npm:19.1.0" dependencies: - "@babel/runtime": "npm:^7.12.13" - history: "npm:^4.9.0" - loose-envify: "npm:^1.3.1" - prop-types: "npm:^15.6.2" - react-router: "npm:5.3.4" - tiny-invariant: "npm:^1.0.2" - tiny-warning: "npm:^1.0.0" + react-reconciler: "npm:0.33.0" peerDependencies: - react: ">=15" - checksum: 10c0/f04f727e2ed2e9d1d3830af02cc61690ff67b1524c0d18690582bfba0f4d14142ccc88fb6da6befad644fddf086f5ae4c2eb7048c67da8a0b0929c19426421b0 + react: ">=19" + checksum: 10c0/f24f733f20ddeaa43c3564b5e8fd60ffa851c026d5b5469569503b5fbef43da44d959d2ffdf64d0c11b2b26db673df0d7484d701775946c10d8996787d722d22 languageName: node linkType: hard -"react-router@npm:5.3.4, react-router@npm:^5.3.3": - version: 5.3.4 - resolution: "react-router@npm:5.3.4" +"react-router-dom@npm:^7.15.1": + version: 7.17.0 + resolution: "react-router-dom@npm:7.17.0" dependencies: - "@babel/runtime": "npm:^7.12.13" - history: "npm:^4.9.0" - hoist-non-react-statics: "npm:^3.1.0" - loose-envify: "npm:^1.3.1" - path-to-regexp: "npm:^1.7.0" - prop-types: "npm:^15.6.2" - react-is: "npm:^16.6.0" - tiny-invariant: "npm:^1.0.2" - tiny-warning: "npm:^1.0.0" + react-router: "npm:7.17.0" peerDependencies: - react: ">=15" - checksum: 10c0/e15c00dfef199249b4c6e6d98e5e76cc352ce66f3270f13df37cc069ddf7c05e43281e8c308fc407e4435d72924373baef1d2890e0f6b0b1eb423cf47315a053 + react: ">=18" + react-dom: ">=18" + checksum: 10c0/60fc4022bccf2fb17b0d8ae8e7c4bda866ca9bf2d45edf0bcbefc854e97fa8971a589b6b77b984a19abecb718faffde357c829d9a22a7913790d608cc7433714 languageName: node linkType: hard -"react-textarea-autosize@npm:^8.3.2": - version: 8.5.9 - resolution: "react-textarea-autosize@npm:8.5.9" +"react-router@npm:7.17.0": + version: 7.17.0 + resolution: "react-router@npm:7.17.0" dependencies: - "@babel/runtime": "npm:^7.20.13" - use-composed-ref: "npm:^1.3.0" - use-latest: "npm:^1.2.1" + cookie: "npm:^1.0.1" + set-cookie-parser: "npm:^2.6.0" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/3a924db59259a6e3b834dcddc12a8661b43dcdaa1b43c41c732e0548bb2761e9a53dbc6db4e0d9e237728b4869e42c25e5417408b0391aec290c90874733a09f + react: ">=18" + react-dom: ">=18" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10c0/dfa9b0182edba6fab14ef5ac297b292068e71e05bf7e07c6b45fe073d77002089e8e164542364232a8bbb0c7d3ec47b57b78db3984c4383e12308f2d31ad404a + languageName: node + linkType: hard + +"react@npm:19.2.3": + version: 19.2.3 + resolution: "react@npm:19.2.3" + checksum: 10c0/094220b3ba3a76c1b668f972ace1dd15509b157aead1b40391d1c8e657e720c201d9719537375eff08f5e0514748c0319063392a6f000e31303aafc4471f1436 + languageName: node + linkType: hard + +"react@npm:19.2.6": + version: 19.2.6 + resolution: "react@npm:19.2.6" + checksum: 10c0/66afde33b9a9ee87b1e1cae39d8e7e040d1262e719524fd70660c4d4ce79929c532ac19fc3df5a911edaf02768fdf2c49de4ede1ba99bc6aad72796e0e26e798 + languageName: node + linkType: hard + +"react@npm:^19.2.6": + version: 19.2.7 + resolution: "react@npm:19.2.7" + checksum: 10c0/0bd0e2f1bbd4ba97561c6597bf8a5fec05e6476fe61e165c1065598d16668efc6715205599c94d3ddd49d36cb0f21cbf1b9bcc18ee840b805ce222c3e8d558ac languageName: node linkType: hard -"react@npm:17.0.2, react@npm:~17.0.1": +"react@npm:~17.0.1": version: 17.0.2 resolution: "react@npm:17.0.2" dependencies: @@ -20797,13 +18485,6 @@ __metadata: languageName: node linkType: hard -"react@npm:19.2.3": - version: 19.2.3 - resolution: "react@npm:19.2.3" - checksum: 10c0/094220b3ba3a76c1b668f972ace1dd15509b157aead1b40391d1c8e657e720c201d9719537375eff08f5e0514748c0319063392a6f000e31303aafc4471f1436 - languageName: node - linkType: hard - "reactcss@npm:^1.2.0": version: 1.2.3 resolution: "reactcss@npm:1.2.3" @@ -20857,7 +18538,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.0.6, readable-stream@npm:^3.4.0": +"readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.4.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -20883,22 +18564,6 @@ __metadata: languageName: node linkType: hard -"readdirp@npm:~3.6.0": - version: 3.6.0 - resolution: "readdirp@npm:3.6.0" - dependencies: - picomatch: "npm:^2.2.1" - checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b - languageName: node - linkType: hard - -"reading-time@npm:^1.5.0": - version: 1.5.0 - resolution: "reading-time@npm:1.5.0" - checksum: 10c0/0f730852fd4fb99e5f78c5b0cf36ab8c3fa15db96f87d9563843f6fd07a47864273ade539ebb184b785b728cde81a70283aa2d9b80cba5ca03b81868be03cabc - languageName: node - linkType: hard - "recast@npm:^0.20.4": version: 0.20.5 resolution: "recast@npm:0.20.5" @@ -20920,12 +18585,53 @@ __metadata: languageName: node linkType: hard -"recursive-readdir@npm:^2.2.2": - version: 2.2.3 - resolution: "recursive-readdir@npm:2.2.3" +"recma-build-jsx@npm:^1.0.0": + version: 1.0.0 + resolution: "recma-build-jsx@npm:1.0.0" + dependencies: + "@types/estree": "npm:^1.0.0" + estree-util-build-jsx: "npm:^3.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/ca30f5163887b44c74682355da2625f7b49f33267699d22247913e513e043650cbdd6a7497cf13c60f09ad9e7bc2bd35bd20853672773c19188569814b56bb04 + languageName: node + linkType: hard + +"recma-jsx@npm:^1.0.0": + version: 1.0.1 + resolution: "recma-jsx@npm:1.0.1" + dependencies: + acorn-jsx: "npm:^5.0.0" + estree-util-to-js: "npm:^2.0.0" + recma-parse: "npm:^1.0.0" + recma-stringify: "npm:^1.0.0" + unified: "npm:^11.0.0" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10c0/9921b1270581ff133b94678868e665ba0fb6285ee60a6936106bac4899196c2ffb02dde894d9bc088fbf3deacb3e2426a3452e72066bf1203cbefebd7809d93f + languageName: node + linkType: hard + +"recma-parse@npm:^1.0.0": + version: 1.0.0 + resolution: "recma-parse@npm:1.0.0" + dependencies: + "@types/estree": "npm:^1.0.0" + esast-util-from-js: "npm:^2.0.0" + unified: "npm:^11.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/37c0990859a562d082e02d475ca5f4c8ef0840d285270f6699fe888cbb06260f97eb098585eda4aae416182c207fd19cf05e4f0b2dcf55cbf81dde4406d95545 + languageName: node + linkType: hard + +"recma-stringify@npm:^1.0.0": + version: 1.0.0 + resolution: "recma-stringify@npm:1.0.0" dependencies: - minimatch: "npm:^3.0.5" - checksum: 10c0/d0238f137b03af9cd645e1e0b40ae78b6cda13846e3ca57f626fcb58a66c79ae018a10e926b13b3a460f1285acc946a4e512ea8daa2e35df4b76a105709930d1 + "@types/estree": "npm:^1.0.0" + estree-util-to-js: "npm:^2.0.0" + unified: "npm:^11.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/c2ed4c0e8cf8a09aedcd47c5d016d47f6e1ff6c2d4b220e2abaf1b77713bf404756af2ea3ea7999aec5862e8825aff035edceb370c7fd8603a7e9da03bd6987e languageName: node linkType: hard @@ -21038,6 +18744,31 @@ __metadata: languageName: node linkType: hard +"regex-recursion@npm:^6.0.2": + version: 6.0.2 + resolution: "regex-recursion@npm:6.0.2" + dependencies: + regex-utilities: "npm:^2.3.0" + checksum: 10c0/68e8b6889680e904b75d7f26edaf70a1a4dc1087406bff53face4c2929d918fd77c72223843fe816ac8ed9964f96b4160650e8d5909e26a998c6e9de324dadb1 + languageName: node + linkType: hard + +"regex-utilities@npm:^2.3.0": + version: 2.3.0 + resolution: "regex-utilities@npm:2.3.0" + checksum: 10c0/78c550a80a0af75223244fff006743922591bd8f61d91fef7c86b9b56cf9bbf8ee5d7adb6d8991b5e304c57c90103fc4818cf1e357b11c6c669b782839bd7893 + languageName: node + linkType: hard + +"regex@npm:^6.1.0": + version: 6.1.0 + resolution: "regex@npm:6.1.0" + dependencies: + regex-utilities: "npm:^2.3.0" + checksum: 10c0/6e0ee2a1c17d5a66dc1120dfc51899dedf6677857e83a0df4d5a822ebb8645a54a079772efc1ade382b67aad35e4e22b5bd2d33c05ed28b0e000f8f57eb0aec1 + languageName: node + linkType: hard + "regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": version: 1.5.4 resolution: "regexp.prototype.flags@npm:1.5.4" @@ -21102,6 +18833,20 @@ __metadata: languageName: node linkType: hard +"rehype-external-links@npm:^3.0.0": + version: 3.0.0 + resolution: "rehype-external-links@npm:3.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + "@ungap/structured-clone": "npm:^1.0.0" + hast-util-is-element: "npm:^3.0.0" + is-absolute-url: "npm:^4.0.0" + space-separated-tokens: "npm:^2.0.0" + unist-util-visit: "npm:^5.0.0" + checksum: 10c0/486b5db73d8fe72611d62b4eb0b56ec71025ea32bba764ad54473f714ca627be75e057ac29243763f85a77c3810f31727ce3e03c975b3803c1c98643d038e9ae + languageName: node + linkType: hard + "rehype-parse@npm:^7.0.0": version: 7.0.1 resolution: "rehype-parse@npm:7.0.1" @@ -21112,6 +18857,28 @@ __metadata: languageName: node linkType: hard +"rehype-raw@npm:^7.0.0": + version: 7.0.0 + resolution: "rehype-raw@npm:7.0.0" + dependencies: + "@types/hast": "npm:^3.0.0" + hast-util-raw: "npm:^9.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/1435b4b6640a5bc3abe3b2133885c4dbff5ef2190ef9cfe09d6a63f74dd7d7ffd0cede70603278560ccf1acbfb9da9faae4b68065a28bc5aa88ad18e40f32d52 + languageName: node + linkType: hard + +"rehype-recma@npm:^1.0.0": + version: 1.0.0 + resolution: "rehype-recma@npm:1.0.0" + dependencies: + "@types/estree": "npm:^1.0.0" + "@types/hast": "npm:^3.0.0" + hast-util-to-estree: "npm:^3.0.0" + checksum: 10c0/be60d7433a7f788a14f41da3e93ba9d9272c908ddef47757026cc4bbcc912f6301d56810349adf876d294a8d048626a0dbf6988aaa574afbfc29eac1ddc1eb74 + languageName: node + linkType: hard + "rehype-stringify@npm:^8.0.0": version: 8.0.0 resolution: "rehype-stringify@npm:8.0.0" @@ -21132,13 +18899,6 @@ __metadata: languageName: node linkType: hard -"relateurl@npm:^0.2.7": - version: 0.2.7 - resolution: "relateurl@npm:0.2.7" - checksum: 10c0/c248b4e3b32474f116a804b537fa6343d731b80056fb506dffd91e737eef4cac6be47a65aae39b522b0db9d0b1011d1a12e288d82a109ecd94a5299d82f6573a - languageName: node - linkType: hard - "release-it@npm:^13.4.0, release-it@npm:^13.5.6": version: 13.7.2 resolution: "release-it@npm:13.7.2" @@ -21180,61 +18940,71 @@ __metadata: languageName: node linkType: hard -"remark-emoji@npm:^2.2.0": - version: 2.2.0 - resolution: "remark-emoji@npm:2.2.0" +"remark-cjk-friendly-gfm-strikethrough@npm:^2.0.1": + version: 2.1.0 + resolution: "remark-cjk-friendly-gfm-strikethrough@npm:2.1.0" dependencies: - emoticon: "npm:^3.2.0" - node-emoji: "npm:^1.10.0" - unist-util-visit: "npm:^2.0.3" - checksum: 10c0/f7b98c7879ebf289e277e8a2a5f6c75da6a0447efde56a347ad6bb67084da6de8605b3ae9eb9244f02ef0c47755e2c5288222ef12a93c2ba1dec3ba02b0342f5 + mdast-util-to-markdown-cjk-friendly-gfm-strikethrough: "npm:1.0.0" + micromark-extension-cjk-friendly-gfm-strikethrough: "npm:2.0.1" + peerDependencies: + "@types/mdast": ^4.0.0 + unified: ^11.0.0 + peerDependenciesMeta: + "@types/mdast": + optional: true + checksum: 10c0/57d0df3428d53bfbaaffb9ea02a7a47eb073841f13a952f9ce228685d56211749c8a9b10ee6e4b060ae1ed671a4eb4eb715f7877fce64cc7dc3b1f27c324ccd6 + languageName: node + linkType: hard + +"remark-cjk-friendly@npm:^2.0.1": + version: 2.1.0 + resolution: "remark-cjk-friendly@npm:2.1.0" + dependencies: + mdast-util-to-markdown-cjk-friendly: "npm:1.0.0" + micromark-extension-cjk-friendly: "npm:2.0.1" + peerDependencies: + "@types/mdast": ^4.0.0 + unified: ^11.0.0 + peerDependenciesMeta: + "@types/mdast": + optional: true + checksum: 10c0/2dcab78899e56674c459cc2bbd1e208f7d4b45abd3ffd29aa71238c8fa536d453121abb7132b4ee22f1157a75e799be317dcff2d0d224a1c5f0d24b3f52d816d languageName: node linkType: hard -"remark-footnotes@npm:2.0.0": - version: 2.0.0 - resolution: "remark-footnotes@npm:2.0.0" - checksum: 10c0/45b55b3440b74bfeed11fba5ed6b31f2fd35ab4e9ba169061b76a19f5ff4d16d851c9f3c423c7fa54eb0fa5e6043b89098cb9478e9b5b417cf4bdef5571b0236 +"remark-gfm@npm:^4.0.1": + version: 4.0.1 + resolution: "remark-gfm@npm:4.0.1" + dependencies: + "@types/mdast": "npm:^4.0.0" + mdast-util-gfm: "npm:^3.0.0" + micromark-extension-gfm: "npm:^3.0.0" + remark-parse: "npm:^11.0.0" + remark-stringify: "npm:^11.0.0" + unified: "npm:^11.0.0" + checksum: 10c0/427ecc6af3e76222662061a5f670a3e4e33ec5fffe2cabf04034da6a3f9a1bda1fc023e838a636385ba314e66e2bebbf017ca61ebea357eb0f5200fe0625a4b7 languageName: node linkType: hard -"remark-mdx@npm:1.6.22": - version: 1.6.22 - resolution: "remark-mdx@npm:1.6.22" +"remark-mdx@npm:^3.0.0, remark-mdx@npm:^3.1.1": + version: 3.1.1 + resolution: "remark-mdx@npm:3.1.1" dependencies: - "@babel/core": "npm:7.12.9" - "@babel/helper-plugin-utils": "npm:7.10.4" - "@babel/plugin-proposal-object-rest-spread": "npm:7.12.1" - "@babel/plugin-syntax-jsx": "npm:7.12.1" - "@mdx-js/util": "npm:1.6.22" - is-alphabetical: "npm:1.0.4" - remark-parse: "npm:8.0.3" - unified: "npm:9.2.0" - checksum: 10c0/3a964048e58cba7848d59fc920baa330a9b7f619fedb44d4d7985d84875eba8d92e0d0dd0617e28326c6086e21ef441664748526a2517a42555d44c648453b0a + mdast-util-mdx: "npm:^3.0.0" + micromark-extension-mdxjs: "npm:^3.0.0" + checksum: 10c0/3e5585d4c2448d8ac7548b1d148f04b89251ff47fbfc80be1428cecec2fc2530abe30a5da53bb031283f8a78933259df6120c1cd4cc7cc1d43978d508798ba88 languageName: node linkType: hard -"remark-parse@npm:8.0.3": - version: 8.0.3 - resolution: "remark-parse@npm:8.0.3" +"remark-parse@npm:^11.0.0": + version: 11.0.0 + resolution: "remark-parse@npm:11.0.0" dependencies: - ccount: "npm:^1.0.0" - collapse-white-space: "npm:^1.0.2" - is-alphabetical: "npm:^1.0.0" - is-decimal: "npm:^1.0.0" - is-whitespace-character: "npm:^1.0.0" - is-word-character: "npm:^1.0.0" - markdown-escapes: "npm:^1.0.0" - parse-entities: "npm:^2.0.0" - repeat-string: "npm:^1.5.4" - state-toggle: "npm:^1.0.0" - trim: "npm:0.0.1" - trim-trailing-lines: "npm:^1.0.0" - unherit: "npm:^1.0.4" - unist-util-remove-position: "npm:^2.0.0" - vfile-location: "npm:^3.0.0" - xtend: "npm:^4.0.1" - checksum: 10c0/cbb859e2585864942823ce4d23a1b1514168a066ba91d47ca09ff45a5563b81bf17160c182ac7efed718712291c35a117db89b6ce603d04a845497ae7041c185 + "@types/mdast": "npm:^4.0.0" + mdast-util-from-markdown: "npm:^2.0.0" + micromark-util-types: "npm:^2.0.0" + unified: "npm:^11.0.0" + checksum: 10c0/6eed15ddb8680eca93e04fcb2d1b8db65a743dcc0023f5007265dda558b09db595a087f622062ccad2630953cd5cddc1055ce491d25a81f3317c858348a8dd38 languageName: node linkType: hard @@ -21261,12 +19031,16 @@ __metadata: languageName: node linkType: hard -"remark-squeeze-paragraphs@npm:4.0.0": - version: 4.0.0 - resolution: "remark-squeeze-paragraphs@npm:4.0.0" +"remark-rehype@npm:^11.0.0": + version: 11.1.2 + resolution: "remark-rehype@npm:11.1.2" dependencies: - mdast-squeeze-paragraphs: "npm:^4.0.0" - checksum: 10c0/61b39acfde3bebb1e9364a6991957f83ab0d878c0fd1de0e86e9bf9e060574cefb7a76057d64e7422e2a2bcf6e3c54635a4ae43f00b3dda38812ae4b6f4342f4 + "@types/hast": "npm:^3.0.0" + "@types/mdast": "npm:^4.0.0" + mdast-util-to-hast: "npm:^13.0.0" + unified: "npm:^11.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/f9eccacfb596d9605581dc05bfad28635d6ded5dd0a18e88af5fd4df0d3fcf9612e1501d4513bc2164d833cfe9636dab20400080b09e53f155c6e1442a1231fb languageName: node linkType: hard @@ -21279,6 +19053,17 @@ __metadata: languageName: node linkType: hard +"remark-stringify@npm:^11.0.0": + version: 11.0.0 + resolution: "remark-stringify@npm:11.0.0" + dependencies: + "@types/mdast": "npm:^4.0.0" + mdast-util-to-markdown: "npm:^2.0.0" + unified: "npm:^11.0.0" + checksum: 10c0/0cdb37ce1217578f6f847c7ec9f50cbab35df5b9e3903d543e74b405404e67c07defcb23cd260a567b41b769400f6de03c2c3d9cd6ae7a6707d5c8d89ead489f + languageName: node + linkType: hard + "remove-trailing-separator@npm:^1.0.1": version: 1.1.0 resolution: "remove-trailing-separator@npm:1.1.0" @@ -21286,19 +19071,6 @@ __metadata: languageName: node linkType: hard -"renderkid@npm:^3.0.0": - version: 3.0.0 - resolution: "renderkid@npm:3.0.0" - dependencies: - css-select: "npm:^4.1.3" - dom-converter: "npm:^0.2.0" - htmlparser2: "npm:^6.1.0" - lodash: "npm:^4.17.21" - strip-ansi: "npm:^6.0.1" - checksum: 10c0/24a9fae4cc50e731d059742d1b3eec163dc9e3872b12010d120c3fcbd622765d9cda41f79a1bbb4bf63c1d3442f18a08f6e1642cb5d7ebf092a0ce3f7a3bd143 - languageName: node - linkType: hard - "repeat-element@npm:^1.1.2": version: 1.1.4 resolution: "repeat-element@npm:1.1.4" @@ -21334,13 +19106,6 @@ __metadata: languageName: node linkType: hard -"require-like@npm:>= 0.1.1": - version: 0.1.2 - resolution: "require-like@npm:0.1.2" - checksum: 10c0/9035ff6c4000a56ede6fc51dd5c56541fafa5a7dddc9b1c3a5f9148d95ee21c603c9bf5c6e37b19fc7de13d9294260842d8590b2ffd6c7c773e78603d1af8050 - languageName: node - linkType: hard - "require-main-filename@npm:^2.0.0": version: 2.0.0 resolution: "require-main-filename@npm:2.0.0" @@ -21355,13 +19120,6 @@ __metadata: languageName: node linkType: hard -"requires-port@npm:^1.0.0": - version: 1.0.0 - resolution: "requires-port@npm:1.0.0" - checksum: 10c0/b2bfdd09db16c082c4326e573a82c0771daaf7b53b9ce8ad60ea46aa6e30aaf475fe9b164800b89f93b748d2c234d8abff945d2551ba47bf5698e04cd7713267 - languageName: node - linkType: hard - "resolve-alpn@npm:^1.0.0": version: 1.2.1 resolution: "resolve-alpn@npm:1.2.1" @@ -21408,13 +19166,6 @@ __metadata: languageName: node linkType: hard -"resolve-pathname@npm:^3.0.0": - version: 3.0.0 - resolution: "resolve-pathname@npm:3.0.0" - checksum: 10c0/c6ec49b670dc35b9a303c47fa83ba9348a71e92d64a4c4bb85e1b659a29b407aa1ac1cb14a9b5b502982132ca77482bd80534bca147439d66880d35a137fe723 - languageName: node - linkType: hard - "resolve-pkg@npm:2.0.0": version: 2.0.0 resolution: "resolve-pkg@npm:2.0.0" @@ -21445,7 +19196,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.22.10, resolve@npm:^1.22.4, resolve@npm:^1.3.2": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.22.10, resolve@npm:^1.22.4": version: 1.22.11 resolution: "resolve@npm:1.22.11" dependencies: @@ -21485,7 +19236,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.10#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A^1.3.2#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.10#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": version: 1.22.11 resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" dependencies: @@ -21577,13 +19328,6 @@ __metadata: languageName: node linkType: hard -"retry@npm:^0.13.1": - version: 0.13.1 - resolution: "retry@npm:0.13.1" - checksum: 10c0/9ae822ee19db2163497e074ea919780b1efa00431d197c7afdb950e42bf109196774b92a49fc9821f0b8b328a98eea6017410bfc5e8a0fc19c85c6d11adb3772 - languageName: node - linkType: hard - "reusify@npm:^1.0.4": version: 1.1.0 resolution: "reusify@npm:1.1.0" @@ -21591,7 +19335,7 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^2.5.2, rimraf@npm:^2.6.3": +"rimraf@npm:^2.5.2": version: 2.7.1 resolution: "rimraf@npm:2.7.1" dependencies: @@ -21622,6 +19366,18 @@ __metadata: languageName: node linkType: hard +"rsbuild-plugin-open-graph@npm:^1.1.2": + version: 1.1.3 + resolution: "rsbuild-plugin-open-graph@npm:1.1.3" + peerDependencies: + "@rsbuild/core": ^1.0.0 || ^2.0.0-0 + peerDependenciesMeta: + "@rsbuild/core": + optional: true + checksum: 10c0/612f2401133ae31797f8fca623cf6317abbcf2ff8862fa13667596f4045ba46c3c6d2b9113ab9fe512b091ac5b6de50b309f991ac11b974ea283e3deb2e3d6c0 + languageName: node + linkType: hard + "rsvp@npm:^4.8.4": version: 4.8.5 resolution: "rsvp@npm:4.8.5" @@ -21629,27 +19385,6 @@ __metadata: languageName: node linkType: hard -"rtl-detect@npm:^1.0.4": - version: 1.1.2 - resolution: "rtl-detect@npm:1.1.2" - checksum: 10c0/1b92888aafca1593314f837e83fdf02eb208faae3e713ab87c176804728efd3b1980d53b64f65f1fa593348087e852c5cd729b7b9372950f6e9b7be489afc0ca - languageName: node - linkType: hard - -"rtlcss@npm:^3.5.0": - version: 3.5.0 - resolution: "rtlcss@npm:3.5.0" - dependencies: - find-up: "npm:^5.0.0" - picocolors: "npm:^1.0.0" - postcss: "npm:^8.3.11" - strip-json-comments: "npm:^3.1.1" - bin: - rtlcss: bin/rtlcss.js - checksum: 10c0/c50f61bf7315be5677a75464e7457661687e321f714cae51be8b6f7b7ab89c4203b25fae15951eceb79f6d551eae65e42eabde55087fd5aa07ad1e05698793d0 - languageName: node - linkType: hard - "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -21675,15 +19410,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.5.4": - version: 7.8.2 - resolution: "rxjs@npm:7.8.2" - dependencies: - tslib: "npm:^2.1.0" - checksum: 10c0/1fcd33d2066ada98ba8f21fcbbcaee9f0b271de1d38dc7f4e256bfbc6ffcdde68c8bfb69093de7eeb46f24b1fb820620bf0223706cff26b4ab99a7ff7b2e2c45 - languageName: node - linkType: hard - "safe-array-concat@npm:^1.1.3": version: 1.1.3 resolution: "safe-array-concat@npm:1.1.3" @@ -21697,7 +19423,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 @@ -21779,7 +19505,7 @@ __metadata: languageName: node linkType: hard -"sax@npm:>=0.6.0, sax@npm:^1.2.4": +"sax@npm:>=0.6.0": version: 1.4.3 resolution: "sax@npm:1.4.3" checksum: 10c0/45bba07561d93f184a8686e1a543418ced8c844b994fbe45cc49d5cd2fc8ac7ec949dae38565e35e388ad0cca2b75997a29b6857c927bf6553da3f80ed0e4e62 @@ -21803,17 +19529,6 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:2.7.0": - version: 2.7.0 - resolution: "schema-utils@npm:2.7.0" - dependencies: - "@types/json-schema": "npm:^7.0.4" - ajv: "npm:^6.12.2" - ajv-keywords: "npm:^3.4.1" - checksum: 10c0/723c3c856a0313a89aa81c5fb2c93d4b11225f5cdd442665fddd55d3c285ae72e079f5286a3a9a1a973affe888f6c33554a2cf47b79b24cd8de2f1f756a6fb1b - languageName: node - linkType: hard - "schema-utils@npm:^2.5.0, schema-utils@npm:^2.6.5": version: 2.7.1 resolution: "schema-utils@npm:2.7.1" @@ -21836,7 +19551,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^4.0.0, schema-utils@npm:^4.3.0, schema-utils@npm:^4.3.3": +"schema-utils@npm:^4.3.0, schema-utils@npm:^4.3.3": version: 4.3.3 resolution: "schema-utils@npm:4.3.3" dependencies: @@ -21848,30 +19563,12 @@ __metadata: languageName: node linkType: hard -"section-matter@npm:^1.0.0": - version: 1.0.0 - resolution: "section-matter@npm:1.0.0" - dependencies: - extend-shallow: "npm:^2.0.1" - kind-of: "npm:^6.0.0" - checksum: 10c0/8007f91780adc5aaa781a848eaae50b0f680bbf4043b90cf8a96778195b8fab690c87fe7a989e02394ce69890e330811ec8dab22397d384673ce59f7d750641d - languageName: node - linkType: hard - -"select-hose@npm:^2.0.0": - version: 2.0.0 - resolution: "select-hose@npm:2.0.0" - checksum: 10c0/01cc52edd29feddaf379efb4328aededa633f0ac43c64b11a8abd075ff34f05b0d280882c4fbcbdf1a0658202c9cd2ea8d5985174dcf9a2dac7e3a4996fa9b67 - languageName: node - linkType: hard - -"selfsigned@npm:^2.1.1": - version: 2.4.1 - resolution: "selfsigned@npm:2.4.1" +"scroll-into-view-if-needed@npm:^3.1.0": + version: 3.1.0 + resolution: "scroll-into-view-if-needed@npm:3.1.0" dependencies: - "@types/node-forge": "npm:^1.3.0" - node-forge: "npm:^1" - checksum: 10c0/521829ec36ea042f7e9963bf1da2ed040a815cf774422544b112ec53b7edc0bc50a0f8cc2ae7aa6cc19afa967c641fd96a15de0fc650c68651e41277d2e1df09 + compute-scroll-into-view: "npm:^3.0.2" + checksum: 10c0/1f46b090e1e04fcfdef1e384f6d7e615f9f84d4176faf4dbba7347cc0a6e491e5d578eaf4dbe9618dd3d8d38efafde58535b3e00f2a21ce4178c14be364850ff languageName: node linkType: hard @@ -21884,7 +19581,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.4.1, semver@npm:^5.5.0, semver@npm:^5.6.0": +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.5.0, semver@npm:^5.6.0": version: 5.7.2 resolution: "semver@npm:5.7.2" bin: @@ -21922,7 +19619,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": +"semver@npm:^7.1.3, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": version: 7.7.3 resolution: "semver@npm:7.7.3" bin: @@ -21978,7 +19675,7 @@ __metadata: languageName: node linkType: hard -"serialize-javascript@npm:^6.0.0, serialize-javascript@npm:^6.0.2": +"serialize-javascript@npm:^6.0.2": version: 6.0.2 resolution: "serialize-javascript@npm:6.0.2" dependencies: @@ -21987,36 +19684,6 @@ __metadata: languageName: node linkType: hard -"serve-handler@npm:^6.1.3": - version: 6.1.6 - resolution: "serve-handler@npm:6.1.6" - dependencies: - bytes: "npm:3.0.0" - content-disposition: "npm:0.5.2" - mime-types: "npm:2.1.18" - minimatch: "npm:3.1.2" - path-is-inside: "npm:1.0.2" - path-to-regexp: "npm:3.3.0" - range-parser: "npm:1.2.0" - checksum: 10c0/1e1cb6bbc51ee32bc1505f2e0605bdc2e96605c522277c977b67f83be9d66bd1eec8604388714a4d728e036d86b629bc9aec02120ea030d3d2c3899d44696503 - languageName: node - linkType: hard - -"serve-index@npm:^1.9.1": - version: 1.9.1 - resolution: "serve-index@npm:1.9.1" - dependencies: - accepts: "npm:~1.3.4" - batch: "npm:0.6.1" - debug: "npm:2.6.9" - escape-html: "npm:~1.0.3" - http-errors: "npm:~1.6.2" - mime-types: "npm:~2.1.17" - parseurl: "npm:~1.3.2" - checksum: 10c0/a666471a24196f74371edf2c3c7bcdd82adbac52f600804508754b5296c3567588bf694258b19e0cb23a567acfa20d9721bfdaed3286007b81f9741ada8a3a9c - languageName: node - linkType: hard - "serve-static@npm:^1.16.2, serve-static@npm:~1.16.2": version: 1.16.3 resolution: "serve-static@npm:1.16.3" @@ -22036,6 +19703,13 @@ __metadata: languageName: node linkType: hard +"set-cookie-parser@npm:^2.6.0": + version: 2.7.2 + resolution: "set-cookie-parser@npm:2.7.2" + checksum: 10c0/4381a9eb7ee951dfe393fe7aacf76b9a3b4e93a684d2162ab35594fa4053cc82a4d7d7582bf397718012c9adcf839b8cd8f57c6c42901ea9effe33c752da4a45 + languageName: node + linkType: hard + "set-function-length@npm:^1.2.2": version: 1.2.2 resolution: "set-function-length@npm:1.2.2" @@ -22092,13 +19766,6 @@ __metadata: languageName: node linkType: hard -"setprototypeof@npm:1.1.0": - version: 1.1.0 - resolution: "setprototypeof@npm:1.1.0" - checksum: 10c0/a77b20876689c6a89c3b42f0c3596a9cae02f90fc902570cbd97198e9e8240382086c9303ad043e88cee10f61eae19f1004e51d885395a1e9bf49f9ebed12872 - languageName: node - linkType: hard - "setprototypeof@npm:1.2.0, setprototypeof@npm:~1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" @@ -22122,13 +19789,6 @@ __metadata: languageName: node linkType: hard -"shallowequal@npm:^1.1.0": - version: 1.1.0 - resolution: "shallowequal@npm:1.1.0" - checksum: 10c0/b926efb51cd0f47aa9bc061add788a4a650550bbe50647962113a4579b60af2abe7b62f9b02314acc6f97151d4cf87033a2b15fc20852fae306d1a095215396c - languageName: node - linkType: hard - "shebang-command@npm:^1.2.0": version: 1.2.0 resolution: "shebang-command@npm:1.2.0" @@ -22161,7 +19821,7 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.6.1, shell-quote@npm:^1.7.3, shell-quote@npm:^1.8.3": +"shell-quote@npm:^1.6.1": version: 1.8.3 resolution: "shell-quote@npm:1.8.3" checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd @@ -22181,16 +19841,19 @@ __metadata: languageName: node linkType: hard -"shelljs@npm:^0.8.5": - version: 0.8.5 - resolution: "shelljs@npm:0.8.5" +"shiki@npm:4.2.0, shiki@npm:^4.0.2": + version: 4.2.0 + resolution: "shiki@npm:4.2.0" dependencies: - glob: "npm:^7.0.0" - interpret: "npm:^1.0.0" - rechoir: "npm:^0.6.2" - bin: - shjs: bin/shjs - checksum: 10c0/feb25289a12e4bcd04c40ddfab51aff98a3729f5c2602d5b1a1b95f6819ec7804ac8147ebd8d9a85dfab69d501bcf92d7acef03247320f51c1552cec8d8e2382 + "@shikijs/core": "npm:4.2.0" + "@shikijs/engine-javascript": "npm:4.2.0" + "@shikijs/engine-oniguruma": "npm:4.2.0" + "@shikijs/langs": "npm:4.2.0" + "@shikijs/themes": "npm:4.2.0" + "@shikijs/types": "npm:4.2.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + "@types/hast": "npm:^3.0.4" + checksum: 10c0/a729d91e10e22787c56a553663c487454bbdfc3f5604e2833bea1f47bcbc20b824b2e7629f7b03889706314a3fe5963a8c4808a002282dc1c19f726231096b1c languageName: node linkType: hard @@ -22269,17 +19932,6 @@ __metadata: languageName: node linkType: hard -"sirv@npm:^2.0.3": - version: 2.0.4 - resolution: "sirv@npm:2.0.4" - dependencies: - "@polka/url": "npm:^1.0.0-next.24" - mrmime: "npm:^2.0.0" - totalist: "npm:^3.0.0" - checksum: 10c0/68f8ee857f6a9415e9c07a1f31c7c561df8d5f1b1ba79bee3de583fa37da8718def5309f6b1c6e2c3ef77de45d74f5e49efc7959214443aa92d42e9c99180a4e - languageName: node - linkType: hard - "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -22287,27 +19939,6 @@ __metadata: languageName: node linkType: hard -"sitemap@npm:^7.1.1": - version: 7.1.2 - resolution: "sitemap@npm:7.1.2" - dependencies: - "@types/node": "npm:^17.0.5" - "@types/sax": "npm:^1.2.1" - arg: "npm:^5.0.0" - sax: "npm:^1.2.4" - bin: - sitemap: dist/cli.js - checksum: 10c0/01dd1268c0d4b89f8ef082bcb9ef18d0182d00d1622e9c54743474918169491e5360538f9a01a769262e0fe23d6e3822a90680eff0f076cf87b68d459014a34c - languageName: node - linkType: hard - -"slash@npm:^2.0.0": - version: 2.0.0 - resolution: "slash@npm:2.0.0" - checksum: 10c0/f83dbd3cb62c41bb8fcbbc6bf5473f3234b97fa1d008f571710a9d3757a28c7169e1811cad1554ccb1cc531460b3d221c9a7b37f549398d9a30707f0a5af9193 - languageName: node - linkType: hard - "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -22315,13 +19946,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^4.0.0": - version: 4.0.0 - resolution: "slash@npm:4.0.0" - checksum: 10c0/b522ca75d80d107fd30d29df0549a7b2537c83c4c4ecd12cd7d4ea6c8aaca2ab17ada002e7a1d78a9d736a0261509f26ea5b489082ee443a3a810586ef8eff18 - languageName: node - linkType: hard - "slash@npm:^5.1.0": version: 5.1.0 resolution: "slash@npm:5.1.0" @@ -22388,17 +20012,6 @@ __metadata: languageName: node linkType: hard -"sockjs@npm:^0.3.24": - version: 0.3.24 - resolution: "sockjs@npm:0.3.24" - dependencies: - faye-websocket: "npm:^0.11.3" - uuid: "npm:^8.3.2" - websocket-driver: "npm:^0.7.4" - checksum: 10c0/aa102c7d921bf430215754511c81ea7248f2dcdf268fbdb18e4d8183493a86b8793b164c636c52f474a886f747447c962741df2373888823271efdb9d2594f33 - languageName: node - linkType: hard - "socks-proxy-agent@npm:^8.0.3": version: 8.0.5 resolution: "socks-proxy-agent@npm:8.0.5" @@ -22420,13 +20033,6 @@ __metadata: languageName: node linkType: hard -"sort-css-media-queries@npm:2.1.0": - version: 2.1.0 - resolution: "sort-css-media-queries@npm:2.1.0" - checksum: 10c0/6b39dd2503d8279688fee837c63bdf3b49eea14d10d5ae09d9e99e4a0b3da1b702c3931e8f793b702b9ea8929a9389ba8d6345b58b5d1f0ec3e84920685a724a - languageName: node - linkType: hard - "sort-keys@npm:^1.0.0": version: 1.1.2 resolution: "sort-keys@npm:1.1.2" @@ -22497,20 +20103,27 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.5.0, source-map@npm:^0.5.6": +"source-map@npm:^0.5.6": version: 0.5.7 resolution: "source-map@npm:0.5.7" checksum: 10c0/904e767bb9c494929be013017380cbba013637da1b28e5943b566031e29df04fba57edf3f093e0914be094648b577372bd8ad247fa98cfba9c600794cd16b599 languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": +"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.1": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 languageName: node linkType: hard +"source-map@npm:^0.7.0": + version: 0.7.6 + resolution: "source-map@npm:0.7.6" + checksum: 10c0/59f6f05538539b274ba771d2e9e32f6c65451982510564438e048bc1352f019c6efcdc6dd07909b1968144941c14015c2c7d4369fb7c4d7d53ae769716dcc16c + languageName: node + linkType: hard + "sourcemapped-stacktrace@npm:^1.1.6": version: 1.1.11 resolution: "sourcemapped-stacktrace@npm:1.1.11" @@ -22527,6 +20140,13 @@ __metadata: languageName: node linkType: hard +"space-separated-tokens@npm:^2.0.0": + version: 2.0.2 + resolution: "space-separated-tokens@npm:2.0.2" + checksum: 10c0/6173e1d903dca41dcab6a2deed8b4caf61bd13b6d7af8374713500570aa929ff9414ae09a0519f4f8772df993300305a395d4871f35bc4ca72b6db57e1f30af8 + languageName: node + linkType: hard + "spdx-correct@npm:^3.0.0": version: 3.2.0 resolution: "spdx-correct@npm:3.2.0" @@ -22561,33 +20181,6 @@ __metadata: languageName: node linkType: hard -"spdy-transport@npm:^3.0.0": - version: 3.0.0 - resolution: "spdy-transport@npm:3.0.0" - dependencies: - debug: "npm:^4.1.0" - detect-node: "npm:^2.0.4" - hpack.js: "npm:^2.1.6" - obuf: "npm:^1.1.2" - readable-stream: "npm:^3.0.6" - wbuf: "npm:^1.7.3" - checksum: 10c0/eaf7440fa90724fffc813c386d4a8a7427d967d6e46d7c51d8f8a533d1a6911b9823ea9218703debbae755337e85f110185d7a00ae22ec5c847077b908ce71bb - languageName: node - linkType: hard - -"spdy@npm:^4.0.2": - version: 4.0.2 - resolution: "spdy@npm:4.0.2" - dependencies: - debug: "npm:^4.1.0" - handle-thing: "npm:^2.0.0" - http-deceiver: "npm:^1.2.7" - select-hose: "npm:^2.0.0" - spdy-transport: "npm:^3.0.0" - checksum: 10c0/983509c0be9d06fd00bb9dff713c5b5d35d3ffd720db869acdd5ad7aa6fc0e02c2318b58f75328957d8ff772acdf1f7d19382b6047df342044ff3e2d6805ccdf - languageName: node - linkType: hard - "split-on-first@npm:^1.0.0": version: 1.1.0 resolution: "split-on-first@npm:1.1.0" @@ -22645,13 +20238,6 @@ __metadata: languageName: node linkType: hard -"stable@npm:^0.1.8": - version: 0.1.8 - resolution: "stable@npm:0.1.8" - checksum: 10c0/df74b5883075076e78f8e365e4068ecd977af6c09da510cfc3148a303d4b87bc9aa8f7c48feb67ed4ef970b6140bd9eabba2129e28024aa88df5ea0114cba39d - languageName: node - linkType: hard - "stack-utils@npm:^2.0.3": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" @@ -22701,7 +20287,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:>= 1.4.0 < 2, statuses@npm:~1.5.0": +"statuses@npm:~1.5.0": version: 1.5.0 resolution: "statuses@npm:1.5.0" checksum: 10c0/e433900956357b3efd79b1c547da4d291799ac836960c016d10a98f6a810b1b5c0dcc13b5a7aa609a58239b5190e1ea176ad9221c2157d2fd1c747393e6b2940 @@ -22715,13 +20301,6 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.0.1": - version: 3.10.0 - resolution: "std-env@npm:3.10.0" - checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f - languageName: node - linkType: hard - "stop-iteration-iterator@npm:^1.1.0": version: 1.1.0 resolution: "stop-iteration-iterator@npm:1.1.0" @@ -22781,7 +20360,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": +"string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -22792,17 +20371,6 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^5.0.1": - version: 5.1.2 - resolution: "string-width@npm:5.1.2" - dependencies: - eastasianwidth: "npm:^0.2.0" - emoji-regex: "npm:^9.2.2" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca - languageName: node - linkType: hard - "string-width@npm:^7.0.0, string-width@npm:^7.2.0": version: 7.2.0 resolution: "string-width@npm:7.2.0" @@ -22912,14 +20480,13 @@ __metadata: languageName: node linkType: hard -"stringify-object@npm:^3.3.0": - version: 3.3.0 - resolution: "stringify-object@npm:3.3.0" +"stringify-entities@npm:^4.0.0": + version: 4.0.4 + resolution: "stringify-entities@npm:4.0.4" dependencies: - get-own-enumerable-property-symbols: "npm:^3.0.0" - is-obj: "npm:^1.0.1" - is-regexp: "npm:^1.0.0" - checksum: 10c0/ba8078f84128979ee24b3de9a083489cbd3c62cb8572a061b47d4d82601a8ae4b4d86fa8c54dd955593da56bb7c16a6de51c27221fdc6b7139bb4f29d815f35b + character-entities-html4: "npm:^2.0.0" + character-entities-legacy: "npm:^3.0.0" + checksum: 10c0/537c7e656354192406bdd08157d759cd615724e9d0873602d2c9b2f6a5c0a8d0b1d73a0a08677848105c5eebac6db037b57c0b3a4ec86331117fa7319ed50448 languageName: node linkType: hard @@ -22941,15 +20508,6 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": - version: 7.1.2 - resolution: "strip-ansi@npm:7.1.2" - dependencies: - ansi-regex: "npm:^6.0.1" - checksum: 10c0/0d6d7a023de33368fd042aab0bf48f4f4077abdfd60e5393e73c7c411e85e1b3a83507c11af2e656188511475776215df9ca589b4da2295c9455cc399ce1858b - languageName: node - linkType: hard - "strip-ansi@npm:^7.1.0": version: 7.2.0 resolution: "strip-ansi@npm:7.2.0" @@ -22959,13 +20517,6 @@ __metadata: languageName: node linkType: hard -"strip-bom-string@npm:^1.0.0": - version: 1.0.0 - resolution: "strip-bom-string@npm:1.0.0" - checksum: 10c0/5c5717e2643225aa6a6d659d34176ab2657037f1fe2423ac6fcdb488f135e14fef1022030e426d8b4d0989e09adbd5c3288d5d3b9c632abeefd2358dfc512bca - languageName: node - linkType: hard - "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -23040,12 +20591,21 @@ __metadata: languageName: node linkType: hard -"style-to-object@npm:0.3.0, style-to-object@npm:^0.3.0": - version: 0.3.0 - resolution: "style-to-object@npm:0.3.0" +"style-to-js@npm:^1.0.0": + version: 1.1.21 + resolution: "style-to-js@npm:1.1.21" dependencies: - inline-style-parser: "npm:0.1.1" - checksum: 10c0/afe9b96ba077a9068baf8887091870f50298157c0ebf5378151792cf2a2ce084fec9b34fc544da0d9f8e6c22ca0c9e23aa6f075bb8eb051aa1d64363e9987600 + style-to-object: "npm:1.0.14" + checksum: 10c0/94231aa80f58f442c3a5ae01a21d10701e5d62f96b4b3e52eab3499077ee52df203cc0df4a1a870707f5e99470859136ea8657b782a5f4ca7934e0ffe662a588 + languageName: node + linkType: hard + +"style-to-object@npm:1.0.14": + version: 1.0.14 + resolution: "style-to-object@npm:1.0.14" + dependencies: + inline-style-parser: "npm:0.2.7" + checksum: 10c0/854d9e9b77afc336e6d7b09348e7939f2617b34eb0895824b066d8cd1790284cb6d8b2ba36be88025b2595d715dba14b299ae76e4628a366541106f639e13679 languageName: node linkType: hard @@ -23058,19 +20618,7 @@ __metadata: languageName: node linkType: hard -"stylehacks@npm:^5.1.1": - version: 5.1.1 - resolution: "stylehacks@npm:5.1.1" - dependencies: - browserslist: "npm:^4.21.4" - postcss-selector-parser: "npm:^6.0.4" - peerDependencies: - postcss: ^8.2.15 - checksum: 10c0/402c2b545eeda0e972f125779adddc88df11bcf3a89de60c92026bd98cd49c1abffcd5bfe41766398835e0a1c7e5e72bdb6905809ecbb60716cd8d3a32ea7cd3 - languageName: node - linkType: hard - -"styleq@npm:^0.1.2, styleq@npm:^0.1.3": +"styleq@npm:^0.1.3": version: 0.1.3 resolution: "styleq@npm:0.1.3" checksum: 10c0/975d951792e65052f1f6e41aaad46492642ce4922b3dc36d4b49b37c8509f9a776794d8f275360f00116a5e6ab1e31514bdcd5840656c4e3213da6803fa12941 @@ -23128,30 +20676,6 @@ __metadata: languageName: node linkType: hard -"svg-parser@npm:^2.0.4": - version: 2.0.4 - resolution: "svg-parser@npm:2.0.4" - checksum: 10c0/02f6cb155dd7b63ebc2f44f36365bc294543bebb81b614b7628f1af3c54ab64f7e1cec20f06e252bf95bdde78441ae295a412c68ad1678f16a6907d924512b7a - languageName: node - linkType: hard - -"svgo@npm:^2.7.0, svgo@npm:^2.8.0": - version: 2.8.0 - resolution: "svgo@npm:2.8.0" - dependencies: - "@trysound/sax": "npm:0.2.0" - commander: "npm:^7.2.0" - css-select: "npm:^4.1.3" - css-tree: "npm:^1.1.3" - csso: "npm:^4.2.0" - picocolors: "npm:^1.0.0" - stable: "npm:^0.1.8" - bin: - svgo: bin/svgo - checksum: 10c0/0741f5d5cad63111a90a0ce7a1a5a9013f6d293e871b75efe39addb57f29a263e45294e485a4d2ff9cc260a5d142c8b5937b2234b4ef05efdd2706fb2d360ecc - languageName: node - linkType: hard - "swap-case@npm:^1.1.0": version: 1.1.2 resolution: "swap-case@npm:1.1.2" @@ -23178,7 +20702,7 @@ __metadata: languageName: node linkType: hard -"tapable@npm:^2.0.0, tapable@npm:^2.2.0, tapable@npm:^2.2.1, tapable@npm:^2.3.0": +"tapable@npm:^2.2.0, tapable@npm:^2.3.0": version: 2.3.0 resolution: "tapable@npm:2.3.0" checksum: 10c0/cb9d67cc2c6a74dedc812ef3085d9d681edd2c1fa18e4aef57a3c0605fdbe44e6b8ea00bd9ef21bc74dd45314e39d31227aa031ebf2f5e38164df514136f2681 @@ -23215,7 +20739,7 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.3.16, terser-webpack-plugin@npm:^5.3.3": +"terser-webpack-plugin@npm:^5.3.16": version: 5.3.16 resolution: "terser-webpack-plugin@npm:5.3.16" dependencies: @@ -23237,7 +20761,7 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.10.0, terser@npm:^5.15.0, terser@npm:^5.31.1": +"terser@npm:^5.15.0, terser@npm:^5.31.1": version: 5.44.1 resolution: "terser@npm:5.44.1" dependencies: @@ -23281,13 +20805,6 @@ __metadata: languageName: node linkType: hard -"text-table@npm:^0.2.0": - version: 0.2.0 - resolution: "text-table@npm:0.2.0" - checksum: 10c0/02805740c12851ea5982686810702e2f14369a5f4c5c40a836821e3eefc65ffeec3131ba324692a37608294b0fd8c1e55a2dd571ffed4909822787668ddbee5c - languageName: node - linkType: hard - "throat@npm:^5.0.0": version: 5.0.0 resolution: "throat@npm:5.0.0" @@ -23321,27 +20838,6 @@ __metadata: languageName: node linkType: hard -"thunky@npm:^1.0.2": - version: 1.1.0 - resolution: "thunky@npm:1.1.0" - checksum: 10c0/369764f39de1ce1de2ba2fa922db4a3f92e9c7f33bcc9a713241bc1f4a5238b484c17e0d36d1d533c625efb00e9e82c3e45f80b47586945557b45abb890156d2 - languageName: node - linkType: hard - -"tiny-invariant@npm:^1.0.2": - version: 1.3.3 - resolution: "tiny-invariant@npm:1.3.3" - checksum: 10c0/65af4a07324b591a059b35269cd696aba21bef2107f29b9f5894d83cc143159a204b299553435b03874ebb5b94d019afa8b8eff241c8a4cfee95872c2e1c1c4a - languageName: node - linkType: hard - -"tiny-warning@npm:^1.0.0": - version: 1.0.3 - resolution: "tiny-warning@npm:1.0.3" - checksum: 10c0/ef8531f581b30342f29670cb41ca248001c6fd7975ce22122bd59b8d62b4fc84ad4207ee7faa95cde982fa3357cd8f4be650142abc22805538c3b1392d7084fa - languageName: node - linkType: hard - "tinycolor2@npm:^1.4.1": version: 1.6.0 resolution: "tinycolor2@npm:1.6.0" @@ -23458,6 +20954,13 @@ __metadata: languageName: node linkType: hard +"toggle-selection@npm:^1.0.6": + version: 1.0.6 + resolution: "toggle-selection@npm:1.0.6" + checksum: 10c0/f2cf1f2c70f374fd87b0cdc8007453ba9e981c4305a8bf4eac10a30e62ecdfd28bca7d18f8f15b15a506bf8a7bfb20dbe3539f0fcf2a2c8396c1a78d53e1f179 + languageName: node + linkType: hard + "toidentifier@npm:~1.0.1": version: 1.0.1 resolution: "toidentifier@npm:1.0.1" @@ -23472,13 +20975,6 @@ __metadata: languageName: node linkType: hard -"totalist@npm:^3.0.0": - version: 3.0.1 - resolution: "totalist@npm:3.0.1" - checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863 - languageName: node - linkType: hard - "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -23493,6 +20989,13 @@ __metadata: languageName: node linkType: hard +"trim-lines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-lines@npm:3.0.1" + checksum: 10c0/3a1611fa9e52aa56a94c69951a9ea15b8aaad760eaa26c56a65330dc8adf99cb282fc07cc9d94968b7d4d88003beba220a7278bbe2063328eb23fb56f9509e94 + languageName: node + linkType: hard + "trim-newlines@npm:^2.0.0": version: 2.0.0 resolution: "trim-newlines@npm:2.0.0" @@ -23537,6 +21040,13 @@ __metadata: languageName: node linkType: hard +"trough@npm:^2.0.0": + version: 2.2.0 + resolution: "trough@npm:2.2.0" + checksum: 10c0/58b671fc970e7867a48514168894396dd94e6d9d6456aca427cc299c004fe67f35ed7172a36449086b2edde10e78a71a284ec0076809add6834fb8f857ccb9b0 + languageName: node + linkType: hard + "ts-api-utils@npm:^2.5.0": version: 2.5.0 resolution: "ts-api-utils@npm:2.5.0" @@ -23565,7 +21075,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.4.1": +"tslib@npm:^2.0.1, tslib@npm:^2.4.0, tslib@npm:^2.8.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 @@ -23595,13 +21105,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^0.20.2": - version: 0.20.2 - resolution: "type-fest@npm:0.20.2" - checksum: 10c0/dea9df45ea1f0aaa4e2d3bed3f9a0bfe9e5b2592bddb92eb1bf06e50bcf98dbb78189668cd8bc31a0511d3fc25539b4cd5c704497e53e93e2d40ca764b10bfc3 - languageName: node - linkType: hard - "type-fest@npm:^0.21.3": version: 0.21.3 resolution: "type-fest@npm:0.21.3" @@ -23630,13 +21133,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^2.5.0": - version: 2.19.0 - resolution: "type-fest@npm:2.19.0" - checksum: 10c0/a5a7ecf2e654251613218c215c7493574594951c08e52ab9881c9df6a6da0aeca7528c213c622bc374b4e0cb5c443aa3ab758da4e3c959783ce884c3194e12cb - languageName: node - linkType: hard - "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -23737,17 +21233,7 @@ __metadata: bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 - languageName: node - linkType: hard - -"typescript@npm:^4.8.4": - version: 4.9.5 - resolution: "typescript@npm:4.9.5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/5f6cad2e728a8a063521328e612d7876e12f0d8a8390d3b3aaa452a6a65e24e9ac8ea22beb72a924fd96ea0a49ea63bb4e251fb922b12eedfb7f7a26475e5c56 + checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 languageName: node linkType: hard @@ -23771,16 +21257,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^4.8.4#optional!builtin": - version: 4.9.5 - resolution: "typescript@patch:typescript@npm%3A4.9.5#optional!builtin::version=4.9.5&hash=289587" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/e3333f887c6829dfe0ab6c1dbe0dd1e3e2aeb56c66460cb85c5440c566f900c833d370ca34eb47558c0c69e78ced4bfe09b8f4f98b6de7afed9b84b8d1dd06a1 - languageName: node - linkType: hard - "typescript@patch:typescript@npm%3A^6.0.3#optional!builtin": version: 6.0.3 resolution: "typescript@patch:typescript@npm%3A6.0.3#optional!builtin::version=6.0.3&hash=5786d5" @@ -23835,10 +21311,12 @@ __metadata: languageName: node linkType: hard -"undici@npm:^7.12.0": - version: 7.18.2 - resolution: "undici@npm:7.18.2" - checksum: 10c0/4ff0722799f5e06fde5d1e58d318b1d78ba9a477836ad3e7374e9ac30ca20d1ab3282952d341635bf69b8e74b5c1cf366e595b3a96810e0dbf74e622dad7b5f9 +"unhead@npm:2.1.15": + version: 2.1.15 + resolution: "unhead@npm:2.1.15" + dependencies: + hookable: "npm:^6.0.1" + checksum: 10c0/ab266e9cda44d0b528ae3b62ce9136cb4f81bd2aa1e2d66431cefe6b41151a811971951e05b4d81e6adea105b65901d9091dffa29d646f1be6246691606b2407 languageName: node linkType: hard @@ -23897,17 +21375,18 @@ __metadata: languageName: node linkType: hard -"unified@npm:9.2.0": - version: 9.2.0 - resolution: "unified@npm:9.2.0" +"unified@npm:^11.0.0, unified@npm:^11.0.5": + version: 11.0.5 + resolution: "unified@npm:11.0.5" dependencies: - bail: "npm:^1.0.0" + "@types/unist": "npm:^3.0.0" + bail: "npm:^2.0.0" + devlop: "npm:^1.0.0" extend: "npm:^3.0.0" - is-buffer: "npm:^2.0.0" - is-plain-obj: "npm:^2.0.0" - trough: "npm:^1.0.0" - vfile: "npm:^4.0.0" - checksum: 10c0/53aedb794b0ada002b72593d74633f45742e3dfe771a8091c0f51b59119f74f3f1bba0a24c5d72a35629793f992cf9e1debf21aa4689dc718482ffec3a633623 + is-plain-obj: "npm:^4.0.0" + trough: "npm:^2.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/53c8e685f56d11d9d458a43e0e74328a4d6386af51c8ac37a3dcabec74ce5026da21250590d4aff6733ccd7dc203116aae2b0769abc18cdf9639a54ae528dfc9 languageName: node linkType: hard @@ -23927,7 +21406,7 @@ __metadata: languageName: node linkType: hard -"unified@npm:^9.0.0, unified@npm:^9.2.2": +"unified@npm:^9.0.0": version: 9.2.2 resolution: "unified@npm:9.2.2" dependencies: @@ -23980,13 +21459,6 @@ __metadata: languageName: node linkType: hard -"unist-builder@npm:2.0.3, unist-builder@npm:^2.0.0": - version: 2.0.3 - resolution: "unist-builder@npm:2.0.3" - checksum: 10c0/d8b13ffd774bfe6175ca988d63cbaf6d85882a0701d6158597134ce1c3acf665a09421461a4036704f77edb8a6a2792d09eb55382428c2a9a60488b44909eeae - languageName: node - linkType: hard - "unist-builder@npm:^1.0.1": version: 1.0.4 resolution: "unist-builder@npm:1.0.4" @@ -23996,7 +21468,7 @@ __metadata: languageName: node linkType: hard -"unist-util-generated@npm:^1.0.0, unist-util-generated@npm:^1.1.0": +"unist-util-generated@npm:^1.1.0": version: 1.1.6 resolution: "unist-util-generated@npm:1.1.6" checksum: 10c0/ee04a58a6711145ec5c8c6f10dfd3335ac93d9039dc35e7410ffc1299d6f3671b27d9b7aa486f826bd66ec15807ad6d0bf9348b34a1046440e1617abcf42903f @@ -24017,6 +21489,24 @@ __metadata: languageName: node linkType: hard +"unist-util-is@npm:^6.0.0": + version: 6.0.1 + resolution: "unist-util-is@npm:6.0.1" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/5a487d390193811d37a68264e204dbc7c15c40b8fc29b5515a535d921d071134f571d7b5cbd59bcd58d5ce1c0ab08f20fc4a1f0df2287a249c979267fc32ce06 + languageName: node + linkType: hard + +"unist-util-position-from-estree@npm:^2.0.0": + version: 2.0.0 + resolution: "unist-util-position-from-estree@npm:2.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/39127bf5f0594e0a76d9241dec4f7aa26323517120ce1edd5ed91c8c1b9df7d6fb18af556e4b6250f1c7368825720ed892e2b6923be5cdc08a9bb16536dc37b3 + languageName: node + linkType: hard + "unist-util-position@npm:^3.0.0": version: 3.1.0 resolution: "unist-util-position@npm:3.1.0" @@ -24024,6 +21514,15 @@ __metadata: languageName: node linkType: hard +"unist-util-position@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-position@npm:5.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/dde3b31e314c98f12b4dc6402f9722b2bf35e96a4f2d463233dd90d7cde2d4928074a7a11eff0a5eb1f4e200f27fc1557e0a64a7e8e4da6558542f251b1b7400 + languageName: node + linkType: hard + "unist-util-remove-position@npm:^1.0.0": version: 1.1.4 resolution: "unist-util-remove-position@npm:1.1.4" @@ -24033,15 +21532,6 @@ __metadata: languageName: node linkType: hard -"unist-util-remove-position@npm:^2.0.0": - version: 2.0.1 - resolution: "unist-util-remove-position@npm:2.0.1" - dependencies: - unist-util-visit: "npm:^2.0.0" - checksum: 10c0/9aadc8e9fafc4eeb04462454ab084184b84b397a367cab3787c59411b16c8f03d13e80e9ffd6bdae68bf8e5175f42008f410288a041a6ee53bcac8ced45a12ed - languageName: node - linkType: hard - "unist-util-remove@npm:^1.0.0": version: 1.0.3 resolution: "unist-util-remove@npm:1.0.3" @@ -24051,12 +21541,14 @@ __metadata: languageName: node linkType: hard -"unist-util-remove@npm:^2.0.0": - version: 2.1.0 - resolution: "unist-util-remove@npm:2.1.0" +"unist-util-remove@npm:^4.0.0": + version: 4.0.0 + resolution: "unist-util-remove@npm:4.0.0" dependencies: - unist-util-is: "npm:^4.0.0" - checksum: 10c0/f7dea56fb720ddab5e406af12ce37453b028273e23a7cc3e4c9f3f1ec85e1f72c6943a1ebb907120c9be0b1d08b209d7b8c7d2191a5012e16081056edf638df9 + "@types/unist": "npm:^3.0.0" + unist-util-is: "npm:^6.0.0" + unist-util-visit-parents: "npm:^6.0.0" + checksum: 10c0/30f3ed31095dd7f3109266d39c514fab5f2da3fb656d5f78a0e3e7700f219760f2f4d8286c810ae43c241fee3f0a8dd40f8d1e5ebeee3cb810581d5e7e8d4f7d languageName: node linkType: hard @@ -24076,6 +21568,24 @@ __metadata: languageName: node linkType: hard +"unist-util-stringify-position@npm:^4.0.0": + version: 4.0.0 + resolution: "unist-util-stringify-position@npm:4.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/dfe1dbe79ba31f589108cb35e523f14029b6675d741a79dea7e5f3d098785045d556d5650ec6a8338af11e9e78d2a30df12b1ee86529cded1098da3f17ee999e + languageName: node + linkType: hard + +"unist-util-visit-children@npm:^3.0.0": + version: 3.0.0 + resolution: "unist-util-visit-children@npm:3.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10c0/51e95f54fbf11d414952c011c761c3960864948ad3fd2abe3989eb18b18d96b8f48e7ea5ab6f23264d1a3f4f5a1ff76312dd8f2196c78b762098403505c3abb9 + languageName: node + linkType: hard + "unist-util-visit-parents@npm:^2.0.0": version: 2.1.2 resolution: "unist-util-visit-parents@npm:2.1.2" @@ -24095,7 +21605,26 @@ __metadata: languageName: node linkType: hard -"unist-util-visit@npm:2.0.3, unist-util-visit@npm:^2.0.0, unist-util-visit@npm:^2.0.3": +"unist-util-visit-parents@npm:^6.0.0": + version: 6.0.2 + resolution: "unist-util-visit-parents@npm:6.0.2" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-is: "npm:^6.0.0" + checksum: 10c0/f1e4019dbd930301825895e3737b1ee0cd682f7622ddd915062135cbb39f8c090aaece3a3b5eae1f2ea52ec33f0931abb8f8a8b5c48a511a4203e3d360a8cd49 + languageName: node + linkType: hard + +"unist-util-visit@npm:^1.0.0, unist-util-visit@npm:^1.1.0, unist-util-visit@npm:^1.3.0": + version: 1.4.1 + resolution: "unist-util-visit@npm:1.4.1" + dependencies: + unist-util-visit-parents: "npm:^2.0.0" + checksum: 10c0/8fb61725270059034d987813816e6c2d6362f55d4d19936db9ce23a5ace0ae4d584064ceb94dee6618e3318aef087505607f7ccc8c4305fbb644c3b643ae0059 + languageName: node + linkType: hard + +"unist-util-visit@npm:^2.0.3": version: 2.0.3 resolution: "unist-util-visit@npm:2.0.3" dependencies: @@ -24106,12 +21635,14 @@ __metadata: languageName: node linkType: hard -"unist-util-visit@npm:^1.0.0, unist-util-visit@npm:^1.1.0, unist-util-visit@npm:^1.3.0": - version: 1.4.1 - resolution: "unist-util-visit@npm:1.4.1" +"unist-util-visit@npm:^5.0.0, unist-util-visit@npm:^5.1.0": + version: 5.1.0 + resolution: "unist-util-visit@npm:5.1.0" dependencies: - unist-util-visit-parents: "npm:^2.0.0" - checksum: 10c0/8fb61725270059034d987813816e6c2d6362f55d4d19936db9ce23a5ace0ae4d584064ceb94dee6618e3318aef087505607f7ccc8c4305fbb644c3b643ae0059 + "@types/unist": "npm:^3.0.0" + unist-util-is: "npm:^6.0.0" + unist-util-visit-parents: "npm:^6.0.0" + checksum: 10c0/a56e1bbbf63fcb55abe379e660b9a3367787e8be1e2473bdb7e86cfa6f32b6c1fa0092432d7040b8a30b2fc674bbbe024ffe6d03c3d6bf4839b064f584463a4e languageName: node linkType: hard @@ -24188,28 +21719,6 @@ __metadata: languageName: node linkType: hard -"update-notifier@npm:^5.1.0": - version: 5.1.0 - resolution: "update-notifier@npm:5.1.0" - dependencies: - boxen: "npm:^5.0.0" - chalk: "npm:^4.1.0" - configstore: "npm:^5.0.1" - has-yarn: "npm:^2.1.0" - import-lazy: "npm:^2.1.0" - is-ci: "npm:^2.0.0" - is-installed-globally: "npm:^0.4.0" - is-npm: "npm:^5.0.0" - is-yarn-global: "npm:^0.3.0" - latest-version: "npm:^5.1.0" - pupa: "npm:^2.1.1" - semver: "npm:^7.3.4" - semver-diff: "npm:^3.1.1" - xdg-basedir: "npm:^4.0.0" - checksum: 10c0/0dde6db5ac1e5244e1f8bf5b26895a0d53c00797ea2bdbc1302623dd1aecab5cfb88b4f324d482cbd4c8b089464383d8c83db64dec5798ec0136820e22478e47 - languageName: node - linkType: hard - "upper-case-first@npm:^1.1.0, upper-case-first@npm:^1.1.2": version: 1.1.2 resolution: "upper-case-first@npm:1.1.2" @@ -24249,23 +21758,6 @@ __metadata: languageName: node linkType: hard -"url-loader@npm:^4.1.1": - version: 4.1.1 - resolution: "url-loader@npm:4.1.1" - dependencies: - loader-utils: "npm:^2.0.0" - mime-types: "npm:^2.1.27" - schema-utils: "npm:^3.0.0" - peerDependencies: - file-loader: "*" - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - file-loader: - optional: true - checksum: 10c0/71b6300e02ce26c70625eae1a2297c0737635038c62691bb3007ac33e85c0130efc74bfb444baf5c6b3bad5953491159d31d66498967d1417865d0c7e7cd1a64 - languageName: node - linkType: hard - "url-parse-lax@npm:^3.0.0": version: 3.0.0 resolution: "url-parse-lax@npm:3.0.0" @@ -24285,39 +21777,6 @@ __metadata: languageName: node linkType: hard -"use-composed-ref@npm:^1.3.0": - version: 1.4.0 - resolution: "use-composed-ref@npm:1.4.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/c77e0cba9579b7746d52feaf3ce77d8c345f266c9c1ef46584ae68f54646537c87b2ad97f5219a4b1db52f97ec2905e88e5b146add1f28f7e457bd52ca1b93cf - languageName: node - linkType: hard - -"use-isomorphic-layout-effect@npm:^1.1.1": - version: 1.2.1 - resolution: "use-isomorphic-layout-effect@npm:1.2.1" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/4d3c1124d630fbe09c1d2a16af0cd78ac2fe1d22eb24a178363e3d84a897659cc04e8e8cd71f66ff78ff75ef8287fa72e746cb213b96c1097e70e4b4ed69f63f - languageName: node - linkType: hard - -"use-latest-callback@npm:^0.1.7": - version: 0.1.11 - resolution: "use-latest-callback@npm:0.1.11" - peerDependencies: - react: ">=16.8" - checksum: 10c0/8361ccd3e121552173a27fad67bd226e0cc8acf377073357a7ce83f516be3993b57111eb0e5a8748932543ff6bf4d202200de6e8ba6fa78ae111f44d05404128 - languageName: node - linkType: hard - "use-latest-callback@npm:^0.2.3": version: 0.2.6 resolution: "use-latest-callback@npm:0.2.6" @@ -24336,21 +21795,7 @@ __metadata: languageName: node linkType: hard -"use-latest@npm:^1.2.1": - version: 1.3.0 - resolution: "use-latest@npm:1.3.0" - dependencies: - use-isomorphic-layout-effect: "npm:^1.1.1" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/067c648814ad0c1f1e89d2d0e496254b05c4bed6a34e23045b4413824222aab08fd803c59a42852acc16830c17567d03f8c90af0a62be2f4e4b931454d079798 - languageName: node - linkType: hard - -"use-sync-external-store@npm:^1.2.0, use-sync-external-store@npm:^1.6.0": +"use-sync-external-store@npm:^1.6.0": version: 1.6.0 resolution: "use-sync-external-store@npm:1.6.0" peerDependencies: @@ -24373,20 +21818,6 @@ __metadata: languageName: node linkType: hard -"utila@npm:~0.4": - version: 0.4.0 - resolution: "utila@npm:0.4.0" - checksum: 10c0/2791604e09ca4f77ae314df83e80d1805f867eb5c7e13e7413caee01273c278cf2c9a3670d8d25c889a877f7b149d892fe61b0181a81654b425e9622ab23d42e - languageName: node - linkType: hard - -"utility-types@npm:^3.10.0": - version: 3.11.0 - resolution: "utility-types@npm:3.11.0" - checksum: 10c0/2f1580137b0c3e6cf5405f37aaa8f5249961a76d26f1ca8efc0ff49a2fc0e0b2db56de8e521a174d075758e0c7eb3e590edec0832eb44478b958f09914920f19 - languageName: node - linkType: hard - "utils-merge@npm:1.0.1": version: 1.0.1 resolution: "utils-merge@npm:1.0.1" @@ -24394,7 +21825,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:8.3.2, uuid@npm:^8.3.2": +"uuid@npm:8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" bin: @@ -24440,13 +21871,6 @@ __metadata: languageName: node linkType: hard -"value-equal@npm:^1.0.1": - version: 1.0.1 - resolution: "value-equal@npm:1.0.1" - checksum: 10c0/79068098355483ef29f4d3753999ad880875b87625d7e9055cad9346ea4b7662aad3a66f87976801b0dd7a6f828ba973d28b1669ebcd37eaf88cc5f687c1a691 - languageName: node - linkType: hard - "vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -24461,13 +21885,23 @@ __metadata: languageName: node linkType: hard -"vfile-location@npm:^3.0.0, vfile-location@npm:^3.2.0": +"vfile-location@npm:^3.2.0": version: 3.2.0 resolution: "vfile-location@npm:3.2.0" checksum: 10c0/d9513c738fcac26388f4ee04337663514434df718201309088377b53be3fdcfdb01a4a8f02f5a21ebf33690a670f31229e4c7c3991fb7af63f549fda3ec36836 languageName: node linkType: hard +"vfile-location@npm:^5.0.0": + version: 5.0.3 + resolution: "vfile-location@npm:5.0.3" + dependencies: + "@types/unist": "npm:^3.0.0" + vfile: "npm:^6.0.0" + checksum: 10c0/1711f67802a5bc175ea69750d59863343ed43d1b1bb25c0a9063e4c70595e673e53e2ed5cdbb6dcdc370059b31605144d95e8c061b9361bcc2b036b8f63a4966 + languageName: node + linkType: hard + "vfile-message@npm:^1.0.0": version: 1.1.1 resolution: "vfile-message@npm:1.1.1" @@ -24487,6 +21921,16 @@ __metadata: languageName: node linkType: hard +"vfile-message@npm:^4.0.0": + version: 4.0.3 + resolution: "vfile-message@npm:4.0.3" + dependencies: + "@types/unist": "npm:^3.0.0" + unist-util-stringify-position: "npm:^4.0.0" + checksum: 10c0/33d9f219610d27987689bb14fa5573d2daa146941d1a05416dd7702c4215b23f44ed81d059e70d0e4e24f9a57d5f4dc9f18d35a993f04cf9446a7abe6d72d0c0 + languageName: node + linkType: hard + "vfile@npm:^3.0.0": version: 3.0.1 resolution: "vfile@npm:3.0.1" @@ -24511,6 +21955,16 @@ __metadata: languageName: node linkType: hard +"vfile@npm:^6.0.0": + version: 6.0.3 + resolution: "vfile@npm:6.0.3" + dependencies: + "@types/unist": "npm:^3.0.0" + vfile-message: "npm:^4.0.0" + checksum: 10c0/e5d9eb4810623f23758cfc2205323e33552fb5972e5c2e6587babe08fe4d24859866277404fb9e2a20afb71013860d96ec806cb257536ae463c87d70022ab9ef + languageName: node + linkType: hard + "vlq@npm:^1.0.0": version: 1.0.1 resolution: "vlq@npm:1.0.1" @@ -24518,21 +21972,6 @@ __metadata: languageName: node linkType: hard -"wait-on@npm:^6.0.1": - version: 6.0.1 - resolution: "wait-on@npm:6.0.1" - dependencies: - axios: "npm:^0.25.0" - joi: "npm:^17.6.0" - lodash: "npm:^4.17.21" - minimist: "npm:^1.2.5" - rxjs: "npm:^7.5.4" - bin: - wait-on: bin/wait-on - checksum: 10c0/99772bc85d17f7e6ff8b0e40bd2c90a2c2025b0e9a5a3f8edcf39af0c367d8a5bbdd3d4f1190588be8b09745e80bbdf59c2c4059053a7d180e29b8711cc4a840 - languageName: node - linkType: hard - "walker@npm:^1.0.7, walker@npm:^1.0.8, walker@npm:~1.0.5": version: 1.0.8 resolution: "walker@npm:1.0.8" @@ -24559,15 +21998,6 @@ __metadata: languageName: node linkType: hard -"wbuf@npm:^1.1.0, wbuf@npm:^1.7.3": - version: 1.7.3 - resolution: "wbuf@npm:1.7.3" - dependencies: - minimalistic-assert: "npm:^1.0.0" - checksum: 10c0/56edcc5ef2b3d30913ba8f1f5cccc364d180670b24d5f3f8849c1e6fb514e5c7e3a87548ae61227a82859eba6269c11393ae24ce12a2ea1ecb9b465718ddced7 - languageName: node - linkType: hard - "wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" @@ -24584,6 +22014,13 @@ __metadata: languageName: node linkType: hard +"web-namespaces@npm:^2.0.0": + version: 2.0.1 + resolution: "web-namespaces@npm:2.0.1" + checksum: 10c0/df245f466ad83bd5cd80bfffc1674c7f64b7b84d1de0e4d2c0934fb0782e0a599164e7197a4bce310ee3342fd61817b8047ff04f076a1ce12dd470584142a4bd + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -24591,28 +22028,6 @@ __metadata: languageName: node linkType: hard -"webpack-bundle-analyzer@npm:^4.5.0": - version: 4.10.2 - resolution: "webpack-bundle-analyzer@npm:4.10.2" - dependencies: - "@discoveryjs/json-ext": "npm:0.5.7" - acorn: "npm:^8.0.4" - acorn-walk: "npm:^8.0.0" - commander: "npm:^7.2.0" - debounce: "npm:^1.2.1" - escape-string-regexp: "npm:^4.0.0" - gzip-size: "npm:^6.0.0" - html-escaper: "npm:^2.0.2" - opener: "npm:^1.5.2" - picocolors: "npm:^1.0.0" - sirv: "npm:^2.0.3" - ws: "npm:^7.3.1" - bin: - webpack-bundle-analyzer: lib/bin/analyzer.js - checksum: 10c0/00603040e244ead15b2d92981f0559fa14216381349412a30070a7358eb3994cd61a8221d34a3b3fb8202dc3d1c5ee1fbbe94c5c52da536e5b410aa1cf279a48 - languageName: node - linkType: hard - "webpack-dev-middleware@npm:^4.0.0": version: 4.3.0 resolution: "webpack-dev-middleware@npm:4.3.0" @@ -24629,68 +22044,6 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:^5.3.4": - version: 5.3.4 - resolution: "webpack-dev-middleware@npm:5.3.4" - dependencies: - colorette: "npm:^2.0.10" - memfs: "npm:^3.4.3" - mime-types: "npm:^2.1.31" - range-parser: "npm:^1.2.1" - schema-utils: "npm:^4.0.0" - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - checksum: 10c0/257df7d6bc5494d1d3cb66bba70fbdf5a6e0423e39b6420f7631aeb52435afbfbff8410a62146dcdf3d2f945c62e03193aae2ac1194a2f7d5a2523b9d194e9e1 - languageName: node - linkType: hard - -"webpack-dev-server@npm:^4.9.3": - version: 4.15.2 - resolution: "webpack-dev-server@npm:4.15.2" - dependencies: - "@types/bonjour": "npm:^3.5.9" - "@types/connect-history-api-fallback": "npm:^1.3.5" - "@types/express": "npm:^4.17.13" - "@types/serve-index": "npm:^1.9.1" - "@types/serve-static": "npm:^1.13.10" - "@types/sockjs": "npm:^0.3.33" - "@types/ws": "npm:^8.5.5" - ansi-html-community: "npm:^0.0.8" - bonjour-service: "npm:^1.0.11" - chokidar: "npm:^3.5.3" - colorette: "npm:^2.0.10" - compression: "npm:^1.7.4" - connect-history-api-fallback: "npm:^2.0.0" - default-gateway: "npm:^6.0.3" - express: "npm:^4.17.3" - graceful-fs: "npm:^4.2.6" - html-entities: "npm:^2.3.2" - http-proxy-middleware: "npm:^2.0.3" - ipaddr.js: "npm:^2.0.1" - launch-editor: "npm:^2.6.0" - open: "npm:^8.0.9" - p-retry: "npm:^4.5.0" - rimraf: "npm:^3.0.2" - schema-utils: "npm:^4.0.0" - selfsigned: "npm:^2.1.1" - serve-index: "npm:^1.9.1" - sockjs: "npm:^0.3.24" - spdy: "npm:^4.0.2" - webpack-dev-middleware: "npm:^5.3.4" - ws: "npm:^8.13.0" - peerDependencies: - webpack: ^4.37.0 || ^5.0.0 - peerDependenciesMeta: - webpack: - optional: true - webpack-cli: - optional: true - bin: - webpack-dev-server: bin/webpack-dev-server.js - checksum: 10c0/625bd5b79360afcf98782c8b1fd710b180bb0e96d96b989defff550c546890010ceea82ffbecb2a0a23f7f018bc72f2dee7b3070f7b448fb0110df6657fb2904 - languageName: node - linkType: hard - "webpack-hot-middleware@npm:^2.25.0": version: 2.26.1 resolution: "webpack-hot-middleware@npm:2.26.1" @@ -24702,17 +22055,6 @@ __metadata: languageName: node linkType: hard -"webpack-merge@npm:^5.8.0": - version: 5.10.0 - resolution: "webpack-merge@npm:5.10.0" - dependencies: - clone-deep: "npm:^4.0.1" - flat: "npm:^5.0.2" - wildcard: "npm:^2.0.0" - checksum: 10c0/b607c84cabaf74689f965420051a55a08722d897bdd6c29cb0b2263b451c090f962d41ecf8c9bf56b0ab3de56e65476ace0a8ecda4f4a4663684243d90e0512b - languageName: node - linkType: hard - "webpack-sources@npm:^1.1.0": version: 1.4.3 resolution: "webpack-sources@npm:1.4.3" @@ -24723,14 +22065,14 @@ __metadata: languageName: node linkType: hard -"webpack-sources@npm:^3.2.2, webpack-sources@npm:^3.3.3": +"webpack-sources@npm:^3.3.3": version: 3.3.3 resolution: "webpack-sources@npm:3.3.3" checksum: 10c0/ab732f6933b513ba4d505130418995ddef6df988421fccf3289e53583c6a39e205c4a0739cee98950964552d3006604912679c736031337fb4a9d78d8576ed40 languageName: node linkType: hard -"webpack@npm:^5.3.2, webpack@npm:^5.73.0": +"webpack@npm:^5.3.2": version: 5.104.1 resolution: "webpack@npm:5.104.1" dependencies: @@ -24768,47 +22110,6 @@ __metadata: languageName: node linkType: hard -"webpackbar@npm:^5.0.2": - version: 5.0.2 - resolution: "webpackbar@npm:5.0.2" - dependencies: - chalk: "npm:^4.1.0" - consola: "npm:^2.15.3" - pretty-time: "npm:^1.1.0" - std-env: "npm:^3.0.1" - peerDependencies: - webpack: 3 || 4 || 5 - checksum: 10c0/336568a6ed1c1ad743c8d20a5cab5875a7ebe1e96181f49ae0a1a897f1a59d1661d837574a25d8ba9dfa4f2f705bd46ca0cd037ff60286ff70fb8d9db2b0c123 - languageName: node - linkType: hard - -"websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": - version: 0.7.4 - resolution: "websocket-driver@npm:0.7.4" - dependencies: - http-parser-js: "npm:>=0.5.1" - safe-buffer: "npm:>=5.1.0" - websocket-extensions: "npm:>=0.1.1" - checksum: 10c0/5f09547912b27bdc57bac17b7b6527d8993aa4ac8a2d10588bb74aebaf785fdcf64fea034aae0c359b7adff2044dd66f3d03866e4685571f81b13e548f9021f1 - languageName: node - linkType: hard - -"websocket-extensions@npm:>=0.1.1": - version: 0.1.4 - resolution: "websocket-extensions@npm:0.1.4" - checksum: 10c0/bbc8c233388a0eb8a40786ee2e30d35935cacbfe26ab188b3e020987e85d519c2009fe07cfc37b7f718b85afdba7e54654c9153e6697301f72561bfe429177e0 - languageName: node - linkType: hard - -"whatwg-encoding@npm:^3.1.1": - version: 3.1.1 - resolution: "whatwg-encoding@npm:3.1.1" - dependencies: - iconv-lite: "npm:0.6.3" - checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e - languageName: node - linkType: hard - "whatwg-fetch@npm:^3.0.0": version: 3.6.20 resolution: "whatwg-fetch@npm:3.6.20" @@ -24816,13 +22117,6 @@ __metadata: languageName: node linkType: hard -"whatwg-mimetype@npm:^4.0.0": - version: 4.0.0 - resolution: "whatwg-mimetype@npm:4.0.0" - checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df - languageName: node - linkType: hard - "whatwg-url-minimum@npm:^0.1.2": version: 0.1.2 resolution: "whatwg-url-minimum@npm:0.1.2" @@ -24908,7 +22202,7 @@ __metadata: languageName: node linkType: hard -"which@npm:^1.2.9, which@npm:^1.3.1": +"which@npm:^1.2.9": version: 1.3.1 resolution: "which@npm:1.3.1" dependencies: @@ -24961,22 +22255,6 @@ __metadata: languageName: node linkType: hard -"widest-line@npm:^4.0.1": - version: 4.0.1 - resolution: "widest-line@npm:4.0.1" - dependencies: - string-width: "npm:^5.0.1" - checksum: 10c0/7da9525ba45eaf3e4ed1a20f3dcb9b85bd9443962450694dae950f4bdd752839747bbc14713522b0b93080007de8e8af677a61a8c2114aa553ad52bde72d0f9c - languageName: node - linkType: hard - -"wildcard@npm:^2.0.0": - version: 2.0.1 - resolution: "wildcard@npm:2.0.1" - checksum: 10c0/08f70cd97dd9a20aea280847a1fe8148e17cae7d231640e41eb26d2388697cbe65b67fd9e68715251c39b080c5ae4f76d71a9a69fa101d897273efdfb1b58bf7 - languageName: node - linkType: hard - "window-size@npm:1.1.1": version: 1.1.1 resolution: "window-size@npm:1.1.1" @@ -25045,17 +22323,6 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^8.0.1": - version: 8.1.0 - resolution: "wrap-ansi@npm:8.1.0" - dependencies: - ansi-styles: "npm:^6.1.0" - string-width: "npm:^5.0.1" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 - languageName: node - linkType: hard - "wrap-ansi@npm:^9.0.0": version: 9.0.2 resolution: "wrap-ansi@npm:9.0.2" @@ -25096,7 +22363,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^7, ws@npm:^7.3.1, ws@npm:^7.5.10": +"ws@npm:^7, ws@npm:^7.5.10": version: 7.5.10 resolution: "ws@npm:7.5.10" peerDependencies: @@ -25111,7 +22378,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.12.1, ws@npm:^8.13.0": +"ws@npm:^8.12.1": version: 8.19.0 resolution: "ws@npm:8.19.0" peerDependencies: @@ -25150,17 +22417,6 @@ __metadata: languageName: node linkType: hard -"xml-js@npm:^1.6.11": - version: 1.6.11 - resolution: "xml-js@npm:1.6.11" - dependencies: - sax: "npm:^1.2.4" - bin: - xml-js: ./bin/cli.js - checksum: 10c0/c83631057f10bf90ea785cee434a8a1a0030c7314fe737ad9bf568a281083b565b28b14c9e9ba82f11fc9dc582a3a907904956af60beb725be1c9ad4b030bc5a - languageName: node - linkType: hard - "xml2js@npm:0.6.0": version: 0.6.0 resolution: "xml2js@npm:0.6.0" @@ -25227,7 +22483,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:1.10.2, yaml@npm:^1.10.0, yaml@npm:^1.10.2, yaml@npm:^1.7.2": +"yaml@npm:1.10.2, yaml@npm:^1.10.0": version: 1.10.2 resolution: "yaml@npm:1.10.2" checksum: 10c0/5c28b9eb7adc46544f28d9a8d20c5b3cb1215a886609a2fd41f51628d8aaa5878ccd628b755dbcd29f6bb4921bd04ffbc6dcc370689bb96e594e2f9813d2605f @@ -25402,9 +22658,23 @@ __metadata: languageName: node linkType: hard +"zod@npm:^4.3.6": + version: 4.4.3 + resolution: "zod@npm:4.4.3" + checksum: 10c0/7ea31b558e88f9faf44f31dd185e2e1cbf51fed3081787fb96cc2534749b50c0acfc6da7f0922a7353ed092dd358c7d50c28ea96c94d04af64191bd33152eca3 + languageName: node + linkType: hard + "zwitch@npm:^1.0.0": version: 1.0.5 resolution: "zwitch@npm:1.0.5" checksum: 10c0/26dc7d32e5596824b565db1da9650d00d32659c1211195bef50c25c60820f9c942aa7abefe678fc1ed0b97c1755036ac1bde5f97881d7d0e73e04e02aca56957 languageName: node linkType: hard + +"zwitch@npm:^2.0.0, zwitch@npm:^2.0.4": + version: 2.0.4 + resolution: "zwitch@npm:2.0.4" + checksum: 10c0/3c7830cdd3378667e058ffdb4cf2bb78ac5711214e2725900873accb23f3dfe5f9e7e5a06dcdc5f29605da976fc45c26d9a13ca334d6eea2245a15e77b8fc06e + languageName: node + linkType: hard From 1b2b0433ea918b2949b192dfc52bef30f568430c Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Sun, 28 Jun 2026 15:32:41 +0200 Subject: [PATCH 52/94] chore: fix docs deployment for 5.x --- .../docs/components/Appbar/AppbarAction.mdx | 4 +- .../components/Appbar/AppbarBackAction.mdx | 4 +- docs/6.x/docs/components/Badge.mdx | 20 +- docs/6.x/docs/components/Button/Button.mdx | 8 +- .../docs/components/Checkbox/CheckboxItem.mdx | 4 +- docs/6.x/docs/components/Chip/Chip.mdx | 8 +- .../DataTable/DataTablePagination.mdx | 4 +- .../components/Drawer/DrawerCollapsedItem.mdx | 4 +- .../6.x/docs/components/Drawer/DrawerItem.mdx | 4 +- docs/6.x/docs/components/FAB/FAB.mdx | 32 +- docs/6.x/docs/components/FAB/FABExtended.mdx | 32 +- .../docs/components/IconButton/IconButton.mdx | 4 +- .../docs/components/List/ListAccordion.mdx | 4 +- docs/6.x/docs/components/Menu/MenuItem.mdx | 32 +- .../RadioButton/RadioButtonItem.mdx | 4 +- docs/6.x/docs/components/Switch/Switch.mdx | 4 +- .../components/ToggleButton/ToggleButton.mdx | 4 +- docs/src/data/componentDocs5x.json | 312 +++++++++++++----- docs/src/data/componentDocs6x.json | 279 ++++++++++------ scripts/generate-component-docs.ts | 95 +++++- 20 files changed, 616 insertions(+), 246 deletions(-) diff --git a/docs/6.x/docs/components/Appbar/AppbarAction.mdx b/docs/6.x/docs/components/Appbar/AppbarAction.mdx index cd55f9cdb7..5db23d4ba9 100644 --- a/docs/6.x/docs/components/Appbar/AppbarAction.mdx +++ b/docs/6.x/docs/components/Appbar/AppbarAction.mdx @@ -75,11 +75,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx b/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx index 592afcdf6f..4faa4ec18d 100644 --- a/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx +++ b/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx @@ -62,11 +62,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/Badge.mdx b/docs/6.x/docs/components/Badge.mdx index dba6c041ca..fbb1589d84 100644 --- a/docs/6.x/docs/components/Badge.mdx +++ b/docs/6.x/docs/components/Badge.mdx @@ -11,6 +11,10 @@ import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Badges are small status descriptors for UI elements. A badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object. +The badge is styled differently based on whether `children` is passed: +- Small dot when it doesn't have `children` +- Larger pill when it has `children` + @@ -53,14 +57,6 @@ export default MyComponent;
-### size - -
- - - -
- ### style
@@ -69,14 +65,6 @@ export default MyComponent;
-### ref - -
- - - -
- ### theme
diff --git a/docs/6.x/docs/components/Button/Button.mdx b/docs/6.x/docs/components/Button/Button.mdx index 8daa42b8c3..80e87fbf84 100644 --- a/docs/6.x/docs/components/Button/Button.mdx +++ b/docs/6.x/docs/components/Button/Button.mdx @@ -126,11 +126,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
@@ -142,11 +142,11 @@ export default MyComponent;
-### accessibilityRole +### role
- +
diff --git a/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx b/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx index 016438cb2f..c81380f62b 100644 --- a/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx +++ b/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx @@ -85,11 +85,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/Chip/Chip.mdx b/docs/6.x/docs/components/Chip/Chip.mdx index 27fa00b7d3..f712768036 100644 --- a/docs/6.x/docs/components/Chip/Chip.mdx +++ b/docs/6.x/docs/components/Chip/Chip.mdx @@ -132,11 +132,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
@@ -268,11 +268,11 @@ export default MyComponent;
-### accessibilityRole +### role
- + diff --git a/docs/6.x/docs/components/DataTable/DataTablePagination.mdx b/docs/6.x/docs/components/DataTable/DataTablePagination.mdx index 38f1dd0c36..9ededfa603 100644 --- a/docs/6.x/docs/components/DataTable/DataTablePagination.mdx +++ b/docs/6.x/docs/components/DataTable/DataTablePagination.mdx @@ -164,11 +164,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx b/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx index b17999c4df..2305db396d 100644 --- a/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx +++ b/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx @@ -106,11 +106,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/Drawer/DrawerItem.mdx b/docs/6.x/docs/components/Drawer/DrawerItem.mdx index dab7c90e67..4be921a561 100644 --- a/docs/6.x/docs/components/Drawer/DrawerItem.mdx +++ b/docs/6.x/docs/components/Drawer/DrawerItem.mdx @@ -88,11 +88,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/FAB/FAB.mdx b/docs/6.x/docs/components/FAB/FAB.mdx index 87ce7e9761..d672df9781 100644 --- a/docs/6.x/docs/components/FAB/FAB.mdx +++ b/docs/6.x/docs/components/FAB/FAB.mdx @@ -106,19 +106,43 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
-### accessibilityState +### aria-checked
- + + +
+ +### aria-selected + +
+ + + +
+ +### aria-busy + +
+ + + +
+ +### aria-expanded + +
+ +
diff --git a/docs/6.x/docs/components/FAB/FABExtended.mdx b/docs/6.x/docs/components/FAB/FABExtended.mdx index c5873ee2ee..98969dc964 100644 --- a/docs/6.x/docs/components/FAB/FABExtended.mdx +++ b/docs/6.x/docs/components/FAB/FABExtended.mdx @@ -128,19 +128,43 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
-### accessibilityState +### aria-checked
- + + +
+ +### aria-selected + +
+ + + +
+ +### aria-busy + +
+ + + +
+ +### aria-expanded + +
+ +
diff --git a/docs/6.x/docs/components/IconButton/IconButton.mdx b/docs/6.x/docs/components/IconButton/IconButton.mdx index 7daacaaf7f..849f8ce78e 100644 --- a/docs/6.x/docs/components/IconButton/IconButton.mdx +++ b/docs/6.x/docs/components/IconButton/IconButton.mdx @@ -107,11 +107,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/List/ListAccordion.mdx b/docs/6.x/docs/components/List/ListAccordion.mdx index ea06d3a081..acc851352f 100644 --- a/docs/6.x/docs/components/List/ListAccordion.mdx +++ b/docs/6.x/docs/components/List/ListAccordion.mdx @@ -234,11 +234,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/Menu/MenuItem.mdx b/docs/6.x/docs/components/Menu/MenuItem.mdx index d63e012efc..b3fb4e5110 100644 --- a/docs/6.x/docs/components/Menu/MenuItem.mdx +++ b/docs/6.x/docs/components/Menu/MenuItem.mdx @@ -163,19 +163,43 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
-### accessibilityState +### aria-checked
- + + +
+ +### aria-selected + +
+ + + +
+ +### aria-busy + +
+ + + +
+ +### aria-expanded + +
+ + diff --git a/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx index c74403899e..4bfea5da3d 100644 --- a/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx +++ b/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx @@ -91,11 +91,11 @@ export default MyComponent;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/6.x/docs/components/Switch/Switch.mdx b/docs/6.x/docs/components/Switch/Switch.mdx index 460b34024b..41df316693 100644 --- a/docs/6.x/docs/components/Switch/Switch.mdx +++ b/docs/6.x/docs/components/Switch/Switch.mdx @@ -110,11 +110,11 @@ Customize by overriding these `theme.colors` roles:
-### accessibilityLabel +### aria-label
- + diff --git a/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx b/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx index a0dca55b21..e359e46802 100644 --- a/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx +++ b/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx @@ -83,11 +83,11 @@ export default ToggleButtonExample;
-### accessibilityLabel +### aria-label
- +
diff --git a/docs/src/data/componentDocs5x.json b/docs/src/data/componentDocs5x.json index 0984e441a0..5aa299568c 100644 --- a/docs/src/data/componentDocs5x.json +++ b/docs/src/data/componentDocs5x.json @@ -88,7 +88,9 @@ } }, "type": "component", - "dependencies": ["src/components/ActivityIndicator.tsx"] + "dependencies": [ + "src/components/ActivityIndicator.tsx" + ] }, "Appbar/Appbar": { "filepath": "Appbar/Appbar.tsx", @@ -223,7 +225,9 @@ } }, "type": "component", - "dependencies": ["src/components/Appbar/Appbar.tsx"] + "dependencies": [ + "src/components/Appbar/Appbar.tsx" + ] }, "Appbar/AppbarAction": { "filepath": "Appbar/AppbarAction.tsx", @@ -346,7 +350,9 @@ } }, "type": "component", - "dependencies": ["src/components/Appbar/AppbarAction.tsx"], + "dependencies": [ + "src/components/Appbar/AppbarAction.tsx" + ], "group": "Appbar" }, "Appbar/AppbarBackAction": { @@ -449,7 +455,9 @@ } }, "type": "component", - "dependencies": ["src/components/Appbar/AppbarBackAction.tsx"], + "dependencies": [ + "src/components/Appbar/AppbarBackAction.tsx" + ], "group": "Appbar" }, "Appbar/AppbarContent": { @@ -625,7 +633,9 @@ } }, "type": "component", - "dependencies": ["src/components/Appbar/AppbarContent.tsx"], + "dependencies": [ + "src/components/Appbar/AppbarContent.tsx" + ], "group": "Appbar" }, "Appbar/AppbarHeader": { @@ -739,7 +749,9 @@ } }, "type": "component", - "dependencies": ["src/components/Appbar/AppbarHeader.tsx"], + "dependencies": [ + "src/components/Appbar/AppbarHeader.tsx" + ], "group": "Appbar" }, "Avatar/AvatarIcon": { @@ -801,7 +813,9 @@ } }, "type": "component", - "dependencies": ["src/components/Avatar/AvatarIcon.tsx"], + "dependencies": [ + "src/components/Avatar/AvatarIcon.tsx" + ], "group": "Avatar" }, "Avatar/AvatarImage": { @@ -913,7 +927,9 @@ } }, "type": "component", - "dependencies": ["src/components/Avatar/AvatarImage.tsx"], + "dependencies": [ + "src/components/Avatar/AvatarImage.tsx" + ], "group": "Avatar" }, "Avatar/AvatarText": { @@ -995,7 +1011,9 @@ } }, "type": "component", - "dependencies": ["src/components/Avatar/AvatarText.tsx"], + "dependencies": [ + "src/components/Avatar/AvatarText.tsx" + ], "group": "Avatar" }, "Badge": { @@ -1065,7 +1083,9 @@ "tsType": { "name": "ReactRefObject", "raw": "React.RefObject", - "elements": [{}] + "elements": [ + {} + ] }, "description": "" }, @@ -1079,7 +1099,9 @@ } }, "type": "component", - "dependencies": ["src/components/Badge.tsx"] + "dependencies": [ + "src/components/Badge.tsx" + ] }, "Banner": { "filepath": "Banner.tsx", @@ -1284,7 +1306,9 @@ } }, "type": "component", - "dependencies": ["src/components/Banner.tsx"] + "dependencies": [ + "src/components/Banner.tsx" + ] }, "BottomNavigation/BottomNavigation": { "filepath": "BottomNavigation/BottomNavigation.tsx", @@ -1298,7 +1322,9 @@ { "name": "SceneMap", "docblock": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance.", - "modifiers": ["static"], + "modifiers": [ + "static" + ], "params": [ { "name": "scenes", @@ -2292,7 +2318,9 @@ } }, "type": "component", - "dependencies": ["src/components/BottomNavigation/BottomNavigation.tsx"] + "dependencies": [ + "src/components/BottomNavigation/BottomNavigation.tsx" + ] }, "BottomNavigation/BottomNavigationBar": { "filepath": "BottomNavigation/BottomNavigationBar.tsx", @@ -3417,7 +3445,9 @@ } }, "type": "component", - "dependencies": ["src/components/Button/Button.tsx"] + "dependencies": [ + "src/components/Button/Button.tsx" + ] }, "Card/Card": { "filepath": "Card/Card.tsx", @@ -3660,7 +3690,9 @@ } }, "type": "component", - "dependencies": ["src/components/Card/Card.tsx"] + "dependencies": [ + "src/components/Card/Card.tsx" + ] }, "Card/CardActions": { "filepath": "Card/CardActions.tsx", @@ -3704,7 +3736,9 @@ } }, "type": "component", - "dependencies": ["src/components/Card/CardActions.tsx"], + "dependencies": [ + "src/components/Card/CardActions.tsx" + ], "group": "Card" }, "Card/CardContent": { @@ -3769,7 +3803,9 @@ } }, "type": "component", - "dependencies": ["src/components/Card/CardContent.tsx"], + "dependencies": [ + "src/components/Card/CardContent.tsx" + ], "group": "Card" }, "Card/CardCover": { @@ -3820,7 +3856,9 @@ } }, "type": "component", - "dependencies": ["src/components/Card/CardCover.tsx"], + "dependencies": [ + "src/components/Card/CardCover.tsx" + ], "group": "Card" }, "Card/CardTitle": { @@ -4069,7 +4107,9 @@ } }, "type": "component", - "dependencies": ["src/components/Card/CardTitle.tsx"], + "dependencies": [ + "src/components/Card/CardTitle.tsx" + ], "group": "Card" }, "Checkbox/Checkbox": { @@ -4165,7 +4205,9 @@ } }, "type": "component", - "dependencies": ["src/components/Checkbox/Checkbox.tsx"] + "dependencies": [ + "src/components/Checkbox/Checkbox.tsx" + ] }, "Checkbox/CheckboxAndroid": { "filepath": "Checkbox/CheckboxAndroid.tsx", @@ -4260,7 +4302,9 @@ } }, "type": "component", - "dependencies": ["src/components/Checkbox/CheckboxAndroid.tsx"], + "dependencies": [ + "src/components/Checkbox/CheckboxAndroid.tsx" + ], "group": "Checkbox" }, "Checkbox/CheckboxIOS": { @@ -4349,7 +4393,9 @@ } }, "type": "component", - "dependencies": ["src/components/Checkbox/CheckboxIOS.tsx"], + "dependencies": [ + "src/components/Checkbox/CheckboxIOS.tsx" + ], "group": "Checkbox" }, "Checkbox/CheckboxItem": { @@ -4595,7 +4641,9 @@ } }, "type": "component", - "dependencies": ["src/components/Checkbox/CheckboxItem.tsx"], + "dependencies": [ + "src/components/Checkbox/CheckboxItem.tsx" + ], "group": "Checkbox" }, "Chip/Chip": { @@ -4948,7 +4996,9 @@ } }, "type": "component", - "dependencies": ["src/components/Chip/Chip.tsx"] + "dependencies": [ + "src/components/Chip/Chip.tsx" + ] }, "DataTable/DataTable": { "filepath": "DataTable/DataTable.tsx", @@ -4985,7 +5035,9 @@ } }, "type": "component", - "dependencies": ["src/components/DataTable/DataTable.tsx"] + "dependencies": [ + "src/components/DataTable/DataTable.tsx" + ] }, "DataTable/DataTableCell": { "filepath": "DataTable/DataTableCell.tsx", @@ -5078,7 +5130,9 @@ } }, "type": "component", - "dependencies": ["src/components/DataTable/DataTableCell.tsx"], + "dependencies": [ + "src/components/DataTable/DataTableCell.tsx" + ], "group": "DataTable" }, "DataTable/DataTableHeader": { @@ -5123,7 +5177,9 @@ } }, "type": "component", - "dependencies": ["src/components/DataTable/DataTableHeader.tsx"], + "dependencies": [ + "src/components/DataTable/DataTableHeader.tsx" + ], "group": "DataTable" }, "DataTable/DataTablePagination": { @@ -5300,7 +5356,9 @@ } }, "type": "component", - "dependencies": ["src/components/DataTable/DataTablePagination.tsx"], + "dependencies": [ + "src/components/DataTable/DataTablePagination.tsx" + ], "group": "DataTable" }, "DataTable/DataTableRow": { @@ -5375,7 +5433,9 @@ } }, "type": "component", - "dependencies": ["src/components/DataTable/DataTableRow.tsx"], + "dependencies": [ + "src/components/DataTable/DataTableRow.tsx" + ], "group": "DataTable" }, "DataTable/DataTableTitle": { @@ -5498,7 +5558,9 @@ } }, "type": "component", - "dependencies": ["src/components/DataTable/DataTableTitle.tsx"], + "dependencies": [ + "src/components/DataTable/DataTableTitle.tsx" + ], "group": "DataTable" }, "Dialog/Dialog": { @@ -5604,7 +5666,9 @@ } }, "type": "component", - "dependencies": ["src/components/Dialog/Dialog.tsx"] + "dependencies": [ + "src/components/Dialog/Dialog.tsx" + ] }, "Dialog/DialogActions": { "filepath": "Dialog/DialogActions.tsx", @@ -5648,7 +5712,9 @@ } }, "type": "component", - "dependencies": ["src/components/Dialog/DialogActions.tsx"], + "dependencies": [ + "src/components/Dialog/DialogActions.tsx" + ], "group": "Dialog" }, "Dialog/DialogContent": { @@ -5686,7 +5752,9 @@ } }, "type": "component", - "dependencies": ["src/components/Dialog/DialogContent.tsx"], + "dependencies": [ + "src/components/Dialog/DialogContent.tsx" + ], "group": "Dialog" }, "Dialog/DialogIcon": { @@ -5735,7 +5803,9 @@ } }, "type": "component", - "dependencies": ["src/components/Dialog/DialogIcon.tsx"], + "dependencies": [ + "src/components/Dialog/DialogIcon.tsx" + ], "group": "Dialog" }, "Dialog/DialogScrollArea": { @@ -5780,7 +5850,9 @@ } }, "type": "component", - "dependencies": ["src/components/Dialog/DialogScrollArea.tsx"], + "dependencies": [ + "src/components/Dialog/DialogScrollArea.tsx" + ], "group": "Dialog" }, "Dialog/DialogTitle": { @@ -5825,7 +5897,9 @@ } }, "type": "component", - "dependencies": ["src/components/Dialog/DialogTitle.tsx"], + "dependencies": [ + "src/components/Dialog/DialogTitle.tsx" + ], "group": "Dialog" }, "Divider": { @@ -5891,7 +5965,9 @@ } }, "type": "component", - "dependencies": ["src/components/Divider.tsx"] + "dependencies": [ + "src/components/Divider.tsx" + ] }, "Drawer/DrawerCollapsedItem": { "filepath": "Drawer/DrawerCollapsedItem.tsx", @@ -6032,7 +6108,9 @@ } }, "type": "component", - "dependencies": ["src/components/Drawer/DrawerCollapsedItem.tsx"], + "dependencies": [ + "src/components/Drawer/DrawerCollapsedItem.tsx" + ], "group": "Drawer" }, "Drawer/DrawerItem": { @@ -6191,7 +6269,9 @@ } }, "type": "component", - "dependencies": ["src/components/Drawer/DrawerItem.tsx"], + "dependencies": [ + "src/components/Drawer/DrawerItem.tsx" + ], "group": "Drawer" }, "Drawer/DrawerSection": { @@ -6261,7 +6341,9 @@ } }, "type": "component", - "dependencies": ["src/components/Drawer/DrawerSection.tsx"], + "dependencies": [ + "src/components/Drawer/DrawerSection.tsx" + ], "group": "Drawer" }, "FAB/FAB": { @@ -6574,7 +6656,9 @@ } }, "type": "component", - "dependencies": ["src/components/FAB/FAB.tsx"] + "dependencies": [ + "src/components/FAB/FAB.tsx" + ] }, "FAB/AnimatedFAB": { "filepath": "FAB/AnimatedFAB.tsx", @@ -6856,7 +6940,9 @@ } }, "type": "component", - "dependencies": ["src/components/FAB/AnimatedFAB.tsx"] + "dependencies": [ + "src/components/FAB/AnimatedFAB.tsx" + ] }, "FAB/FABGroup": { "filepath": "FAB/FABGroup.tsx", @@ -7298,7 +7384,9 @@ } }, "type": "component", - "dependencies": ["src/components/FAB/FABGroup.tsx"], + "dependencies": [ + "src/components/FAB/FABGroup.tsx" + ], "group": "FAB" }, "HelperText/HelperText": { @@ -7412,7 +7500,9 @@ } }, "type": "component", - "dependencies": ["src/components/HelperText/HelperText.tsx"] + "dependencies": [ + "src/components/HelperText/HelperText.tsx" + ] }, "IconButton/IconButton": { "filepath": "IconButton/IconButton.tsx", @@ -7621,7 +7711,9 @@ } }, "type": "component", - "dependencies": ["src/components/IconButton/IconButton.tsx"] + "dependencies": [ + "src/components/IconButton/IconButton.tsx" + ] }, "Icon": { "filepath": "Icon.tsx", @@ -7679,7 +7771,9 @@ } }, "type": "component", - "dependencies": ["src/components/Icon.tsx"] + "dependencies": [ + "src/components/Icon.tsx" + ] }, "List/ListAccordion": { "filepath": "List/ListAccordion.tsx", @@ -8028,7 +8122,9 @@ } }, "type": "component", - "dependencies": ["src/components/List/ListAccordion.tsx"], + "dependencies": [ + "src/components/List/ListAccordion.tsx" + ], "group": "List" }, "List/ListAccordionGroup": { @@ -8100,7 +8196,9 @@ } }, "type": "component", - "dependencies": ["src/components/List/ListAccordionGroup.tsx"], + "dependencies": [ + "src/components/List/ListAccordionGroup.tsx" + ], "group": "List" }, "List/ListIcon": { @@ -8151,7 +8249,9 @@ } }, "type": "component", - "dependencies": ["src/components/List/ListIcon.tsx"], + "dependencies": [ + "src/components/List/ListIcon.tsx" + ], "group": "List" }, "List/ListItem": { @@ -8439,7 +8539,9 @@ } }, "type": "component", - "dependencies": ["src/components/List/ListItem.tsx"], + "dependencies": [ + "src/components/List/ListItem.tsx" + ], "group": "List" }, "List/ListSection": { @@ -8504,7 +8606,9 @@ } }, "type": "component", - "dependencies": ["src/components/List/ListSection.tsx"], + "dependencies": [ + "src/components/List/ListSection.tsx" + ], "group": "List" }, "List/ListSubheader": { @@ -8548,7 +8652,9 @@ } }, "type": "component", - "dependencies": ["src/components/List/ListSubheader.tsx"], + "dependencies": [ + "src/components/List/ListSubheader.tsx" + ], "group": "List" }, "Menu/Menu": { @@ -8759,7 +8865,9 @@ } }, "type": "component", - "dependencies": ["src/components/Menu/Menu.tsx"] + "dependencies": [ + "src/components/Menu/Menu.tsx" + ] }, "Menu/MenuItem": { "filepath": "Menu/MenuItem.tsx", @@ -8950,7 +9058,9 @@ } }, "type": "component", - "dependencies": ["src/components/Menu/MenuItem.tsx"], + "dependencies": [ + "src/components/Menu/MenuItem.tsx" + ], "group": "Menu" }, "Modal": { @@ -9088,7 +9198,9 @@ } }, "type": "component", - "dependencies": ["src/components/Modal.tsx"] + "dependencies": [ + "src/components/Modal.tsx" + ] }, "Portal/Portal": { "filepath": "Portal/Portal.tsx", @@ -9129,7 +9241,9 @@ } }, "type": "component", - "dependencies": ["src/components/Portal/Portal.tsx"] + "dependencies": [ + "src/components/Portal/Portal.tsx" + ] }, "Portal/PortalHost": { "filepath": "Portal/PortalHost.tsx", @@ -9153,7 +9267,9 @@ } }, "type": "component", - "dependencies": ["src/components/Portal/PortalHost.tsx"], + "dependencies": [ + "src/components/Portal/PortalHost.tsx" + ], "group": "Portal" }, "ProgressBar": { @@ -9263,7 +9379,9 @@ } }, "type": "component", - "dependencies": ["src/components/ProgressBar.tsx"] + "dependencies": [ + "src/components/ProgressBar.tsx" + ] }, "RadioButton/RadioButton": { "filepath": "RadioButton/RadioButton.tsx", @@ -9361,7 +9479,9 @@ } }, "type": "component", - "dependencies": ["src/components/RadioButton/RadioButton.tsx"] + "dependencies": [ + "src/components/RadioButton/RadioButton.tsx" + ] }, "RadioButton/RadioButtonAndroid": { "filepath": "RadioButton/RadioButtonAndroid.tsx", @@ -9459,7 +9579,9 @@ } }, "type": "component", - "dependencies": ["src/components/RadioButton/RadioButtonAndroid.tsx"], + "dependencies": [ + "src/components/RadioButton/RadioButtonAndroid.tsx" + ], "group": "RadioButton" }, "RadioButton/RadioButtonGroup": { @@ -9513,7 +9635,9 @@ } }, "type": "component", - "dependencies": ["src/components/RadioButton/RadioButtonGroup.tsx"], + "dependencies": [ + "src/components/RadioButton/RadioButtonGroup.tsx" + ], "group": "RadioButton" }, "RadioButton/RadioButtonIOS": { @@ -9605,7 +9729,9 @@ } }, "type": "component", - "dependencies": ["src/components/RadioButton/RadioButtonIOS.tsx"], + "dependencies": [ + "src/components/RadioButton/RadioButtonIOS.tsx" + ], "group": "RadioButton" }, "RadioButton/RadioButtonItem": { @@ -9850,7 +9976,9 @@ } }, "type": "component", - "dependencies": ["src/components/RadioButton/RadioButtonItem.tsx"], + "dependencies": [ + "src/components/RadioButton/RadioButtonItem.tsx" + ], "group": "RadioButton" }, "Searchbar": { @@ -10232,7 +10360,9 @@ } }, "type": "component", - "dependencies": ["src/components/Searchbar.tsx"] + "dependencies": [ + "src/components/Searchbar.tsx" + ] }, "SegmentedButtons/SegmentedButtons": { "filepath": "SegmentedButtons/SegmentedButtons.tsx", @@ -10424,7 +10554,9 @@ } }, "type": "component", - "dependencies": ["src/components/SegmentedButtons/SegmentedButtons.tsx"] + "dependencies": [ + "src/components/SegmentedButtons/SegmentedButtons.tsx" + ] }, "Snackbar": { "filepath": "Snackbar.tsx", @@ -10676,7 +10808,9 @@ } }, "type": "component", - "dependencies": ["src/components/Snackbar.tsx"] + "dependencies": [ + "src/components/Snackbar.tsx" + ] }, "Surface": { "filepath": "Surface.tsx", @@ -10820,7 +10954,9 @@ } }, "type": "component", - "dependencies": ["src/components/Surface.tsx"] + "dependencies": [ + "src/components/Surface.tsx" + ] }, "Switch/Switch": { "filepath": "Switch/Switch.tsx", @@ -10884,7 +11020,9 @@ } }, "type": "component", - "dependencies": ["src/components/Switch/Switch.tsx"] + "dependencies": [ + "src/components/Switch/Switch.tsx" + ] }, "TextInput/TextInput": { "filepath": "TextInput/TextInput.tsx", @@ -11211,7 +11349,9 @@ } }, "type": "component", - "dependencies": ["src/components/TextInput/TextInput.tsx"] + "dependencies": [ + "src/components/TextInput/TextInput.tsx" + ] }, "TextInput/Adornment/TextInputAffix": { "filepath": "TextInput/Adornment/TextInputAffix.tsx", @@ -11309,7 +11449,9 @@ } }, "type": "component", - "dependencies": ["src/components/TextInput/Adornment/TextInputAffix.tsx"], + "dependencies": [ + "src/components/TextInput/Adornment/TextInputAffix.tsx" + ], "group": "TextInput" }, "TextInput/Adornment/TextInputIcon": { @@ -11409,7 +11551,9 @@ } }, "type": "component", - "dependencies": ["src/components/TextInput/Adornment/TextInputIcon.tsx"], + "dependencies": [ + "src/components/TextInput/Adornment/TextInputIcon.tsx" + ], "group": "TextInput" }, "ToggleButton/ToggleButton": { @@ -11570,7 +11714,9 @@ } }, "type": "component", - "dependencies": ["src/components/ToggleButton/ToggleButton.tsx"] + "dependencies": [ + "src/components/ToggleButton/ToggleButton.tsx" + ] }, "ToggleButton/ToggleButtonGroup": { "filepath": "ToggleButton/ToggleButtonGroup.tsx", @@ -11632,7 +11778,9 @@ } }, "type": "component", - "dependencies": ["src/components/ToggleButton/ToggleButtonGroup.tsx"], + "dependencies": [ + "src/components/ToggleButton/ToggleButtonGroup.tsx" + ], "group": "ToggleButton" }, "ToggleButton/ToggleButtonRow": { @@ -11699,7 +11847,9 @@ } }, "type": "component", - "dependencies": ["src/components/ToggleButton/ToggleButtonRow.tsx"], + "dependencies": [ + "src/components/ToggleButton/ToggleButtonRow.tsx" + ], "group": "ToggleButton" }, "Tooltip/Tooltip": { @@ -11767,7 +11917,9 @@ } }, "type": "component", - "dependencies": ["src/components/Tooltip/Tooltip.tsx"] + "dependencies": [ + "src/components/Tooltip/Tooltip.tsx" + ] }, "TouchableRipple/TouchableRipple": { "filepath": "TouchableRipple/TouchableRipple.tsx", @@ -11966,7 +12118,9 @@ } }, "type": "component", - "dependencies": ["src/components/TouchableRipple/TouchableRipple.tsx"] + "dependencies": [ + "src/components/TouchableRipple/TouchableRipple.tsx" + ] }, "Typography/Text": { "filepath": "Typography/Text.tsx", @@ -12023,7 +12177,9 @@ } }, "type": "component", - "dependencies": ["src/components/Typography/Text.tsx"] + "dependencies": [ + "src/components/Typography/Text.tsx" + ] } } } diff --git a/docs/src/data/componentDocs6x.json b/docs/src/data/componentDocs6x.json index dcacb1f99f..56b356912b 100644 --- a/docs/src/data/componentDocs6x.json +++ b/docs/src/data/componentDocs6x.json @@ -272,7 +272,7 @@ }, "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -380,7 +380,7 @@ }, "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -991,10 +991,10 @@ "Badge": { "filepath": "Badge.tsx", "title": "Badge", - "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", + "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\nThe badge is styled differently based on whether `children` is passed:\n- Small dot when it doesn't have `children`\n- Larger pill when it has `children`\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", "link": "badge", "data": { - "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", + "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\nThe badge is styled differently based on whether `children` is passed:\n- Small dot when it doesn't have `children`\n- Larger pill when it has `children`\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", "displayName": "Badge", "methods": [], "statics": [], @@ -1026,17 +1026,6 @@ }, "description": "Content of the `Badge`." }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the `Badge`.", - "defaultValue": { - "value": "20", - "computed": false - } - }, "style": { "required": false, "tsType": { @@ -1050,17 +1039,6 @@ }, "description": "" }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - {} - ] - }, - "description": "" - }, "theme": { "required": false, "tsType": { @@ -1424,7 +1402,7 @@ ] } }, - "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation` is a controlled component, which means the `index` needs to be updated via the `onIndexChange` callback." + "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `aria-label`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation` is a controlled component, which means the `index` needs to be updated via the `onIndexChange` callback." }, "onIndexChange": { "required": true, @@ -1738,7 +1716,7 @@ } } }, - "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default." + "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route['aria-label']` by default." }, "getBadge": { "required": false, @@ -2317,7 +2295,7 @@ ] } }, - "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation.Bar` is a controlled component, which means the `index` needs to be updated via the `onTabPress` callback." + "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `aria-label`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation.Bar` is a controlled component, which means the `index` needs to be updated via the `onTabPress` callback." }, "renderIcon": { "required": false, @@ -2546,9 +2524,9 @@ } } }, - "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default.", + "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route['aria-label']` by default.", "defaultValue": { - "value": "({ route }: { route: Route }) =>\nroute.accessibilityLabel", + "value": "({ route }: { route: Route }) => route['aria-label']", "computed": false } }, @@ -3097,7 +3075,7 @@ }, "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -3111,10 +3089,10 @@ }, "description": "Accessibility hint for the button. This is read by the screen reader when the user taps the button." }, - "accessibilityRole": { + "role": { "required": false, "tsType": { - "name": "AccessibilityRole" + "name": "Role" }, "description": "Accessibility role for the button. The \"button\" role is set by default.", "defaultValue": { @@ -4206,7 +4184,7 @@ }, "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -4448,7 +4426,7 @@ }, "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -4659,7 +4637,7 @@ }, "description": "Specifies the largest possible scale a text font can reach." }, - "accessibilityRole": { + "role": { "defaultValue": { "value": "'button'", "computed": false @@ -4986,7 +4964,7 @@ }, "description": "Label text to display which indicates current pagination." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -5720,7 +5698,7 @@ }, "description": "Specifies the largest possible scale a label font can reach." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -5834,7 +5812,7 @@ }, "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -6079,19 +6057,50 @@ }, "description": "Function to execute on press." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" }, "description": "Accessibility label. Falls back to nothing if unset." }, - "accessibilityState": { + "aria-checked": { + "required": false, + "tsType": { + "name": "union", + "raw": "boolean | 'mixed'", + "elements": [ + { + "name": "boolean" + }, + { + "name": "literal", + "value": "'mixed'" + } + ] + }, + "description": "Indicates whether the element is checked. Accepts `true`, `false`,\nor `'mixed'` for an indeterminate state." + }, + "aria-selected": { "required": false, "tsType": { - "name": "AccessibilityState" + "name": "boolean" }, - "description": "Accessibility state forwarded to the underlying button." + "description": "Indicates whether the element is selected." + }, + "aria-busy": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Indicates whether the element is currently busy (e.g. loading)." + }, + "aria-expanded": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Indicates whether the element's controlled content is expanded." }, "background": { "required": false, @@ -6252,7 +6261,7 @@ }, "description": "Function to execute on press." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -6263,12 +6272,43 @@ "computed": true } }, - "accessibilityState": { + "aria-checked": { "required": false, "tsType": { - "name": "AccessibilityState" + "name": "union", + "raw": "boolean | 'mixed'", + "elements": [ + { + "name": "boolean" + }, + { + "name": "literal", + "value": "'mixed'" + } + ] + }, + "description": "Indicates whether the element is checked. Accepts `true`, `false`,\nor `'mixed'` for an indeterminate state." + }, + "aria-selected": { + "required": false, + "tsType": { + "name": "boolean" }, - "description": "Accessibility state forwarded to the underlying button." + "description": "Indicates whether the element is selected." + }, + "aria-busy": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Indicates whether the element is currently busy (e.g. loading)." + }, + "aria-expanded": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Indicates whether the element's controlled content is expanded." }, "labelMaxFontSizeMultiplier": { "required": false, @@ -6373,7 +6413,7 @@ "tsType": { "name": "signature", "type": "object", - "raw": "{\n /**\n * Icon displayed in the trigger FAB (and cross-faded to `closeIcon` when\n * the menu is open).\n */\n icon: IconSource;\n variant?: Variant;\n size?: Size;\n containerColor?: ColorValue;\n contentColor?: ColorValue;\n visible?: boolean;\n onPress?: (e: GestureResponderEvent) => void;\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Icon displayed in the trigger FAB (and cross-faded to `closeIcon` when\n * the menu is open).\n */\n icon: IconSource;\n variant?: Variant;\n size?: Size;\n containerColor?: ColorValue;\n contentColor?: ColorValue;\n visible?: boolean;\n onPress?: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label for the trigger FAB.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6441,7 +6481,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6509,7 +6549,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6549,7 +6589,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6568,7 +6608,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6608,7 +6648,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6633,7 +6673,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6673,7 +6713,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6692,7 +6732,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6732,7 +6772,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6751,7 +6791,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6791,7 +6831,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6816,7 +6856,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6856,7 +6896,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6875,7 +6915,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6915,7 +6955,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6934,7 +6974,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -6974,7 +7014,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -6993,7 +7033,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7033,7 +7073,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7058,7 +7098,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7098,7 +7138,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7117,7 +7157,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7157,7 +7197,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7176,7 +7216,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7216,7 +7256,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7235,7 +7275,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7275,7 +7315,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7294,7 +7334,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7334,7 +7374,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7359,7 +7399,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7399,7 +7439,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7418,7 +7458,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7458,7 +7498,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7477,7 +7517,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7517,7 +7557,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7536,7 +7576,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7576,7 +7616,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7595,7 +7635,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7635,7 +7675,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7654,7 +7694,7 @@ { "name": "signature", "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n accessibilityLabel?: string;\n testID?: string;\n}", + "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", "signature": { "properties": [ { @@ -7694,7 +7734,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -7835,7 +7875,7 @@ "computed": false } }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -8315,7 +8355,7 @@ }, "description": "TestID used for testing purposes" }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -9270,19 +9310,50 @@ "computed": false } }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" }, "description": "Accessibility label for the Touchable. This is read by the screen reader when the user taps the component." }, - "accessibilityState": { + "aria-checked": { "required": false, "tsType": { - "name": "AccessibilityState" + "name": "union", + "raw": "boolean | 'mixed'", + "elements": [ + { + "name": "boolean" + }, + { + "name": "literal", + "value": "'mixed'" + } + ] + }, + "description": "Indicates whether the element is checked. Accepts `true`, `false`,\nor `'mixed'` for an indeterminate state." + }, + "aria-selected": { + "required": false, + "tsType": { + "name": "boolean" }, - "description": "Accessibility state for the Touchable. This is read by the screen reader when the user taps the component." + "description": "Indicates whether the element is selected." + }, + "aria-busy": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Indicates whether the element is currently busy (e.g. loading)." + }, + "aria-expanded": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "Indicates whether the element's controlled content is expanded." } } }, @@ -10046,7 +10117,7 @@ }, "description": "Function to execute on long press." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" @@ -10640,7 +10711,7 @@ { "name": "signature", "type": "object", - "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}", + "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n 'aria-label'?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}", "signature": { "properties": [ { @@ -10665,7 +10736,7 @@ } }, { - "key": "accessibilityLabel", + "key": "aria-label", "value": { "name": "string", "required": false @@ -10758,9 +10829,9 @@ } } ], - "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}[]" + "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n 'aria-label'?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}[]" }, - "description": "Buttons to display as options in toggle button.\nButton should contain the following properties:\n- `value`: value of button (required)\n- `icon`: icon to display for the item\n- `disabled`: whether the button is disabled\n- `accessibilityLabel`: acccessibility label for the button. This is read by the screen reader when the user taps the button.\n- `checkedColor`: custom color for checked Text and Icon\n- `uncheckedColor`: custom color for unchecked Text and Icon\n- `onPress`: callback that is called when button is pressed\n- `label`: label text of the button\n- `showSelectedCheck`: show optional check icon to indicate selected state\n- `style`: pass additional styles for the button\n- `testID`: testID to be used on tests" + "description": "Buttons to display as options in toggle button.\nButton should contain the following properties:\n- `value`: value of button (required)\n- `icon`: icon to display for the item\n- `disabled`: whether the button is disabled\n- `aria-label`: accessibility label for the button. This is read by the screen reader when the user taps the button.\n- `checkedColor`: custom color for checked Text and Icon\n- `uncheckedColor`: custom color for unchecked Text and Icon\n- `onPress`: callback that is called when button is pressed\n- `label`: label text of the button\n- `showSelectedCheck`: show optional check icon to indicate selected state\n- `style`: pass additional styles for the button\n- `testID`: testID to be used on tests" }, "density": { "required": false, @@ -11275,12 +11346,12 @@ }, "description": "" }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" }, - "description": "" + "description": "Accessibility label for the switch. This is read by the screen reader when the user focuses the switch." } } }, @@ -11634,7 +11705,7 @@ }, "description": "Whether the button is disabled." }, - "accessibilityLabel": { + "aria-label": { "required": false, "tsType": { "name": "string" diff --git a/scripts/generate-component-docs.ts b/scripts/generate-component-docs.ts index 7550fd1669..a3f5a177dd 100644 --- a/scripts/generate-component-docs.ts +++ b/scripts/generate-component-docs.ts @@ -4,6 +4,7 @@ import { createRequire } from 'node:module'; import * as os from 'node:os'; import * as path from 'node:path'; import { fileURLToPath } from 'node:url'; +import * as vm from 'node:vm'; type PluginOptions = { docsRootDir: string; @@ -36,6 +37,57 @@ const isPluginOptions = (value: unknown): value is PluginOptions => { const isPluginFactory = (value: unknown): value is PluginFactory => typeof value === 'function'; +const findObjectEnd = (source: string, start: number) => { + let depth = 0; + + for (let index = start; index < source.length; index++) { + const char = source[index]; + + if (char === '{') { + depth++; + } + + if (char === '}') { + depth--; + } + + if (depth === 0) { + return index + 1; + } + } + + return -1; +}; + +const loadLegacyPluginConfig = ( + docusaurusConfigPath: string +): PluginOptions => { + const source = fs.readFileSync(docusaurusConfigPath, 'utf8'); + const pluginPathIndex = source.indexOf("'./component-docs-plugin'"); + const optionsStart = source.indexOf('{', pluginPathIndex); + const optionsEnd = findObjectEnd(source, optionsStart); + + if (pluginPathIndex === -1 || optionsStart === -1 || optionsEnd === -1) { + throw new Error( + `Unable to read component docs plugin options from ${docusaurusConfigPath}` + ); + } + + const optionsSource = source.slice(optionsStart, optionsEnd); + const pluginOptions: unknown = vm.runInNewContext(`(${optionsSource})`, { + __dirname: path.dirname(docusaurusConfigPath), + path, + }); + + if (!isPluginOptions(pluginOptions)) { + throw new Error( + `Unable to read component docs plugin options from ${docusaurusConfigPath}` + ); + } + + return pluginOptions; +}; + const loadPluginConfig = ( sourceDir: string, requireFromScript: ReturnType @@ -45,15 +97,43 @@ const loadPluginConfig = ( 'docs', 'component-docs.config.js' ); - const sharedConfig: unknown = requireFromScript(sharedConfigPath); - if (!isPluginOptions(sharedConfig)) { - throw new Error( - `Unable to read component docs config from ${sharedConfigPath}` - ); + if (fs.existsSync(sharedConfigPath)) { + const sharedConfig: unknown = requireFromScript(sharedConfigPath); + + if (!isPluginOptions(sharedConfig)) { + throw new Error( + `Unable to read component docs config from ${sharedConfigPath}` + ); + } + + return sharedConfig; } - return sharedConfig; + const docusaurusConfigPath = path.join( + sourceDir, + 'docs', + 'docusaurus.config.js' + ); + + return loadLegacyPluginConfig(docusaurusConfigPath); +}; + +const writeDocusaurusConfigStub = (sourceDir: string) => { + fs.writeFileSync( + path.join(sourceDir, 'docs', 'docusaurus.config.js'), + `module.exports = { + baseUrl: '/react-native-paper/', + customFields: { + moreExamples: {}, + knownIssues: {}, + themeColors: {}, + screenshots: {}, + extendedExamples: {}, + }, +}; +` + ); }; const normalizeDocs = (value: unknown, sourceDir: string): unknown => { @@ -162,6 +242,9 @@ const main = async () => { } const pluginOptions = loadPluginConfig(sourceDir, requireFromScript); + + writeDocusaurusConfigStub(sourceDir); + const pluginFactoryPath = path.join( sourceDir, 'docs', From 565772c36bf9c2f9c5b3cc68c65b74e0d8c1cd36 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Thu, 20 Aug 2026 13:30:33 +0200 Subject: [PATCH 53/94] chore: avoid unnecessary type assertions --- eslint.config.mjs | 8 +++++++ example/src/ExampleList.tsx | 6 ++---- example/src/Examples/AppbarExample.tsx | 1 + example/src/Examples/BannerExample.tsx | 1 + .../src/Examples/BottomNavigationExample.tsx | 1 + example/src/Examples/CardExample.tsx | 2 +- .../SegmentedButtonMultiselectRealCase.tsx | 1 + example/src/Examples/SurfaceExample.tsx | 12 ++++++----- example/src/Examples/TeamsList.tsx | 7 +++++-- example/src/RootNavigator.tsx | 12 ++++++++++- example/utils/index.ts | 6 ++++-- src/components/Appbar/Appbar.tsx | 1 + src/components/Appbar/AppbarAction.tsx | 4 ++-- src/components/Appbar/AppbarContent.tsx | 15 +++++-------- src/components/Appbar/AppbarHeader.tsx | 3 ++- src/components/Appbar/utils.ts | 9 ++++---- src/components/Banner.tsx | 4 ++-- .../BottomNavigation/BottomNavigationBar.tsx | 17 +++++++++------ src/components/BottomNavigation/utils.ts | 8 +++---- src/components/Button/Button.tsx | 14 ++++--------- src/components/Button/utils.tsx | 18 +++++++++------- src/components/Card/Card.tsx | 5 ++++- src/components/Card/CardActions.tsx | 7 +++++-- src/components/Card/CardContent.tsx | 2 +- src/components/Card/CardCover.tsx | 2 +- src/components/Card/utils.tsx | 6 +++--- src/components/Checkbox/Checkbox.tsx | 8 ++++--- src/components/Checkbox/CheckboxItem.tsx | 4 ++-- src/components/Chip/Chip.tsx | 7 ++++--- src/components/Chip/helpers.tsx | 4 ++-- src/components/Dialog/Dialog.tsx | 4 ++-- src/components/Dialog/DialogIcon.tsx | 3 +-- src/components/Dialog/DialogScrollArea.tsx | 3 +-- src/components/Dialog/DialogTitle.tsx | 4 ++-- src/components/FAB/tokens.ts | 1 + src/components/IconButton/IconButton.tsx | 1 + src/components/Menu/Menu.tsx | 4 ++-- src/components/Menu/MenuItem.tsx | 4 ++-- src/components/Portal/Portal.tsx | 4 ++-- src/components/Portal/PortalHost.tsx | 3 ++- .../RadioButton/RadioButtonAndroid.tsx | 2 +- .../RadioButton/RadioButtonGroup.tsx | 5 ++--- src/components/RadioButton/RadioButtonIOS.tsx | 2 +- .../RadioButton/RadioButtonItem.tsx | 6 +++--- src/components/Searchbar.tsx | 4 ++-- src/components/Snackbar.tsx | 15 +++++++------ src/components/Surface.tsx | 6 ++++-- src/components/Switch/Switch.tsx | 1 + .../ToggleButton/ToggleButtonGroup.tsx | 1 + src/components/Tooltip/Tooltip.tsx | 21 ++++++++++++------- src/components/TouchableRipple/Pressable.tsx | 3 ++- .../TouchableRipple/TouchableRipple.tsx | 10 +++++---- src/components/Typography/AnimatedText.tsx | 1 + src/components/Typography/Text.tsx | 19 +++++++++++------ .../__tests__/Appbar/Appbar.test.tsx | 1 + src/components/__tests__/Card/Card.test.tsx | 3 ++- src/components/__tests__/Dialog.test.tsx | 1 + src/components/__tests__/Menu.test.tsx | 4 +++- src/components/__tests__/Modal.test.tsx | 3 +++ src/components/__tests__/TextInput.test.tsx | 5 ++++- src/core/__tests__/PaperProvider.test.tsx | 8 +++---- src/theme/fonts.tsx | 11 +++++++--- src/theme/provider.tsx | 5 ++++- src/theme/schemes/DynamicTheme.android.tsx | 10 ++++----- src/theme/tokens/ref/typeface.ts | 12 ++++++++--- src/theme/tokens/sys/color.ts | 6 ++---- src/utils/isKeyboardFocusEvent.ts | 11 +++++----- src/utils/splitStyles.ts | 4 +++- src/utils/useLazyRef.tsx | 1 + 69 files changed, 245 insertions(+), 162 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index ee6bef8b48..b0415543d2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -144,6 +144,14 @@ export default defineConfig( }, }, }, + { + files: ['**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-unsafe-type-assertion': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + }, + }, { files: ['**/*.test.{js,ts,tsx}', '**/__tests__/**/*.{js,ts,tsx}'], diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index 53132e299f..8a2646d798 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -103,11 +103,9 @@ export const examples = { ...nestedExamples, }; -type MainExampleId = keyof typeof mainExamples; - -const data = (Object.keys(mainExamples) as MainExampleId[]).map((id) => ({ +const data = Object.entries(mainExamples).map(([id, example]) => ({ id, - data: mainExamples[id], + data: example, })); export default function ExampleList() { diff --git a/example/src/Examples/AppbarExample.tsx b/example/src/Examples/AppbarExample.tsx index bdb47ad641..2d4eb0bbc4 100644 --- a/example/src/Examples/AppbarExample.tsx +++ b/example/src/Examples/AppbarExample.tsx @@ -140,6 +140,7 @@ const AppbarExample = () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion setAppbarMode(value as AppbarModes) } > diff --git a/example/src/Examples/BannerExample.tsx b/example/src/Examples/BannerExample.tsx index b3a0d8a002..b40cfbdcc1 100644 --- a/example/src/Examples/BannerExample.tsx +++ b/example/src/Examples/BannerExample.tsx @@ -89,6 +89,7 @@ const styles = StyleSheet.create({ web: { grid: { // there is no 'grid' type in RN :( + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion display: 'grid' as 'none', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gridRowGap: '8px', diff --git a/example/src/Examples/BottomNavigationExample.tsx b/example/src/Examples/BottomNavigationExample.tsx index 8b2495fdbc..b66bb474d6 100644 --- a/example/src/Examples/BottomNavigationExample.tsx +++ b/example/src/Examples/BottomNavigationExample.tsx @@ -154,6 +154,7 @@ const styles = StyleSheet.create({ web: { content: { // there is no 'grid' type in RN :( + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion display: 'grid' as 'none', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gridRowGap: '8px', diff --git a/example/src/Examples/CardExample.tsx b/example/src/Examples/CardExample.tsx index ad3451e8b0..61c7ebe822 100644 --- a/example/src/Examples/CardExample.tsx +++ b/example/src/Examples/CardExample.tsx @@ -19,7 +19,7 @@ type Mode = 'elevated' | 'outlined' | 'contained'; const CardExample = () => { const { colors } = useTheme(); - const [selectedMode, setSelectedMode] = React.useState('elevated' as Mode); + const [selectedMode, setSelectedMode] = React.useState('elevated'); const [isSelected, setIsSelected] = React.useState(false); const preferences = React.useContext(PreferencesContext); diff --git a/example/src/Examples/SegmentedButtons/SegmentedButtonMultiselectRealCase.tsx b/example/src/Examples/SegmentedButtons/SegmentedButtonMultiselectRealCase.tsx index d8a237e493..28475a32da 100644 --- a/example/src/Examples/SegmentedButtons/SegmentedButtonMultiselectRealCase.tsx +++ b/example/src/Examples/SegmentedButtons/SegmentedButtonMultiselectRealCase.tsx @@ -13,6 +13,7 @@ const SegmentedButtonMultiselectRealCase = () => { const filteredData = React.useMemo( () => restaurantsData.filter((item) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion value.includes(item.price.toString() as PriceRange) ), [value] diff --git a/example/src/Examples/SurfaceExample.tsx b/example/src/Examples/SurfaceExample.tsx index 449c9e4dda..a246b3c63c 100644 --- a/example/src/Examples/SurfaceExample.tsx +++ b/example/src/Examples/SurfaceExample.tsx @@ -6,14 +6,14 @@ import type { Elevation } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; const SurfaceExample = () => { - const elevationValues = Array.from({ length: 6 }); + const elevationValues: Elevation[] = [0, 1, 2, 3, 4, 5]; - const renderSurface = (index: number, mode: 'flat' | 'elevated') => ( + const renderSurface = (index: Elevation, mode: 'flat' | 'elevated') => ( {`Elevation ${index === 1 ? '(default)' : ''} ${index}`} @@ -25,13 +25,15 @@ const SurfaceExample = () => { - {elevationValues.map((_, index) => renderSurface(index, 'elevated'))} + {elevationValues.map((elevation) => + renderSurface(elevation, 'elevated') + )} - {elevationValues.map((_, index) => renderSurface(index, 'flat'))} + {elevationValues.map((elevation) => renderSurface(elevation, 'flat'))} diff --git a/example/src/Examples/TeamsList.tsx b/example/src/Examples/TeamsList.tsx index c75d2fac37..0296760d85 100644 --- a/example/src/Examples/TeamsList.tsx +++ b/example/src/Examples/TeamsList.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { DataTable } from 'react-native-paper'; -import { teamsList } from '../../utils'; +import { teamsList, type colorThemes } from '../../utils'; import ScreenWrapper from '../ScreenWrapper'; const TeamsList = () => { @@ -24,7 +24,10 @@ const TeamsList = () => { key={item.key} onPress={() => navigation.navigate('TeamDetails', { - sourceColor: item.name.split(' ')[1].toLowerCase(), + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + sourceColor: item.name + .split(' ')[1] + .toLowerCase() as keyof typeof colorThemes, headerTitle: item.name, darkMode: item.darkMode ?? false, }) diff --git a/example/src/RootNavigator.tsx b/example/src/RootNavigator.tsx index b64fd1d802..95ea077275 100644 --- a/example/src/RootNavigator.tsx +++ b/example/src/RootNavigator.tsx @@ -9,6 +9,7 @@ import { import { Appbar } from 'react-native-paper'; import ExampleList, { examples } from './ExampleList'; +import { colorThemes } from '../utils'; const { TeamDetails, ...examplesWithoutParams } = examples; @@ -16,7 +17,9 @@ type ExampleRouteName = keyof typeof examplesWithoutParams; const fromEntries = ( entries: Array<[Key, Value]> -) => Object.fromEntries(entries) as Record; +) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + Object.fromEntries(entries) as Record; function Header({ navigation, route, options, back }: NativeStackHeaderProps) { const drawerNavigation = useNavigation('Home'); @@ -50,6 +53,7 @@ const Root = createNativeStackNavigator({ }, linking: '', }), + /* eslint-disable @typescript-eslint/no-unsafe-type-assertion */ ...fromEntries( ( Object.entries(examplesWithoutParams) as [ @@ -67,6 +71,7 @@ const Root = createNativeStackNavigator({ }), ]) ), + /* eslint-enable @typescript-eslint/no-unsafe-type-assertion */ TeamDetails: createNativeStackScreen({ screen: TeamDetails, options: { @@ -75,6 +80,11 @@ const Root = createNativeStackNavigator({ linking: { path: 'team-details/:sourceColor?/:headerTitle?/:darkMode?', parse: { + sourceColor: (value) => + value in colorThemes + ? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + (value as keyof typeof colorThemes) + : 'paper', darkMode: (value) => value === 'true', }, }, diff --git a/example/utils/index.ts b/example/utils/index.ts index 8cd541363c..00f9f22bd6 100644 --- a/example/utils/index.ts +++ b/example/utils/index.ts @@ -1229,7 +1229,7 @@ export const colorThemes = { colors: { ...DarkTheme.colors, ...darkCyanColors.colors }, }, }, -} as { [key: string]: { light: Theme; dark: Theme } }; +} satisfies Record; export const songsData = [ { @@ -1421,4 +1421,6 @@ export const restaurantsData = [ ]; export const dynamicThemeSupported = - Platform.OS === 'android' && (Platform.Version as number) >= 31; + Platform.OS === 'android' && + typeof Platform.Version === 'number' && + Platform.Version >= 31; diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index 114ca59e20..8ef9965819 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -156,6 +156,7 @@ const Appbar = ({ backgroundColor: customBackground, elevation = elevated ? 2 : 0, ...restStyle + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion } = (flattenedStyle || {}) as Exclude & { elevation?: Elevation; backgroundColor?: ColorValue; diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index 96e802a34f..400271a988 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -8,7 +8,7 @@ import type { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { Theme, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; @@ -86,7 +86,7 @@ const AppbarAction = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as Theme; + const { colors } = theme; const actionIconColor = iconColor ? iconColor diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index ba6a83465c..5270aa271d 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -10,12 +10,7 @@ import type { import { modeTextVariant } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { - $RemoveChildren, - Theme, - TypescaleKey, - ThemeProp, -} from '../../types'; +import type { $RemoveChildren, ThemeProp } from '../../types'; import Text from '../Typography/Text'; import type { TextRef } from '../Typography/Text'; @@ -104,7 +99,7 @@ const AppbarContent = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors, fonts } = theme as Theme; + const { colors, fonts } = theme; const titleTextColor = titleColor ? titleColor : colors.onSurface; @@ -115,14 +110,14 @@ const AppbarContent = ({ 'center-aligned': styles.v3DefaultContainer, }; - const variant = modeTextVariant[mode] as TypescaleKey; + const variant = modeTextVariant[mode]; const contentWrapperProps = { - pointerEvents: 'box-none' as ViewProps['pointerEvents'], + pointerEvents: 'box-none', style: [styles.container, modeContainerStyles[mode], style], testID, ...rest, - }; + } satisfies ViewProps; const content = ( <> diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index d869242046..0252cc89ba 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -104,6 +104,7 @@ const AppbarHeader = ({ backgroundColor: customBackground, zIndex = elevated ? 1 : 0, ...restStyle + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion } = (flattenedStyle || {}) as Exclude & { height?: number; elevation?: number; @@ -134,7 +135,7 @@ const AppbarHeader = ({ paddingHorizontal: Math.max(left, right), }, borderRadius, - shadow(elevation, theme.colors.shadow) as ViewStyle, + shadow(elevation, theme.colors.shadow), ]} > { - const { colors } = theme as Theme; + const { colors } = theme; if (customBackground) { return customBackground; } @@ -63,6 +63,7 @@ export const getAppbarBorders = ( const borders: Record = {}; for (const property of borderStyleProperties) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const value = style[property as keyof typeof style]; if (value) { borders[property] = value; @@ -121,7 +122,7 @@ export const renderAppbarContent = ({ mode = 'small', theme, }: RenderAppbarContentProps) => { - return React.Children.toArray(children as React.ReactNode | React.ReactNode[]) + return React.Children.toArray(children) .filter((child) => child != null && typeof child !== 'boolean') .filter((child) => // @ts-expect-error: TypeScript complains about the type of type but it doesn't matter diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index 4f28cf0390..f994396931 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -11,7 +11,7 @@ import type { IconSource } from './Icon'; import Surface from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; -import type { $Omit, $RemoveChildren, Theme, ThemeProp } from '../types'; +import type { $Omit, $RemoveChildren, ThemeProp } from '../types'; const DEFAULT_MAX_WIDTH = 960; @@ -133,7 +133,7 @@ const Banner = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as Theme; + const { colors } = theme; const { current: position } = React.useRef( new Animated.Value(visible ? 1 : 0) ); diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index f25bcf0170..245dda9707 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -15,7 +15,7 @@ import { getLabelColor, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { Theme, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import useAnimatedValue from '../../utils/useAnimatedValue'; import useAnimatedValueArray from '../../utils/useAnimatedValueArray'; import useIsKeyboardShown from '../../utils/useIsKeyboardShown'; @@ -327,7 +327,7 @@ const BottomNavigationBar = ({ theme: themeOverrides, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as Theme; + const { colors } = theme; const { bottom, left, right } = useSafeAreaInsets(); const { scale } = theme.animation; const compact = compactProp ?? false; @@ -433,8 +433,10 @@ const BottomNavigationBar = ({ const { routes } = navigationState; - const { backgroundColor: customBackground } = (StyleSheet.flatten(style) || - {}) as { + const { + backgroundColor: customBackground, + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + } = (StyleSheet.flatten(style) || {}) as { elevation?: number; backgroundColor?: ColorValue; }; @@ -576,7 +578,7 @@ const BottomNavigationBar = ({ const isLegacyOrV3Shifting = shifting && labeled; - const font = (theme as Theme).fonts.labelMedium; + const font = theme.fonts.labelMedium; return renderTouchable({ key: route.key, @@ -644,6 +646,7 @@ const BottomNavigationBar = ({ }) ) : ( ({ source={ route.unfocusedIcon !== undefined ? route.unfocusedIcon - : (route.focusedIcon as IconSource) + : // focusedIcon is required when renderIcon is omitted. + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + (route.focusedIcon as IconSource) } color={inactiveTintColor} size={24} diff --git a/src/components/BottomNavigation/utils.ts b/src/components/BottomNavigation/utils.ts index ede88105e4..e5d42940d8 100644 --- a/src/components/BottomNavigation/utils.ts +++ b/src/components/BottomNavigation/utils.ts @@ -1,6 +1,6 @@ import type { ColorValue } from 'react-native'; -import type { InternalTheme, Theme } from '../../types'; +import type { InternalTheme } from '../../types'; export const getActiveTintColor = ({ activeColor, @@ -13,7 +13,7 @@ export const getActiveTintColor = ({ return activeColor; } - return (theme as Theme).colors.onSecondaryContainer; + return theme.colors.onSecondaryContainer; }; export const getInactiveTintColor = ({ @@ -27,7 +27,7 @@ export const getInactiveTintColor = ({ return inactiveColor; } - return (theme as Theme).colors.onSurfaceVariant; + return theme.colors.onSurfaceVariant; }; export const getLabelColor = ({ @@ -41,7 +41,7 @@ export const getLabelColor = ({ focused: boolean; theme: InternalTheme; }) => { - const { colors } = theme as Theme; + const { colors } = theme; if (hasColor) { return tintColor; } diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 53d61a2d6f..bfc7782d66 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -13,7 +13,7 @@ import type { import { getButtonColors, getButtonTouchableRippleStyle } from './utils'; import type { ButtonMode } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $Omit, Theme, ThemeProp } from '../../types'; +import type { $Omit, ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import { splitStyles } from '../../utils/splitStyles'; import ActivityIndicator from '../ActivityIndicator'; @@ -253,6 +253,7 @@ const Button = ({ } }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; const [, borderRadiusStyles] = splitStyles( flattenedStyles, @@ -293,7 +294,7 @@ const Button = ({ const { color: customLabelColor, fontSize: customLabelSize } = StyleSheet.flatten(labelStyle) || {}; - const font = (theme as Theme).fonts.labelLarge; + const font = theme.fonts.labelLarge; const textStyle = { color: textColor, @@ -320,14 +321,7 @@ const Button = ({ {...rest} ref={ref} testID={`${testID}-container`} - style={ - [ - styles.button, - compact && styles.compact, - buttonStyle, - style, - ] as Animated.WithAnimatedValue> - } + style={[styles.button, compact && styles.compact, buttonStyle, style]} elevation={elevation} container > diff --git a/src/components/Button/utils.tsx b/src/components/Button/utils.tsx index a2ff870c46..058342b142 100644 --- a/src/components/Button/utils.tsx +++ b/src/components/Button/utils.tsx @@ -2,7 +2,7 @@ import type { ColorValue, ViewStyle } from 'react-native'; import { black, white } from '../../theme/colors'; import { tokens } from '../../theme/tokens'; -import type { InternalTheme, Theme } from '../../types'; +import type { InternalTheme } from '../../types'; import { splitStyles } from '../../utils/splitStyles'; const stateOpacity = tokens.md.sys.state.opacity; @@ -46,7 +46,7 @@ const getButtonBackgroundColor = ({ }: BaseProps & { customButtonColor?: ColorValue; }) => { - const { colors } = theme as Theme; + const { colors } = theme; if (customButtonColor && !disabled) { return customButtonColor; } @@ -85,7 +85,7 @@ const getButtonTextColor = ({ backgroundColor: ColorValue; dark?: boolean; }) => { - const { colors } = theme as Theme; + const { colors } = theme; if (customTextColor && !disabled) { return customTextColor; } @@ -217,14 +217,16 @@ export const getButtonTouchableRippleStyle = ( (style) => style.startsWith('border') && style.endsWith('Radius') ); - ( - Object.keys(borderRadiusStyles) as Array - ).forEach((key) => { - const value = style[key as keyof ViewStyleBorderRadiusStyles]; + const borderRadiusKeys = + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + Object.keys(borderRadiusStyles) as Array; + + borderRadiusKeys.forEach((key) => { + const value = style[key]; if (typeof value === 'number') { // Only subtract borderWidth if value is greater than 0 const radius = value > 0 ? value - borderWidth : 0; - touchableRippleStyle[key as keyof ViewStyleBorderRadiusStyles] = radius; + touchableRippleStyle[key] = radius; } }); return touchableRippleStyle; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index e1cc58c52a..f0fdc89407 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -188,7 +188,9 @@ const Card = ({ const total = React.Children.count(children); const siblings = React.Children.map(children, (child) => React.isValidElement(child) && child.type - ? (child.type as any).displayName + ? typeof child.type !== 'string' && 'displayName' in child.type + ? child.type.displayName + : null : null ); @@ -197,6 +199,7 @@ const Card = ({ mode: cardMode, }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; const { borderColor = themedBorderColor } = flattenedStyles; diff --git a/src/components/Card/CardActions.tsx b/src/components/Card/CardActions.tsx index 689a97d1c1..529db00f52 100644 --- a/src/components/Card/CardActions.tsx +++ b/src/components/Card/CardActions.tsx @@ -38,8 +38,11 @@ export type Props = ViewProps & { const CardActions = ({ theme, style, children, ...rest }: Props) => { useInternalTheme(theme); - const justifyContent = 'flex-end' as ViewStyle['justifyContent']; - const containerStyle = [styles.container, { justifyContent }, style]; + const containerStyle = [ + styles.container, + { justifyContent: 'flex-end' } satisfies ViewStyle, + style, + ]; return ( diff --git a/src/components/Card/CardContent.tsx b/src/components/Card/CardContent.tsx index af69fd7459..f53ff034f5 100644 --- a/src/components/Card/CardContent.tsx +++ b/src/components/Card/CardContent.tsx @@ -18,7 +18,7 @@ export type Props = ViewProps & { /** * @internal */ - siblings?: Array; + siblings?: Array; style?: StyleProp; }; diff --git a/src/components/Card/CardCover.tsx b/src/components/Card/CardCover.tsx index e68bfb4adb..dc348dbdd8 100644 --- a/src/components/Card/CardCover.tsx +++ b/src/components/Card/CardCover.tsx @@ -51,7 +51,7 @@ const CardCover = ({ }: Props) => { const theme = useInternalTheme(themeOverrides); - const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; + const flattenedStyles = StyleSheet.flatten(style) || {}; const [, borderRadiusStyles] = splitStyles( flattenedStyles, (style) => style.startsWith('border') && style.endsWith('Radius') diff --git a/src/components/Card/utils.tsx b/src/components/Card/utils.tsx index 785a23c820..644c8f4c27 100644 --- a/src/components/Card/utils.tsx +++ b/src/components/Card/utils.tsx @@ -1,6 +1,6 @@ import type { StyleProp, ViewStyle } from 'react-native'; -import type { InternalTheme, Theme } from '../../types'; +import type { InternalTheme } from '../../types'; type CardMode = 'elevated' | 'outlined' | 'contained'; @@ -39,7 +39,7 @@ export const getCardCoverStyle = ({ }; const getBorderColor = ({ theme }: { theme: InternalTheme }) => { - return (theme as Theme).colors.outline; + return theme.colors.outline; }; const getBackgroundColor = ({ @@ -49,7 +49,7 @@ const getBackgroundColor = ({ theme: InternalTheme; isMode: (mode: CardMode) => boolean; }) => { - const { colors } = theme as Theme; + const { colors } = theme; if (isMode('contained')) { return colors.surfaceVariant; } diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index f93b48becc..f2527383d7 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -239,6 +239,9 @@ const Checkbox = ({ setFocused(false); }, []); + const checked: boolean | 'mixed' = + status === 'indeterminate' ? 'mixed' : status === 'checked'; + // When `accessible={false}` is passed (typically by `CheckboxItem`, which // owns the a11y tree for the wrapped row), suppress our own a11y role // and state so the same logical control doesn't expose two `checked` @@ -249,9 +252,7 @@ const Checkbox = ({ : { role: 'checkbox' as const, 'aria-disabled': !!disabled, - 'aria-checked': (status === 'indeterminate' - ? 'mixed' - : status === 'checked') as boolean | 'mixed', + 'aria-checked': checked, 'aria-live': 'polite' as const, }; @@ -369,6 +370,7 @@ const Checkmark = ({ }; // Web-only style; not in StyleSheet because `outline` is outside ViewStyle. +// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const webNoOutline = { outline: 'none' } as unknown as ViewStyle; const styles = StyleSheet.create({ diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index e8bd23a59b..fae1a44c85 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -147,10 +147,10 @@ const CheckboxItem = ({ const textAlign = isLeading ? 'right' : 'left'; - const computedStyle = { + const computedStyle: TextStyle = { ...getStateLayer(theme, 'onSurface', disabled ? 'disabled' : 'enabled'), textAlign, - } as TextStyle; + }; return ( theme as Theme; +const md3 = (theme: InternalTheme) => theme; const stateOpacity = tokens.md.sys.state.opacity; diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index e79804d324..a7b082fe89 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -10,7 +10,7 @@ import DialogIcon from './DialogIcon'; import DialogScrollArea from './DialogScrollArea'; import DialogTitle from './DialogTitle'; import { useInternalTheme } from '../../core/theming'; -import type { Theme, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import Modal from '../Modal'; import type { DialogChildProps } from './utils'; @@ -100,7 +100,7 @@ const Dialog = ({ }: Props) => { const { right, left } = useSafeAreaInsets(); const theme = useInternalTheme(themeOverrides); - const borderRadius = (theme as Theme).shapes.corner.extraLarge; + const borderRadius = theme.shapes.corner.extraLarge; const backgroundColor = theme.colors.surfaceContainerHigh; diff --git a/src/components/Dialog/DialogIcon.tsx b/src/components/Dialog/DialogIcon.tsx index 199becd06c..3ced9e12b0 100644 --- a/src/components/Dialog/DialogIcon.tsx +++ b/src/components/Dialog/DialogIcon.tsx @@ -3,7 +3,6 @@ import type { ColorValue } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import type { Theme } from '../../types'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; @@ -70,7 +69,7 @@ const DialogIcon = ({ theme: themeOverrides, }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors } = theme as Theme; + const { colors } = theme; //@ts-ignore const iconColor = color || colors.secondary; diff --git a/src/components/Dialog/DialogScrollArea.tsx b/src/components/Dialog/DialogScrollArea.tsx index b76d4cf031..a46824be45 100644 --- a/src/components/Dialog/DialogScrollArea.tsx +++ b/src/components/Dialog/DialogScrollArea.tsx @@ -4,7 +4,6 @@ import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -import type { Theme } from '../../types'; export type Props = ViewProps & { /** @@ -51,7 +50,7 @@ export type Props = ViewProps & { */ const DialogScrollArea = (props: Props) => { const theme = useInternalTheme(props.theme); - const { colors } = theme as Theme; + const { colors } = theme; const borderStyles = { borderColor: colors.surfaceVariant, borderTopWidth: 1, diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index bdf5021379..ae553daee2 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import type { StyleProp, TextStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { Theme, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import Text from '../Typography/Text'; export type Props = React.ComponentPropsWithRef & { @@ -53,7 +53,7 @@ const DialogTitle = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const { colors, fonts } = theme as Theme; + const { colors, fonts } = theme; const headerTextStyle = { color: colors.onSurface, diff --git a/src/components/FAB/tokens.ts b/src/components/FAB/tokens.ts index 73ef1e9ff8..0fb79d1d9c 100644 --- a/src/components/FAB/tokens.ts +++ b/src/components/FAB/tokens.ts @@ -125,4 +125,5 @@ export const FOCUS_RING_THICKNESS = focusIndicator.thickness; export const FOCUS_RING_OUTER_OFFSET = focusIndicator.outerOffset; export const FOCUS_RING_INSET = FOCUS_RING_OUTER_OFFSET + FOCUS_RING_THICKNESS; +// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion export const webNoOutline = { outline: 'none' } as unknown as ViewStyle; diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 52a64e98d0..270c9289ac 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -150,6 +150,7 @@ const IconButton = ({ const { borderWidth = mode === 'outlined' && !selected ? 1 : 0, borderRadius = buttonSize / 2, + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion } = (StyleSheet.flatten(style) || {}) as ViewStyle; const borderStyles = { diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index ca6e849e3d..ec4c256dad 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -25,7 +25,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import MenuItem from './MenuItem'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; -import type { Elevation, Theme, ThemeProp } from '../../types'; +import type { Elevation, ThemeProp } from '../../types'; import { addEventListener } from '../../utils/addEventListener'; import { BackHandler } from '../../utils/BackHandler/BackHandler'; import Portal from '../Portal/Portal'; @@ -194,7 +194,7 @@ const Menu = ({ }: Props) => { const theme = useInternalTheme(themeOverrides); const { direction } = useLocale(); - const { colors: md3Colors } = theme as Theme; + const { colors: md3Colors } = theme; const insets = useSafeAreaInsets(); const [rendered, setRendered] = React.useState(visible); const [left, setLeft] = React.useState(0); diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 945c12794b..149a93a752 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -15,7 +15,7 @@ import { MIN_WIDTH, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { Theme, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; @@ -177,7 +177,7 @@ const MenuItem = ({ const titleTextStyle = { color: titleColor, - ...(theme as Theme).fonts.bodyLarge, + ...theme.fonts.bodyLarge, }; return ( diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index b3bc9bffb3..bfbe53c837 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import PortalConsumer from './PortalConsumer'; import PortalHost, { PortalContext } from './PortalHost'; -import type { PortalMethods } from './PortalHost'; import { LocaleContext, LocaleProvider } from '../../core/locale'; import { Consumer as SettingsConsumer, @@ -56,8 +55,9 @@ class Portal extends React.Component { {(settings) => ( {(manager) => ( - + + {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} {children} diff --git a/src/components/Portal/PortalHost.tsx b/src/components/Portal/PortalHost.tsx index ec6c98f116..d8193007b8 100644 --- a/src/components/Portal/PortalHost.tsx +++ b/src/components/Portal/PortalHost.tsx @@ -18,6 +18,7 @@ export type PortalMethods = { unmount: (key: number) => void; }; +// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion export const PortalContext = React.createContext(null as any); /** @@ -95,7 +96,7 @@ export default class PortalHost extends React.Component { if (index > -1) { this.queue[index] = op; } else { - this.queue.push(op as Operation); + this.queue.push(op); } } }; diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index 5fcaf9f6e5..86740cafa8 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -102,7 +102,7 @@ const RadioButtonAndroid = ({ return ( - {(context?: RadioButtonContextType) => { + {(context: RadioButtonContextType | null) => { const checked = isChecked({ contextValue: context?.value, diff --git a/src/components/RadioButton/RadioButtonGroup.tsx b/src/components/RadioButton/RadioButtonGroup.tsx index 8522bf3fc4..ef76cd30aa 100644 --- a/src/components/RadioButton/RadioButtonGroup.tsx +++ b/src/components/RadioButton/RadioButtonGroup.tsx @@ -21,9 +21,8 @@ export type RadioButtonContextType = { onValueChange: (item: string) => void; }; -export const RadioButtonContext = React.createContext( - null as any -); +export const RadioButtonContext = + React.createContext(null); /** * Radio button group allows to control a group of radio buttons. diff --git a/src/components/RadioButton/RadioButtonIOS.tsx b/src/components/RadioButton/RadioButtonIOS.tsx index 015356eea3..131e5ce7c7 100644 --- a/src/components/RadioButton/RadioButtonIOS.tsx +++ b/src/components/RadioButton/RadioButtonIOS.tsx @@ -61,7 +61,7 @@ const RadioButtonIOS = ({ return ( - {(context?: RadioButtonContextType) => { + {(context: RadioButtonContextType | null) => { const checked = isChecked({ contextValue: context?.value, diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index e37ed435e2..849c9b637d 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -179,14 +179,14 @@ const RadioButtonItem = ({ const textAlign = isLeading ? 'right' : 'left'; - const computedStyle = { + const computedStyle: TextStyle = { ...getStateLayer(theme, 'onSurface', disabled ? 'disabled' : 'enabled'), textAlign, - } as TextStyle; + }; return ( - {(context?: RadioButtonContextType) => { + {(context: RadioButtonContextType | null) => { const checked = isChecked({ contextValue: context?.value, diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 3cc733e126..b0819b5710 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -18,7 +18,7 @@ import Surface from './Surface'; import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import { cornerNone } from '../theme/tokens/sys/shape'; -import type { Theme, ThemeProp } from '../types'; +import type { ThemeProp } from '../types'; interface Style { marginRight: number; @@ -189,7 +189,7 @@ const Searchbar = ({ }: Props) => { const theme = useInternalTheme(themeOverrides); const { direction } = useLocale(); - const { colors, fonts } = theme as Theme; + const { colors, fonts } = theme; const root = React.useRef(null); React.useImperativeHandle(ref, () => ({ diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index 04502eb7de..05e2b3d6c1 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -13,7 +13,7 @@ import Surface from './Surface'; import Text from './Typography/Text'; import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; -import type { $Omit, $RemoveChildren, Theme, ThemeProp } from '../types'; +import type { $Omit, $RemoveChildren, ThemeProp } from '../types'; export type Props = $Omit, 'mode'> & { /** @@ -158,7 +158,9 @@ const Snackbar = ({ const { current: opacity } = React.useRef( new Animated.Value(0.0) ); - const hideTimeout = React.useRef(undefined); + const hideTimeout = React.useRef | undefined>( + undefined + ); const [hidden, setHidden] = React.useState(!visible); @@ -179,10 +181,7 @@ const Snackbar = ({ duration === Number.NEGATIVE_INFINITY; if (!isInfinity) { - hideTimeout.current = setTimeout( - onDismiss, - duration - ) as unknown as NodeJS.Timeout; + hideTimeout.current = setTimeout(onDismiss, duration); } } }); @@ -230,7 +229,7 @@ const Snackbar = ({ } }, [visible, handleOnVisible, handleOnHidden]); - const { colors } = theme as Theme; + const { colors } = theme; if (hidden) { return null; @@ -290,7 +289,7 @@ const Snackbar = ({ styles.container, { backgroundColor, - borderRadius: (theme as Theme).shapes.corner.extraSmall, + borderRadius: theme.shapes.corner.extraSmall, opacity: opacity, transform: [ { diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index 5966469df9..b0b0f0c4aa 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -15,7 +15,7 @@ import { shadow, shadowLayers, } from '../theme/tokens/sys/elevation'; -import type { Elevation, Theme, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../types'; import { isAnimatedValue } from '../utils/animations'; import { splitStyles } from '../utils/splitStyles'; @@ -137,6 +137,7 @@ const SurfaceIOS = ({ ...props }: SurfaceIOSProps) => { const [outerLayerViewStyles, innerLayerViewStyles] = React.useMemo(() => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; const [filteredStyles, outerLayerStyles, borderRadiusStyles] = splitStyles( @@ -237,7 +238,7 @@ const Surface = ({ }: Props) => { const theme = useInternalTheme(overridenTheme); - const { colors } = theme as Theme; + const { colors } = theme; const backgroundColor = (() => { if (isAnimatedValue(elevation)) { @@ -287,6 +288,7 @@ const Surface = ({ return androidElevationLevels[elevation]; }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const { margin, padding, transform, borderRadius } = (StyleSheet.flatten( style ) || {}) as ViewStyle; diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index 6f12dafdd8..b0ca4b4bd7 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -539,6 +539,7 @@ const styles = StyleSheet.create({ }); // Web-only style; not in StyleSheet because `outline` is outside ViewStyle. +// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const webNoOutline = { outline: 'none' } as unknown as ViewStyle; export default Switch; diff --git a/src/components/ToggleButton/ToggleButtonGroup.tsx b/src/components/ToggleButton/ToggleButtonGroup.tsx index baaf1ee1a9..f3e52356d8 100644 --- a/src/components/ToggleButton/ToggleButtonGroup.tsx +++ b/src/components/ToggleButton/ToggleButtonGroup.tsx @@ -22,6 +22,7 @@ type ToggleButtonContextType = { export const ToggleButtonGroupContext = //@ts-expect-error: TS can't ensure the type from Group to children + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion React.createContext(null as any); /** diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 8d237d6991..a3bb3519c7 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -81,8 +81,8 @@ const Tooltip = ({ tooltip: {}, measured: false, }); - const showTooltipTimer = React.useRef([]); - const hideTooltipTimer = React.useRef([]); + const showTooltipTimer = React.useRef[]>([]); + const hideTooltipTimer = React.useRef[]>([]); const childrenWrapperRef = React.useRef(null); const touched = React.useRef(false); @@ -121,10 +121,10 @@ const Tooltip = ({ } if (isWeb) { - let id = setTimeout(() => { + const id = setTimeout(() => { touched.current = true; setVisible(true); - }, enterTouchDelay) as unknown as NodeJS.Timeout; + }, enterTouchDelay); showTooltipTimer.current.push(id); } else { touched.current = true; @@ -139,10 +139,10 @@ const Tooltip = ({ showTooltipTimer.current = []; } - let id = setTimeout(() => { + const id = setTimeout(() => { setVisible(false); setMeasurement({ children: {}, tooltip: {}, measured: false }); - }, leaveTouchDelay) as unknown as NodeJS.Timeout; + }, leaveTouchDelay); hideTooltipTimer.current.push(id); }, [leaveTouchDelay]); @@ -151,7 +151,9 @@ const Tooltip = ({ return null; } if (!isValidChild) return null; - const props = children.props as TooltipChildProps; + const props = + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + children.props as TooltipChildProps; if (props.disabled) return null; return props.onPress?.(); }, [children.props, isValidChild]); @@ -159,6 +161,7 @@ const Tooltip = ({ const handleHoverIn = React.useCallback(() => { handleTouchStart(); if (isValidChild) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion (children.props as TooltipChildProps).onHoverIn?.(); } }, [children.props, handleTouchStart, isValidChild]); @@ -166,6 +169,7 @@ const Tooltip = ({ const handleHoverOut = React.useCallback(() => { handleTouchEnd(); if (isValidChild) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion (children.props as TooltipChildProps).onHoverOut?.(); } }, [children.props, handleTouchEnd, isValidChild]); @@ -205,7 +209,9 @@ const Tooltip = ({ { backgroundColor: theme.colors.onSurface, ...getTooltipPosition( + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion measurement as Measurement, + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion children as React.ReactElement ), borderRadius: theme.shapes.corner.extraSmall, @@ -257,6 +263,7 @@ const styles = StyleSheet.create({ hidden: { opacity: 0, }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion pressContainer: { ...(Platform.OS === 'web' && { cursor: 'default' }), } as ViewStyle, diff --git a/src/components/TouchableRipple/Pressable.tsx b/src/components/TouchableRipple/Pressable.tsx index 446cb234a7..ebeb45cf9c 100644 --- a/src/components/TouchableRipple/Pressable.tsx +++ b/src/components/TouchableRipple/Pressable.tsx @@ -36,6 +36,7 @@ export type PressableProps = Omit< | undefined; }; +// @ts-expect-error React Native Web adds focused and hovered callback state fields that React Native omits. export const Pressable: React.ForwardRefExoticComponent< PressableProps & React.RefAttributes -> = PressableNative as any; +> = PressableNative; diff --git a/src/components/TouchableRipple/TouchableRipple.tsx b/src/components/TouchableRipple/TouchableRipple.tsx index 93c07111ef..d913b1ee3c 100644 --- a/src/components/TouchableRipple/TouchableRipple.tsx +++ b/src/components/TouchableRipple/TouchableRipple.tsx @@ -232,14 +232,16 @@ const TouchableRipple = ({ onPressOut?.(e); if (rippleEffectEnabled) { - const containers = e.currentTarget.querySelectorAll( - '[data-paper-ripple]' - ) as HTMLElement[]; + const containers: NodeListOf = + e.currentTarget.querySelectorAll('[data-paper-ripple]'); requestAnimationFrame(() => { requestAnimationFrame(() => { containers.forEach((container) => { - const ripple = container.firstChild as HTMLSpanElement; + const ripple = container.firstElementChild; + if (!(ripple instanceof HTMLSpanElement)) { + return; + } Object.assign(ripple.style, { transitionDuration: '250ms', diff --git a/src/components/Typography/AnimatedText.tsx b/src/components/Typography/AnimatedText.tsx index ea47c43b58..497566b9f1 100644 --- a/src/components/Typography/AnimatedText.tsx +++ b/src/components/Typography/AnimatedText.tsx @@ -99,6 +99,7 @@ const styles = StyleSheet.create({ }); export const customAnimatedText = () => + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion AnimatedText as (props: Props) => ReactNode; export default AnimatedText; diff --git a/src/components/Typography/Text.tsx b/src/components/Typography/Text.tsx index df957411f9..38a14fb423 100644 --- a/src/components/Typography/Text.tsx +++ b/src/components/Typography/Text.tsx @@ -97,12 +97,13 @@ const Text = ({ let textStyle = [font, style]; if ( - React.isValidElement(rest.children) && + React.isValidElement<{ + variant?: string; + style?: StyleProp; + }>(rest.children) && (rest.children.type === Component || rest.children.type === AnimatedText) ) { - const { props } = rest.children as { - props: { variant?: string; style?: StyleProp }; - }; + const { props } = rest.children; // Context: Some components have the built-in `Text` component with a predefined variant, // that also accepts `children` as a `React.Node`. This can result in a situation, @@ -115,7 +116,11 @@ const Text = ({ // Solution: To address the following scenario, the code below overrides the `variant` // specified in a parent in favor of children's variant: if (props.variant) { - font = theme.fonts[props.variant as VariantProp]; + font = + theme.fonts[ + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + props.variant as VariantProp + ]; textStyle = [style, font]; } @@ -176,6 +181,8 @@ type TextComponent = (props: Props) => ReactNode; const Component = Text as TextComponent; -export const customText = () => Component as unknown as TextComponent; +export const customText = () => + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + Component as unknown as TextComponent; export default Component; diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 8738f71e37..27cd573f9d 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -18,6 +18,7 @@ import Menu from '../../Menu/Menu'; import Searchbar from '../../Searchbar'; import Text from '../../Typography/Text'; +// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const renderAppbarContent = utilRenderAppbarContent as ( props: Parameters[0] ) => { props: any }[]; diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index 61659c8a24..d9e91d9d73 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -198,7 +198,8 @@ describe('getCardColors - border color', () => { expect( getCardColors({ theme: getTheme(), - mode: undefined as any, + // @ts-expect-error: Verify the runtime fallback when mode is omitted. + mode: undefined, }) ).toMatchObject({ borderColor: getTheme().colors.outline }); }); diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 59785c52cf..32477e5fe1 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -17,6 +17,7 @@ interface BackHandlerStatic extends RNBackHandlerStatic { mockPressBack(): void; } +// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const BackHandler = RNBackHandler as BackHandlerStatic; describe('Dialog', () => { diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 128fb87cdb..bd14689f70 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -75,7 +75,9 @@ it('renders menu with content styles', async () => { expect(tree).toMatchSnapshot(); }); -([0, 1, 2, 3, 4, 5] as Elevation[]).forEach((elevation) => +const elevations: Elevation[] = [0, 1, 2, 3, 4, 5]; + +elevations.forEach((elevation) => it(`renders menu with background color based on elevation value = ${elevation}`, async () => { const theme = getTheme(); diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 2009f3d444..ff4cf2fd41 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -19,6 +19,7 @@ interface BackHandlerStatic extends RNBackHandlerStatic { mockPressBack(): void; } +// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const BackHandler = RNBackHandler as BackHandlerStatic; describe('Modal', () => { @@ -31,9 +32,11 @@ describe('Modal', () => { afterAll(() => { jest.useRealTimers(); + /* eslint-disable @typescript-eslint/no-unsafe-type-assertion */ ( window.requestAnimationFrame as unknown as { mockRestore(): void } ).mockRestore(); + /* eslint-enable @typescript-eslint/no-unsafe-type-assertion */ }); describe('by default', () => { diff --git a/src/components/__tests__/TextInput.test.tsx b/src/components/__tests__/TextInput.test.tsx index 14b1c72bdb..b87e482c7f 100644 --- a/src/components/__tests__/TextInput.test.tsx +++ b/src/components/__tests__/TextInput.test.tsx @@ -382,7 +382,10 @@ it('renders the input via render with merged props', async () => { expect(screen.getByTestId('custom-input')).toBeOnTheScreen(); expect(renderInput).toHaveBeenCalled(); - const merged = renderInput.mock.calls[0]?.[0] as TextInputRenderProps; + const merged = renderInput.mock.calls[0]?.[0]; + if (!merged) { + throw new Error('Expected the custom input renderer to be called.'); + } expect(merged['aria-label']).toBe('Pin'); expect(merged.value).toBe('12'); }); diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 1c5d821ebe..28ac30e327 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -117,8 +117,8 @@ const createProvider = (theme?: ThemeProp) => { ); }; -const ExtendedLightTheme = { ...LightTheme } as ThemeProp; -const ExtendedDarkTheme = { ...DarkTheme } as ThemeProp; +const ExtendedLightTheme: ThemeProp = { ...LightTheme }; +const ExtendedDarkTheme: ThemeProp = { ...DarkTheme }; const defaultPlatform = Platform.OS; @@ -316,13 +316,13 @@ describe('PaperProvider', () => { it('uses provided custom theme', async () => { mockAppearance(); - const customTheme = { + const customTheme: ThemeProp = { ...ExtendedLightTheme, colors: { ...ExtendedLightTheme.colors, primary: 'tomato', }, - } as ThemeProp; + }; await render(createProvider(customTheme)); // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(screen.getByTestId('provider-child-view').props.theme).toStrictEqual( diff --git a/src/theme/fonts.tsx b/src/theme/fonts.tsx index 847f62637c..2fe7b55626 100644 --- a/src/theme/fonts.tsx +++ b/src/theme/fonts.tsx @@ -13,11 +13,12 @@ function configureFontsConfig( return typescale; } - const isFlatConfig = Object.keys(config).every( - (key) => typeof config[key as keyof typeof config] !== 'object' + const isFlatConfig = Object.values(config).every( + (value) => typeof value !== 'object' ); if (isFlatConfig) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return Object.fromEntries( Object.entries(typescale).map(([variantName, variantProperties]) => [ variantName, @@ -26,12 +27,16 @@ function configureFontsConfig( ) as Typescale; } + const typescaleByVariant: Partial< + Record + > = typescale; + return Object.assign( {}, typescale, ...Object.entries(config).map(([variantName, variantProperties]) => ({ [variantName]: { - ...typescale[variantName as TypescaleKey], + ...typescaleByVariant[variantName], ...variantProperties, }, })) diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index b4101eaa0f..546a9dcf88 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -29,6 +29,7 @@ export const isPlatformColorSentinel = (v: unknown): boolean => typeof v === 'object' && ('resource_paths' in v || 'semantic' in v || 'dynamic' in v); +/* eslint-disable @typescript-eslint/no-unsafe-type-assertion */ export const safeMerge = (base: T, overrides: unknown): T => { if ( !base || @@ -44,7 +45,7 @@ export const safeMerge = (base: T, overrides: unknown): T => { return (overrides ?? base) as T; } const out: Record = { ...(base as Record) }; - for (const key of Object.keys(overrides as Record)) { + for (const key of Object.keys(overrides)) { out[key] = safeMerge( (base as Record)[key], (overrides as Record)[key] @@ -52,6 +53,7 @@ export const safeMerge = (base: T, overrides: unknown): T => { } return out as T; }; +/* eslint-enable @typescript-eslint/no-unsafe-type-assertion */ /** Memoize `themeOverrides` at the call site; inline object literals defeat the memo. */ export const useInternalTheme = ( @@ -74,6 +76,7 @@ export const defaultThemes = { }; export const getTheme = ( + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion isDark: Scheme = false as Scheme ): (typeof defaultThemes)[Scheme extends true ? 'dark' : 'light'] => { const scheme = isDark ? 'dark' : 'light'; diff --git a/src/theme/schemes/DynamicTheme.android.tsx b/src/theme/schemes/DynamicTheme.android.tsx index 07cc9f93d2..0fb9c29b0e 100644 --- a/src/theme/schemes/DynamicTheme.android.tsx +++ b/src/theme/schemes/DynamicTheme.android.tsx @@ -5,7 +5,7 @@ import { LightTheme } from './LightTheme'; import { Palette } from '../tokens'; import type { Theme, ThemeColors } from '../types'; -const apiLevel = Platform.Version as number; +const apiLevel = Platform.OS === 'android' ? Platform.Version : null; // eslint-disable-next-line @react-native/platform-colors const ac = (name: string) => PlatformColor(`@android:color/${name}`); @@ -21,9 +21,9 @@ const ac = (name: string) => PlatformColor(`@android:color/${name}`); * @see https://github.com/material-components/material-components-android/blob/master/docs/theming/Color.md */ const pick = (api34: string, api31: string | null, ref: string): ColorValue => - apiLevel >= 34 + apiLevel != null && apiLevel >= 34 ? ac(api34) - : apiLevel >= 31 && api31 !== null + : apiLevel != null && apiLevel >= 31 && api31 !== null ? ac(api31) : ref; @@ -468,10 +468,10 @@ function buildDynamicColors(scheme: 'light' | 'dark'): Partial { role, pick(api34, api31, ref), ]) - ) as Partial; + ); } -export const isDynamicColorSupported = apiLevel >= 31; +export const isDynamicColorSupported = apiLevel != null && apiLevel >= 31; const lightDynamicColors = isDynamicColorSupported ? buildDynamicColors('light') diff --git a/src/theme/tokens/ref/typeface.ts b/src/theme/tokens/ref/typeface.ts index e4b92318ec..fdf6993ae8 100644 --- a/src/theme/tokens/ref/typeface.ts +++ b/src/theme/tokens/ref/typeface.ts @@ -9,14 +9,20 @@ export const typeface = { ios: 'System', default: 'sans-serif', }), - weightRegular: '400' as Font['fontWeight'], + weightRegular: '400', plainMedium: Platform.select({ web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', ios: 'System', default: 'sans-serif-medium', }), - weightMedium: '500' as Font['fontWeight'], + weightMedium: '500', - weightBold: '700' as Font['fontWeight'], + weightBold: '700', +} satisfies { + brandRegular?: string; + weightRegular: Font['fontWeight']; + plainMedium?: string; + weightMedium: Font['fontWeight']; + weightBold: Font['fontWeight']; }; diff --git a/src/theme/tokens/sys/color.ts b/src/theme/tokens/sys/color.ts index efdb08cbba..a6c22ca0fb 100644 --- a/src/theme/tokens/sys/color.ts +++ b/src/theme/tokens/sys/color.ts @@ -154,11 +154,9 @@ export function buildScheme( const tones = roleToTone[opts.mode][contrast]; const elevTones = elevationToTone[opts.mode][contrast]; + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const mapped = Object.fromEntries( - (Object.keys(tones) as Array).map((role) => [ - role, - palette[tones[role]], - ]) + Object.entries(tones).map(([role, tone]) => [role, palette[tone]]) ) as MappedRoles; return { diff --git a/src/utils/isKeyboardFocusEvent.ts b/src/utils/isKeyboardFocusEvent.ts index 630bd36182..d7f05b731d 100644 --- a/src/utils/isKeyboardFocusEvent.ts +++ b/src/utils/isKeyboardFocusEvent.ts @@ -10,11 +10,12 @@ export const isKeyboardFocusEvent = ( ): boolean => { if (Platform.OS !== 'web') return true; try { - const target = e.currentTarget as unknown as { - matches?: (selector: string) => boolean; - }; - if (typeof target.matches === 'function') { - return target.matches(':focus-visible'); + const target: unknown = e.currentTarget; + if (typeof target === 'object' && target !== null && 'matches' in target) { + const { matches } = target; + if (typeof matches === 'function') { + return matches.call(target, ':focus-visible'); + } } } catch { // older browsers throw on unknown selectors diff --git a/src/utils/splitStyles.ts b/src/utils/splitStyles.ts index d6b490e45a..910c62eb45 100644 --- a/src/utils/splitStyles.ts +++ b/src/utils/splitStyles.ts @@ -29,12 +29,13 @@ export function splitStyles( // `Object.entries` will be used to iterate over the styles and `Object.fromEntries` will be called before returning // Entries which match the given filters will be temporarily stored in `newStyles` - const newStyles = filters.map(() => [] as Entry[]); + const newStyles: Entry[][] = filters.map(() => []); // Entries which match no filter const rest: Entry[] = []; // Iterate every style property + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion outer: for (const item of Object.entries(styles) as Entry[]) { // Check each filter for (let i = 0; i < filters.length; i++) { @@ -53,6 +54,7 @@ export function splitStyles( newStyles.unshift(rest); // Convert arrays of entries into objects + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return newStyles.map((styles) => Object.fromEntries(styles)) as unknown as [ ViewStyle, ...MappedTuple, diff --git a/src/utils/useLazyRef.tsx b/src/utils/useLazyRef.tsx index 37c11472d2..5ef6aac27d 100644 --- a/src/utils/useLazyRef.tsx +++ b/src/utils/useLazyRef.tsx @@ -7,5 +7,6 @@ export default function useLazyRef(callback: () => T) { lazyRef.current = callback(); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return lazyRef as React.MutableRefObject; } From ea80c26d9f631cef9e5b42378cf5840686582adc Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 21 Aug 2026 12:11:13 +0200 Subject: [PATCH 54/94] chore: fix failing docs build --- docs/src/data/componentDocs6x.json | 13 +++++++++++-- package.json | 3 +++ yarn.lock | 8 ++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/src/data/componentDocs6x.json b/docs/src/data/componentDocs6x.json index 56b356912b..300c315b99 100644 --- a/docs/src/data/componentDocs6x.json +++ b/docs/src/data/componentDocs6x.json @@ -3637,10 +3637,19 @@ "name": "Array", "elements": [ { - "name": "string" + "name": "union", + "raw": "string | null", + "elements": [ + { + "name": "string" + }, + { + "name": "null" + } + ] } ], - "raw": "Array" + "raw": "Array" }, "description": "@internal" }, diff --git a/package.json b/package.json index 3a0296b5f5..25ca91fb73 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,9 @@ "react-native-safe-area-context": "*", "react-native-worklets": ">=0.8.1" }, + "resolutions": { + "ast-types": "0.16.1" + }, "jest": { "preset": "@react-native/jest-preset", "setupFiles": [ diff --git a/yarn.lock b/yarn.lock index 313bbc5130..100597d848 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6500,12 +6500,12 @@ __metadata: languageName: node linkType: hard -"ast-types@npm:0.14.2, ast-types@npm:^0.14.2": - version: 0.14.2 - resolution: "ast-types@npm:0.14.2" +"ast-types@npm:0.16.1": + version: 0.16.1 + resolution: "ast-types@npm:0.16.1" dependencies: tslib: "npm:^2.0.1" - checksum: 10c0/5d66d89b6c07fe092087454b6042dbaf81f2882b176db93861e2b986aafe0bce49e1f1ff59aac775d451c1426ad1e967d250e9e3548f5166ea8a3475e66c169d + checksum: 10c0/abcc49e42eb921a7ebc013d5bec1154651fb6dbc3f497541d488859e681256901b2990b954d530ba0da4d0851271d484f7057d5eff5e07cb73e8b10909f711bf languageName: node linkType: hard From df3cdfd7a1feb48260b946d6c0fd63cced020ebc Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 21 Aug 2026 15:38:59 +0200 Subject: [PATCH 55/94] fix: make elevation shadows on iOS & Web match android more closely (#5062) ### Motivation On iOS and Web, we manually calculate elevation shadows. They are originally from material-web: https://github.com/callstack/react-native-paper/pull/3089 But looking at them side-by-side on all platforms, they look nothing like Android. This reworks the shadows so they more closely match Android's elevation. Note that it's an approximation. On Android, the elevation is dynamic and changes based on where the view is. Views toward the bottom have a larger shadow than views toward the top. But we can't achieve it with shadow styles directly, so we need to approximate it. Also changed the example, as the huge rectangles fit fewer ones on screen and made it harder to see the shadows. ### Known issues - Animated elevation doesn't work on Web. But it's a pre-existing bug. It should be fixed once we move to Reanimated. ### Test plan **Before** elevation-before **After** elevation-after --- example/src/Examples/SurfaceExample.tsx | 92 +++++++++--- src/components/FAB/useVisibility.ts | 7 +- src/components/__tests__/Surface.test.tsx | 135 +++++++++++++----- .../__snapshots__/Banner.test.tsx.snap | 72 +++++----- .../__tests__/__snapshots__/FAB.test.tsx.snap | 76 +++++----- .../__snapshots__/FABExtended.test.tsx.snap | 34 ++--- .../__snapshots__/FABMenu.test.tsx.snap | 40 +++--- .../__snapshots__/Menu.test.tsx.snap | 24 ++-- .../__snapshots__/Snackbar.test.tsx.snap | 48 +++---- src/theme/tokens/sys/elevation.ts | 125 ++++++++++++---- 10 files changed, 416 insertions(+), 237 deletions(-) diff --git a/example/src/Examples/SurfaceExample.tsx b/example/src/Examples/SurfaceExample.tsx index a246b3c63c..fe420830ce 100644 --- a/example/src/Examples/SurfaceExample.tsx +++ b/example/src/Examples/SurfaceExample.tsx @@ -1,30 +1,67 @@ -import { ScrollView, StyleSheet, View } from 'react-native'; +import * as React from 'react'; +import { Animated, ScrollView, StyleSheet, View } from 'react-native'; -import { Surface, Text, Palette, List } from 'react-native-paper'; +import { Surface, Text, Palette, List, IconButton } from 'react-native-paper'; import type { Elevation } from 'react-native-paper'; import ScreenWrapper from '../ScreenWrapper'; +const elevationLevels: Elevation[] = [0, 1, 2, 3, 4, 5]; + +const AnimatedSurface = () => { + const [index, setIndex] = React.useState(3); + + const level = elevationLevels[index]; + const elevation = React.useRef(new Animated.Value(level)).current; + + React.useEffect(() => { + Animated.timing(elevation, { + toValue: level, + duration: 250, + useNativeDriver: false, + }).start(); + }, [elevation, level]); + + return ( + + + {`Elevation ${level}`} + + + setIndex(index - 1)} + /> + setIndex(index + 1)} + /> + + + ); +}; + const SurfaceExample = () => { const elevationValues: Elevation[] = [0, 1, 2, 3, 4, 5]; const renderSurface = (index: Elevation, mode: 'flat' | 'elevated') => ( - - - {`Elevation ${index === 1 ? '(default)' : ''} ${index}`} - + + {`Elevation ${index}`} ); return ( - + {elevationValues.map((elevation) => renderSurface(elevation, 'elevated') )} @@ -32,11 +69,19 @@ const SurfaceExample = () => { - + {elevationValues.map((elevation) => renderSurface(elevation, 'flat'))} + + + + @@ -68,21 +113,22 @@ const styles = StyleSheet.create({ padding: 24, alignItems: 'center', }, + scroll: { + gap: 24, + paddingHorizontal: 16, + paddingVertical: 24, + }, surface: { - margin: 24, - height: 80, - width: 80, + height: 120, + width: 120, + borderRadius: 8, alignItems: 'center', justifyContent: 'center', }, - v3Surface: { - borderRadius: 16, - height: 200, - width: 200, - alignItems: 'center', - justifyContent: 'center', + actions: { + flexDirection: 'row', + gap: 16, }, - horizontalSurfacesContainer: { flexDirection: 'row', justifyContent: 'space-between', diff --git a/src/components/FAB/useVisibility.ts b/src/components/FAB/useVisibility.ts index 12914e7f64..f6c4b2e645 100644 --- a/src/components/FAB/useVisibility.ts +++ b/src/components/FAB/useVisibility.ts @@ -11,8 +11,6 @@ import { import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { - IOS_SHADOW_RADIUS_FACTOR, - SHADOW_OPACITY, androidElevationLevels, shadow, shadowLayers, @@ -79,6 +77,7 @@ export function useVisibility({ const restingElevationDp = androidElevationLevels[elevation]; const shadowOffsetHeight = shadowLayers[0].height[elevation]; const shadowRadius = shadowLayers[0].shadowRadius[elevation]; + const shadowOpacity = elevation ? shadowLayers[0].shadowOpacity : 0; const shadowColor = theme.colors.shadow; const webShadow = @@ -93,9 +92,9 @@ export function useVisibility({ } return { shadowColor, - shadowOpacity: alpha.value * (elevation ? SHADOW_OPACITY : 0), + shadowOpacity: alpha.value * shadowOpacity, shadowOffset: { width: 0, height: shadowOffsetHeight }, - shadowRadius: shadowRadius * IOS_SHADOW_RADIUS_FACTOR, + shadowRadius, }; }); diff --git a/src/components/__tests__/Surface.test.tsx b/src/components/__tests__/Surface.test.tsx index 1a9ebc8a45..d0a0437f65 100644 --- a/src/components/__tests__/Surface.test.tsx +++ b/src/components/__tests__/Surface.test.tsx @@ -2,7 +2,14 @@ import type { ViewStyle } from 'react-native'; import { StyleSheet } from 'react-native'; import { Platform } from 'react-native'; -import { describe, expect, it } from '@jest/globals'; +import { + afterEach, + beforeEach, + describe, + expect, + it, + jest, +} from '@jest/globals'; import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; @@ -13,20 +20,31 @@ type StyleCase = { value: ViewStyle[keyof ViewStyle]; }; +const SPOT_SHADOW_OPACITY = 0.19; +const AMBIENT_SHADOW_OPACITY = 0.039; + +afterEach(() => { + jest.restoreAllMocks(); +}); + describe('Surface', () => { it('should properly render passed props', async () => { - const testID = 'surface-container'; await render( - + {null} ); // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId(testID).props.pointerEvents).toBe('box-none'); + expect(screen.getByTestId('surface-container').props.pointerEvents).toBe( + 'box-none' + ); }); describe('on iOS', () => { - Platform.OS = 'ios'; + beforeEach(() => { + jest.replaceProperty(Platform, 'OS', 'ios'); + }); + const styles = StyleSheet.create({ absoluteStyles: { bottom: 10, @@ -59,23 +77,34 @@ describe('Surface', () => { ); - expect(screen.getByTestId('surface-test')).not.toHaveStyle({ - shadowColor: '#000', - shadowOpacity: 0.3, - shadowOffset: { width: 0, height: 4 }, - shadowRadius: 4, - }); + // @ts-expect-error expect(screen.getByTestId('surface-test-outer-layer')).not.toHaveStyle({ - shadowColor: '#000', - shadowOpacity: 0.15, - shadowOffset: { width: 0, height: 8 }, - shadowRadius: 12, + shadowOpacity: expect.any(Number), + }); + // @ts-expect-error + expect(screen.getByTestId('surface-test')).not.toHaveStyle({ + shadowOpacity: expect.any(Number), }); expect(screen.getByTestId('surface-test')).toHaveStyle({ backgroundColor: getTheme().colors.surfaceContainerHighest, }); }); + it('should render a spot shadow over an ambient shadow, if mode is elevated', async () => { + await render( + + {null} + + ); + + expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle({ + shadowOpacity: SPOT_SHADOW_OPACITY, + }); + expect(screen.getByTestId('surface-test')).toHaveStyle({ + shadowOpacity: AMBIENT_SHADOW_OPACITY, + }); + }); + it.each([ { property: 'opacity', value: 0.7 }, { property: 'transform', value: [{ scale: 1.02 }] }, @@ -170,43 +199,37 @@ describe('Surface', () => { describe('outer layer', () => { it('should not render rest style', async () => { - const testID = 'surface-test'; - await render( - + {null} ); - expect(screen.getByTestId(`${testID}-outer-layer`)).not.toHaveStyle( + expect(screen.getByTestId('surface-test-outer-layer')).not.toHaveStyle( styles.restStyle ); }); it('should render absolute position properties on outer layer', async () => { - const testID = 'surface-test'; - await render( - + {null} ); - expect(screen.getByTestId(`${testID}-outer-layer`)).toHaveStyle( + expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( styles.absoluteStyles ); }); it('should render absolute position properties on the outer layer', async () => { - const testID = 'surface-test'; - await render( - + {null} ); - expect(screen.getByTestId(`${testID}-outer-layer`)).toHaveStyle( + expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( styles.absoluteStyles ); }); @@ -214,15 +237,13 @@ describe('Surface', () => { describe('inner layer', () => { it('should render inner layer styles on the inner layer', async () => { - const testID = 'surface-test'; - await render( - + {null} ); - expect(screen.getByTestId(testID)).toHaveStyle( + expect(screen.getByTestId('surface-test')).toHaveStyle( styles.innerLayerViewStyle ); }); @@ -246,39 +267,75 @@ describe('Surface', () => { describe('children wrapper', () => { it('should render rest styles', async () => { - const testID = 'surface-test'; const combinedStyles = [styles.innerLayerViewStyle, styles.restStyle]; await render( - + {null} ); - expect(screen.getByTestId(testID)).toHaveStyle(combinedStyles); + expect(screen.getByTestId('surface-test')).toHaveStyle(combinedStyles); }); }); }); describe('on Android', () => { + beforeEach(() => { + jest.replaceProperty(Platform, 'OS', 'android'); + }); + it('should render Surface with appropriate bg color but without shadow, if mode is set to "flat"', async () => { - Platform.OS = 'android'; - const testID = 'surface-container'; await render( {null} ); - expect(screen.getByTestId(testID)).not.toHaveStyle({ elevation: 5 }); - expect(screen.getByTestId(testID)).toHaveStyle({ + // @ts-expect-error + expect(screen.getByTestId('surface-container')).not.toHaveStyle({ + elevation: expect.any(Number), + }); + expect(screen.getByTestId('surface-container')).toHaveStyle({ backgroundColor: getTheme().colors.surfaceContainerHighest, }); }); + + it('should render the dp value for the elevation level, if mode is elevated', async () => { + await render( + + {null} + + ); + + expect(screen.getByTestId('surface-container')).toHaveStyle({ + elevation: 12, + }); + }); + }); + + describe('on Web', () => { + beforeEach(() => { + jest.replaceProperty(Platform, 'OS', 'web'); + }); + + it('should render both shadows in one box shadow, if mode is elevated', async () => { + await render( + + {null} + + ); + + expect(screen.getByTestId('surface-container')).toHaveStyle({ + boxShadow: + `0px 6.75px 19.22px rgba(0, 0, 0, ${SPOT_SHADOW_OPACITY}), ` + + `0px 0px 6px rgba(0, 0, 0, ${AMBIENT_SHADOW_OPACITY})`, + }); + }); }); }); diff --git a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap index a6fb8c8dee..4e75db8cc7 100644 --- a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap @@ -9,11 +9,11 @@ exports[`render visible banner, with custom theme 1`] = ` "opacity": 1, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0.55, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 3, + "shadowOpacity": 0.19, + "shadowRadius": 0.79, } } testID="surface-outer-layer" @@ -26,11 +26,11 @@ exports[`render visible banner, with custom theme 1`] = ` "flex": undefined, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.25, } } testID="surface" @@ -285,11 +285,11 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` "opacity": 0, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0.55, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 3, + "shadowOpacity": 0.19, + "shadowRadius": 0.79, } } testID="surface-outer-layer" @@ -302,11 +302,11 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` "flex": undefined, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.25, } } testID="surface" @@ -420,11 +420,11 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "opacity": 1, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0.55, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 3, + "shadowOpacity": 0.19, + "shadowRadius": 0.79, } } testID="surface-outer-layer" @@ -437,11 +437,11 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` "flex": undefined, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.25, } } testID="surface" @@ -718,11 +718,11 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "opacity": 1, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0.55, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 3, + "shadowOpacity": 0.19, + "shadowRadius": 0.79, } } testID="surface-outer-layer" @@ -735,11 +735,11 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` "flex": undefined, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.25, } } testID="surface" @@ -1146,11 +1146,11 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` "opacity": 1, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0.55, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 3, + "shadowOpacity": 0.19, + "shadowRadius": 0.79, } } testID="surface-outer-layer" @@ -1163,11 +1163,11 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` "flex": undefined, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.25, } } testID="surface" @@ -1291,11 +1291,11 @@ exports[`renders visible banner, without action buttons and without image 1`] = "opacity": 1, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0.55, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 3, + "shadowOpacity": 0.19, + "shadowRadius": 0.79, } } testID="surface-outer-layer" @@ -1308,11 +1308,11 @@ exports[`renders visible banner, without action buttons and without image 1`] = "flex": undefined, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.25, } } testID="surface" diff --git a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap index 348a6bba64..cec33bb556 100644 --- a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap @@ -23,11 +23,11 @@ exports[`renders FAB large size 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -195,11 +195,11 @@ exports[`renders FAB medium size 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -367,11 +367,11 @@ exports[`renders FAB transitioning to not visible 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, "shadowOpacity": 0, - "shadowRadius": 4, + "shadowRadius": 4.75, }, { "pointerEvents": "none", @@ -539,11 +539,11 @@ exports[`renders FAB transitioning to visible 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -711,11 +711,11 @@ exports[`renders FAB with aria-label 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -884,11 +884,11 @@ exports[`renders FAB with containerColor and contentColor overrides 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -1056,11 +1056,11 @@ exports[`renders FAB with containerColor override 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -1228,11 +1228,11 @@ exports[`renders FAB with default props 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -1400,11 +1400,11 @@ exports[`renders FAB with primary variant 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -1572,11 +1572,11 @@ exports[`renders FAB with secondary variant 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -1744,11 +1744,11 @@ exports[`renders FAB with tertiary variant 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -1916,11 +1916,11 @@ exports[`renders FAB with tonalSecondary variant 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -2088,11 +2088,11 @@ exports[`renders FAB with tonalTertiary variant 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", diff --git a/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap b/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap index 2ceb981d60..d159f82912 100644 --- a/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FABExtended.test.tsx.snap @@ -24,11 +24,11 @@ exports[`renders extended FAB collapsed 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -263,11 +263,11 @@ exports[`renders extended FAB expanded 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -502,11 +502,11 @@ exports[`renders extended FAB large size 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -741,11 +741,11 @@ exports[`renders extended FAB medium size 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -980,11 +980,11 @@ exports[`renders extended FAB not visible 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, "shadowOpacity": 0, - "shadowRadius": 4, + "shadowRadius": 4.75, }, { "pointerEvents": "none", @@ -1219,11 +1219,11 @@ exports[`renders extended FAB transitioning to collapsed 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", diff --git a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap index 726f4ef1b2..d48d6e3666 100644 --- a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap @@ -424,11 +424,11 @@ exports[`renders FAB.Menu closed 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -1109,11 +1109,11 @@ exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, "shadowOpacity": 0, - "shadowRadius": 4, + "shadowRadius": 4.75, }, { "pointerEvents": "none", @@ -1794,11 +1794,11 @@ exports[`renders FAB.Menu open 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -3139,11 +3139,11 @@ exports[`renders FAB.Menu with 6 items 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -3825,11 +3825,11 @@ exports[`renders FAB.Menu with center alignment 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -4568,11 +4568,11 @@ exports[`renders FAB.Menu with items having icons 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", @@ -5253,11 +5253,11 @@ exports[`renders FAB.Menu with start alignment 1`] = ` { "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 4, + "height": 3.33, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 4, + "shadowOpacity": 0.19, + "shadowRadius": 4.75, }, { "pointerEvents": "auto", diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index 11d5aea821..3e027106ae 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -269,11 +269,11 @@ exports[`renders menu with content styles 1`] = ` "opacity": 0, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 2, + "height": 1.66, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 6, + "shadowOpacity": 0.19, + "shadowRadius": 2.36, "transform": [ { "scaleX": 0, @@ -299,11 +299,11 @@ exports[`renders menu with content styles 1`] = ` "paddingVertical": 8, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.75, } } testID="menu-surface" @@ -998,11 +998,11 @@ exports[`renders visible menu 1`] = ` "opacity": 0, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 2, + "height": 1.66, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 6, + "shadowOpacity": 0.19, + "shadowRadius": 2.36, "transform": [ { "scaleX": 0, @@ -1026,11 +1026,11 @@ exports[`renders visible menu 1`] = ` "paddingVertical": 8, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.75, } } testID="menu-surface" diff --git a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap index 4857580e83..76d4857c89 100644 --- a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap @@ -30,11 +30,11 @@ exports[`renders snackbar with Text as a child 1`] = ` "opacity": 0, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 2, + "height": 1.66, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 6, + "shadowOpacity": 0.19, + "shadowRadius": 2.36, "transform": [ { "scale": 0.9, @@ -58,11 +58,11 @@ exports[`renders snackbar with Text as a child 1`] = ` "minHeight": 48, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.75, } } testID="surface" @@ -118,11 +118,11 @@ exports[`renders snackbar with View & Text as a child 1`] = ` "opacity": 0, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 2, + "height": 1.66, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 6, + "shadowOpacity": 0.19, + "shadowRadius": 2.36, "transform": [ { "scale": 0.9, @@ -146,11 +146,11 @@ exports[`renders snackbar with View & Text as a child 1`] = ` "minHeight": 48, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.75, } } testID="surface" @@ -232,11 +232,11 @@ exports[`renders snackbar with action button 1`] = ` "opacity": 0, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 2, + "height": 1.66, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 6, + "shadowOpacity": 0.19, + "shadowRadius": 2.36, "transform": [ { "scale": 0.9, @@ -260,11 +260,11 @@ exports[`renders snackbar with action button 1`] = ` "minHeight": 48, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.75, } } testID="surface" @@ -503,11 +503,11 @@ exports[`renders snackbar with content 1`] = ` "opacity": 0, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 2, + "height": 1.66, "width": 0, }, - "shadowOpacity": 0.15, - "shadowRadius": 6, + "shadowOpacity": 0.19, + "shadowRadius": 2.36, "transform": [ { "scale": 0.9, @@ -531,11 +531,11 @@ exports[`renders snackbar with content 1`] = ` "minHeight": 48, "shadowColor": "rgba(0, 0, 0, 1)", "shadowOffset": { - "height": 1, + "height": 0, "width": 0, }, - "shadowOpacity": 0.3, - "shadowRadius": 2, + "shadowOpacity": 0.039, + "shadowRadius": 0.75, } } testID="surface" diff --git a/src/theme/tokens/sys/elevation.ts b/src/theme/tokens/sys/elevation.ts index de797183d4..18e92dcb63 100644 --- a/src/theme/tokens/sys/elevation.ts +++ b/src/theme/tokens/sys/elevation.ts @@ -43,21 +43,90 @@ export const elevationInputRange: Elevation[] = Object.values(defaultElevation); export const androidElevationLevels = [0, 1, 3, 6, 8, 12]; -export const SHADOW_OPACITY = 0.3; -// iOS shadowRadius is a Gaussian sigma; CSS blur-radius = 2*sigma. -// Dividing by this factor normalizes the spread to match Web. -export const IOS_SHADOW_RADIUS_FACTOR = 0.5; +/** + * Android draws two shadows for an elevated view: + * - Spot shadow cast by a light source above the top edge of the window + * - Ambient shadow centered on the view + * + * The shadow is dynamic based on how far the view is from the light source. + * But we can't express this dynamic value in a static shadow. + * + * So we use an approximation assuming: + * - Light is at the platform's default height and radius + * - Screen is the size of a phone, 400dp wide and 800dp tall + * - View is halfway down the window, and centered horizontally + */ + +// https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/res/res/values/dimens.xml#752 +const LIGHT_Z = 500; +const LIGHT_RADIUS = 800; +const SPOT_SHADOW_ALPHA = 0.19; +const AMBIENT_SHADOW_ALPHA = 0.039; + +// https://skia.googlesource.com/skia/+/refs/heads/main/src/core/SkDrawShadowInfo.h#51 +const AMBIENT_HEIGHT_FACTOR = 1 / 128; +const AMBIENT_GEOM_FACTOR = 64; + +// Approximate blur based on how Skia draws the shadow +// https://skia.googlesource.com/skia/+/refs/heads/main/src/opts/SkRasterPipeline_opts.h#5326 +const SOFT_EDGE_BLUR_FACTOR = 0.94; +const SOFT_EDGE_OFFSET_FACTOR = 0.17; + +// Since we can't express the dynamic shadow statically +// We use these device dimensions to calculate as an approximation +const WINDOW_WIDTH = 400; +const WINDOW_HEIGHT = 800; + +// https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/java/android/view/ThreadedRenderer.java#602 +const LIGHT_HEIGHT = LIGHT_Z * ((WINDOW_WIDTH / 450 + 2) / 3); + +// Sub-pixel precision is enough for a shadow, and keeps the values readable. +const round = (value: number) => Math.round(value * 100) / 100; + +// How far the view has moved towards the light +// This controls the height and softness of the shadow +const getLightRatio = (dp: number) => dp / (LIGHT_HEIGHT - dp); + +// How wide the shadow fades out over +// A larger light casts a softer shadow +const getSoftEdge = (dp: number) => getLightRatio(dp) * LIGHT_RADIUS; + +const spotBlurRadius = androidElevationLevels.map((dp) => + round(getSoftEdge(dp) * SOFT_EDGE_BLUR_FACTOR) +); + +// Android's ambient shadow starts dark at the view’s edge and fades outward. +// A regular blur is already half faded at that edge because half of it is hidden behind the view. +// Using the same fade distance gives both shadows a similar size, but our shadow starts lighter. +// https://skia.googlesource.com/skia/+/refs/heads/main/src/utils/SkShadowTessellator.cpp#923 +const ambientBlurRadius = androidElevationLevels.map((dp) => + round(dp * AMBIENT_HEIGHT_FACTOR * AMBIENT_GEOM_FACTOR) +); + +// On iOS, we need to use half the blur radius to draw the approximately same blur in `shadowRadius` +// https://github.com/react/react-native/blob/main/packages/react-native/React/Fabric/Utils/RCTBoxShadow.mm#L64-L67 +const IOS_SHADOW_RADIUS_FACTOR = 0.5; + +const getShadowRadius = (blurRadius: number[]) => + blurRadius.map((radius) => round(radius * IOS_SHADOW_RADIUS_FACTOR)); export const shadowLayers = [ { - shadowOpacity: 0.15, - height: [0, 1, 2, 4, 6, 8], - shadowRadius: [0, 3, 6, 8, 10, 12], + height: androidElevationLevels.map((dp) => + round( + getLightRatio(dp) * (WINDOW_HEIGHT / 2) - + getSoftEdge(dp) * SOFT_EDGE_OFFSET_FACTOR + ) + ), + blurRadius: spotBlurRadius, + shadowOpacity: SPOT_SHADOW_ALPHA, + shadowRadius: getShadowRadius(spotBlurRadius), }, { - shadowOpacity: SHADOW_OPACITY, - height: [0, 1, 1, 1, 2, 4], - shadowRadius: [0, 2, 2, 3, 3, 4], + height: androidElevationLevels.map(() => 0), + blurRadius: ambientBlurRadius, + shadowOpacity: AMBIENT_SHADOW_ALPHA, + shadowRadius: getShadowRadius(ambientBlurRadius), }, ]; @@ -71,8 +140,13 @@ const getShadowColor = (shadowColor: ColorValue, shadowOpacity: number) => { return color(shadowColor).alpha(shadowOpacity).rgb().string(); }; -const getBoxShadowValue = (elevation: number, shadowColor: string) => - `0px ${shadowLayers[0].height[elevation]}px ${shadowLayers[0].shadowRadius[elevation]}px ${shadowColor}`; +const getBoxShadowValue = (elevation: number, layerColors: readonly string[]) => + shadowLayers + .map( + (layer, index) => + `0px ${layer.height[elevation]}px ${layer.blurRadius[elevation]}px ${layerColors[index]}` + ) + .join(', '); export function shadow(elevation: number, shadowColor: ColorValue): ViewStyle; // eslint-disable-next-line no-redeclare @@ -91,24 +165,30 @@ export function shadow( shadowColor: ColorValue ): ViewStyle | AnimatedShadowStyle { if (Platform.OS === 'web') { - const webShadowColor = getShadowColor(shadowColor, SHADOW_OPACITY); + const layerColors = shadowLayers.map((layer) => + getShadowColor(shadowColor, layer.shadowOpacity) + ); if (isAnimatedValue(elevation)) { return { boxShadow: elevation.interpolate({ inputRange: elevationInputRange, outputRange: elevationInputRange.map((value) => - getBoxShadowValue(value, webShadowColor) + getBoxShadowValue(value, layerColors) ), }), }; } return { - boxShadow: getBoxShadowValue(elevation, webShadowColor), + boxShadow: getBoxShadowValue(elevation, layerColors), }; } + // For a single view, we can only draw one shadow + // So we pick the spot shadow, as it shows the depth + const [spotShadow] = shadowLayers; + if (isAnimatedValue(elevation)) { return { shadowColor, @@ -116,31 +196,28 @@ export function shadow( width: new Animated.Value(0), height: elevation.interpolate({ inputRange: elevationInputRange, - outputRange: shadowLayers[0].height, + outputRange: spotShadow.height, }), }, shadowOpacity: elevation.interpolate({ inputRange: [0, 1], - outputRange: [0, SHADOW_OPACITY], + outputRange: [0, spotShadow.shadowOpacity], extrapolate: 'clamp', }), shadowRadius: elevation.interpolate({ inputRange: elevationInputRange, - outputRange: shadowLayers[0].shadowRadius.map( - (r) => r * IOS_SHADOW_RADIUS_FACTOR - ), + outputRange: spotShadow.shadowRadius, }), }; } return { shadowColor, - shadowOpacity: elevation ? SHADOW_OPACITY : 0, + shadowOpacity: elevation ? spotShadow.shadowOpacity : 0, shadowOffset: { width: 0, - height: shadowLayers[0].height[elevation], + height: spotShadow.height[elevation], }, - shadowRadius: - shadowLayers[0].shadowRadius[elevation] * IOS_SHADOW_RADIUS_FACTOR, + shadowRadius: spotShadow.shadowRadius[elevation], }; } From dfaecf05bf5aa23d06502048b7bfb3b2150c11bc Mon Sep 17 00:00:00 2001 From: lukemorawski <134388952+lukemorawski@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:54:23 +0200 Subject: [PATCH 56/94] chore: render banner below the app bar on native in example (#5061) --- example/src/Examples/BannerExample.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/example/src/Examples/BannerExample.tsx b/example/src/Examples/BannerExample.tsx index b40cfbdcc1..5679a92518 100644 --- a/example/src/Examples/BannerExample.tsx +++ b/example/src/Examples/BannerExample.tsx @@ -36,7 +36,7 @@ const BannerExample = () => { }; return ( - <> + {PHOTOS.map((uri) => ( @@ -78,13 +78,16 @@ const BannerExample = () => { Two line text string with two actions. One to two lines is preferable on mobile. - + ); }; BannerExample.title = 'Banner'; const styles = StyleSheet.create({ + container: { + flex: 1, + }, ...Platform.select({ web: { grid: { From c48175396a016177dec9e1eda32b37431b675292 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 2 Sep 2026 08:58:54 +0200 Subject: [PATCH 57/94] refactor: rework Surface and elevation shadows (#5078) --- docs/6.x/docs/components/Card/Card.mdx | 8 + docs/6.x/docs/components/Chip/Chip.mdx | 8 + docs/6.x/docs/components/Modal.mdx | 34 +- docs/6.x/docs/components/Surface.mdx | 142 +- docs/6.x/docs/guides/migration.md | 154 + docs/src/data/componentDocs6x.json | 646 +- example/src/Examples/BannerExample.tsx | 46 +- example/src/Examples/ButtonExample.tsx | 24 +- example/src/Examples/CardExample.tsx | 20 +- example/src/Examples/ChipExample.tsx | 32 +- .../Dialogs/DialogWithCustomColors.tsx | 6 +- example/src/Examples/SurfaceExample.tsx | 32 +- jest/reanimatedSnapshotSerializer.js | 27 + jest/testSetup.js | 11 +- package.json | 3 + src/components/Appbar/Appbar.tsx | 43 +- src/components/Appbar/AppbarAction.tsx | 12 +- src/components/Appbar/AppbarBackAction.tsx | 5 +- src/components/Appbar/AppbarHeader.tsx | 69 +- src/components/Appbar/utils.ts | 33 +- src/components/Badge.tsx | 4 +- src/components/Banner.tsx | 100 +- .../BottomNavigation/BottomNavigationBar.tsx | 24 +- src/components/Button/Button.tsx | 90 +- src/components/Card/Card.tsx | 83 +- src/components/Chip/Chip.tsx | 70 +- src/components/Chip/helpers.tsx | 42 +- src/components/Dialog/Dialog.tsx | 18 +- src/components/FAB/Extended.tsx | 38 +- src/components/FAB/FAB.tsx | 4 +- src/components/FAB/Shell.tsx | 61 +- src/components/FAB/useVisibility.ts | 102 - src/components/IconButton/IconButton.tsx | 28 +- src/components/Menu/Menu.tsx | 258 +- src/components/Modal.tsx | 149 +- src/components/Searchbar.tsx | 25 +- src/components/Snackbar.tsx | 178 +- src/components/Surface.tsx | 518 +- src/components/ToggleButton/ToggleButton.tsx | 6 +- .../__tests__/Appbar/Appbar.test.tsx | 190 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 1424 +- src/components/__tests__/Banner.test.tsx | 34 +- .../__tests__/BottomNavigation.test.tsx | 81 +- src/components/__tests__/Button.test.tsx | 91 +- src/components/__tests__/Card/Card.test.tsx | 82 +- .../Card/__snapshots__/Card.test.tsx.snap | 159 +- .../__snapshots__/Checkbox.test.tsx.snap | 21 + .../__snapshots__/CheckboxItem.test.tsx.snap | 6 + src/components/__tests__/Chip.test.tsx | 95 +- src/components/__tests__/FABExtended.test.tsx | 50 +- src/components/__tests__/IconButton.test.tsx | 32 +- src/components/__tests__/Menu.test.tsx | 87 +- src/components/__tests__/Modal.test.tsx | 84 +- src/components/__tests__/Searchbar.test.tsx | 31 +- src/components/__tests__/Snackbar.test.tsx | 35 +- src/components/__tests__/Surface.test.tsx | 331 +- .../__tests__/ToggleButton.test.tsx | 40 +- .../__snapshots__/Badge.test.tsx.snap | 90 +- .../__snapshots__/Banner.test.tsx.snap | 2531 ++- .../BottomNavigation.test.tsx.snap | 16522 ++++++++-------- .../__snapshots__/Button.test.tsx.snap | 3753 ++-- .../__snapshots__/Chip.test.tsx.snap | 2234 ++- .../__snapshots__/DataTable.test.tsx.snap | 2894 ++- .../__tests__/__snapshots__/FAB.test.tsx.snap | 1037 +- .../__snapshots__/FABExtended.test.tsx.snap | 580 +- .../__snapshots__/FABMenu.test.tsx.snap | 749 +- .../__snapshots__/IconButton.test.tsx.snap | 1084 +- .../__snapshots__/ListItem.test.tsx.snap | 345 +- .../__snapshots__/Menu.test.tsx.snap | 1135 +- .../__snapshots__/Searchbar.test.tsx.snap | 1732 +- .../__snapshots__/Snackbar.test.tsx.snap | 999 +- .../__snapshots__/Switch.test.tsx.snap | 26 + .../__snapshots__/TextInput.test.tsx.snap | 1947 +- .../__snapshots__/ToggleButton.test.tsx.snap | 642 +- src/theme/tokens/sys/elevation.ts | 145 +- src/utils/animations.ts | 5 - 76 files changed, 23616 insertions(+), 20860 deletions(-) create mode 100644 jest/reanimatedSnapshotSerializer.js delete mode 100644 src/components/FAB/useVisibility.ts delete mode 100644 src/utils/animations.ts diff --git a/docs/6.x/docs/components/Card/Card.mdx b/docs/6.x/docs/components/Card/Card.mdx index 0248a53318..de2e1b19cb 100644 --- a/docs/6.x/docs/components/Card/Card.mdx +++ b/docs/6.x/docs/components/Card/Card.mdx @@ -159,6 +159,14 @@ export default MyComponent; +
+ +### ref + +
+ + + diff --git a/docs/6.x/docs/components/Chip/Chip.mdx b/docs/6.x/docs/components/Chip/Chip.mdx index f712768036..f1a72dc03d 100644 --- a/docs/6.x/docs/components/Chip/Chip.mdx +++ b/docs/6.x/docs/components/Chip/Chip.mdx @@ -268,6 +268,14 @@ export default MyComponent;
+### ref + +
+ + + +
+ ### role
diff --git a/docs/6.x/docs/components/Modal.mdx b/docs/6.x/docs/components/Modal.mdx index 72691042b1..f94e758cad 100644 --- a/docs/6.x/docs/components/Modal.mdx +++ b/docs/6.x/docs/components/Modal.mdx @@ -28,12 +28,18 @@ const MyComponent = () => { const showModal = () => setVisible(true); const hideModal = () => setVisible(false); - const containerStyle = { backgroundColor: 'white', padding: 20 }; + + const containerStyle = { padding: 20 }; return ( - + Example Modal. Click outside this area to dismiss. @@ -111,6 +117,30 @@ export default MyComponent;
+### contentBackgroundColor + +
+ + + +
+ +### contentBorderRadius + +
+ + + +
+ +### contentElevation + +
+ + + +
+ ### style
diff --git a/docs/6.x/docs/components/Surface.mdx b/docs/6.x/docs/components/Surface.mdx index 841a74e3e4..4add0da5c0 100644 --- a/docs/6.x/docs/components/Surface.mdx +++ b/docs/6.x/docs/components/Surface.mdx @@ -9,9 +9,9 @@ import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; import ExtendedExample from '@docs/components/ExtendedExample.tsx'; Surface is a basic container that can give depth to an element with elevation shadow. -On dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface. -See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. -Overlay and shadow can be applied by specifying the `elevation` property both on Android and iOS. + +On Android, Surface uses the native `elevation` style, +and falls back to shadows that approximate the elevation on other platforms. @@ -26,7 +26,7 @@ import { Surface, Text } from 'react-native-paper'; import { StyleSheet } from 'react-native'; const MyComponent = () => ( - + Surface ); @@ -35,9 +35,9 @@ export default MyComponent; const styles = StyleSheet.create({ surface: { - padding: 8, height: 80, width: 80, + padding: 8, alignItems: 'center', justifyContent: 'center', }, @@ -52,11 +52,131 @@ const styles = StyleSheet.create({
-### children (required) +### backgroundColor
- + + +
+ +### borderRadius + +
+ + + +
+ +### borderBottomEndRadius + +
+ + + +
+ +### borderBottomLeftRadius + +
+ + + +
+ +### borderBottomRightRadius + +
+ + + +
+ +### borderBottomStartRadius + +
+ + + +
+ +### borderEndEndRadius + +
+ + + +
+ +### borderEndStartRadius + +
+ + + +
+ +### borderStartEndRadius + +
+ + + +
+ +### borderStartStartRadius + +
+ + + +
+ +### borderTopEndRadius + +
+ + + +
+ +### borderTopLeftRadius + +
+ + + +
+ +### borderTopRightRadius + +
+ + + +
+ +### borderTopStartRadius + +
+ + + +
+ +### borderCurve + +
+ + + +
+ +### transitionDuration + +
+ +
@@ -92,6 +212,14 @@ const styles = StyleSheet.create({
+### children (required) + +
+ + + +
+ ### testID
diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index 1fcd32bd25..e488b101ef 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -4,8 +4,162 @@ title: Migration from Paper 5.x to 6.x TBC +## General changes + +### Animations + +React Native Paper 6 uses [Reanimated](https://docs.swmansion.com/react-native-reanimated/) for animations as opposed to the built-in React Native `Animated` API. + +The following props now accept animated styles returned from `useAnimatedStyle`. They no longer accept `Animated.Value` or `Animated.AnimatedInterpolation` where these were previously supported: + +- `Appbar.Action` and `Appbar.BackAction`: `style` +- `Badge`: `style` +- `Banner`: `style` +- `Button`: `style` +- `Card`: `style` +- `Chip`: `style` +- `Dialog`: `style` +- `FAB` and `FAB.Extended`: `style` +- `IconButton`: `style` +- `Menu`: `contentStyle` +- `Modal`: `contentContainerStyle` +- `Searchbar`: `style` +- `Snackbar`: `style` +- `Surface`: `style` +- `ToggleButton`: `style` + +So you can use Reanimated's `useSharedValue` and `useAnimatedStyle` to animate these components instead of the React Native `Animated` API. + +```tsx +import { useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; + +const MyComponent = () => { + const opacity = useSharedValue(1); + const animatedStyle = useAnimatedStyle(() => ({ + opacity: opacity.value, + })); + + return Button; +}; +``` + +### Elevation + +The `elevation` prop no longer accepts a React Native `Animated.Value` in the following components: + +- `Banner` +- `Card` +- `Searchbar` +- `Snackbar` +- `Surface` + +You can use an elevation level from `0` to `5` instead. Changes to the elevation level are animated automatically. + +### Styles + +The following component style props no longer support overriding their background color or border radius: + +- `Banner` +- `Button` +- `Card` +- `Chip` +- `Dialog` +- `Menu`: `contentStyle` +- `Searchbar` +- `Snackbar` + +You can use the component's color prop where available, or override the corresponding theme colors. + +### Test IDs + +Some hardcoded and generated test IDs have been removed for the following components: + +- `Appbar.Header`: `${testID}-root-layer` +- `Surface`: `surface` and `${testID}-outer-layer` + +You can specify a `testID` explicitly and use that value to query the component. + ## Components +### Appbar + +The `style` props for `Appbar` and `Appbar.Header` no longer accept `Animated.Value` or `Animated.AnimatedInterpolation`. They only accept static styles. + +The `style.elevation` property is no longer supported. Use the `elevated` prop to control Appbar elevation. + +### Surface + +- The `elevation` prop no longer accepts a React Native `Animated.Value`. Any `elevation` changes are animated automatically. +- The `style` prop no longer configures elevation, background color, or border radius. Use these props instead: + - `elevation` + - `backgroundColor` + - `borderRadius` + - `borderBottomEndRadius` + - `borderBottomLeftRadius` + - `borderBottomRightRadius` + - `borderBottomStartRadius` + - `borderEndEndRadius` + - `borderEndStartRadius` + - `borderStartEndRadius` + - `borderStartStartRadius` + - `borderTopEndRadius` + - `borderTopLeftRadius` + - `borderTopRightRadius` + - `borderTopStartRadius` + - `borderCurve` +- The `pointerEvents` prop is no longer supported as it's deprecated in React Native Web. You can specify `pointerEvents` in the `style` prop instead. +- The `overflow: 'hidden'` style is no longer supported in `style` as it can clip shadows. You can nest a `View` inside the `Surface` and apply `overflow: 'hidden'` to that instead. +- The default `testID` for `Surface` was removed. You can specify a `testID` explicitly if you need it. + +e.g.: + +```diff + ++ + Content ++ + +``` + +### Modal + +- The `contentContainerStyle` prop no longer configures the background color or any border radius property. We have added new props for these: + - `contentBackgroundColor` + - `contentBorderRadius` +- We have added the `contentElevation` prop to configure the elevation of the modal content. + +e.g.: + +```diff + + Content + +``` + +### Dialog + +- The default elevation changed from level `1` to level `3`. +- The `style` prop no longer configures the background color or border radius. You can override `theme.colors.surfaceContainerHigh` and `theme.shapes.corner.extraLarge` using the `theme` prop instead. + ### TextInput The Paper 6.x `TextInput` is a complete rewrite with a new API. Import the component the same way, but note that the props and behavior have changed significantly. diff --git a/docs/src/data/componentDocs6x.json b/docs/src/data/componentDocs6x.json index 300c315b99..b223c9c0a8 100644 --- a/docs/src/data/componentDocs6x.json +++ b/docs/src/data/componentDocs6x.json @@ -153,7 +153,11 @@ "tsType": { "name": "boolean" }, - "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment." + "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment.", + "defaultValue": { + "value": "false", + "computed": false + } }, "safeAreaInsets": { "required": false, @@ -206,19 +210,23 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", + "name": "Omit", "elements": [ { "name": "ViewStyle" + }, + { + "name": "literal", + "value": "'elevation'" } ], - "raw": "StyleProp" + "raw": "Omit" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" } @@ -304,19 +312,19 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", + "name": "AnimatedStyle", "elements": [ { "name": "ViewStyle" } ], - "raw": "StyleProp" + "raw": "AnimatedStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp>" }, "description": "" }, @@ -416,19 +424,19 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", + "name": "AnimatedStyle", "elements": [ { "name": "ViewStyle" } ], - "raw": "StyleProp" + "raw": "AnimatedStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp>" }, "description": "" }, @@ -694,19 +702,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "AppbarStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -1032,10 +1034,16 @@ "name": "StyleProp", "elements": [ { - "name": "TextStyle" + "name": "AnimatedStyle", + "elements": [ + { + "name": "TextStyle" + } + ], + "raw": "AnimatedStyle" } ], - "raw": "StyleProp" + "raw": "StyleProp>" }, "description": "" }, @@ -1147,37 +1155,7 @@ "elevation": { "required": false, "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] + "name": "Elevation" }, "description": "@supported Available in v5.x with theme version 3\nChanges Banner shadow and background on iOS and Android.", "defaultValue": { @@ -1195,19 +1173,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -1234,7 +1206,35 @@ "onShowAnimationFinished": { "required": false, "tsType": { - "name": "Animated.EndCallback" + "name": "signature", + "type": "function", + "raw": "(result: { finished: boolean }) => void", + "signature": { + "arguments": [ + { + "name": "result", + "type": { + "name": "signature", + "type": "object", + "raw": "{ finished: boolean }", + "signature": { + "properties": [ + { + "key": "finished", + "value": { + "name": "boolean", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "void" + } + } }, "description": "\nOptional callback that will be called after the opening animation finished running normally", "defaultValue": { @@ -1245,7 +1245,35 @@ "onHideAnimationFinished": { "required": false, "tsType": { - "name": "Animated.EndCallback" + "name": "signature", + "type": "function", + "raw": "(result: { finished: boolean }) => void", + "signature": { + "arguments": [ + { + "name": "result", + "type": { + "name": "signature", + "type": "object", + "raw": "{ finished: boolean }", + "signature": { + "properties": [ + { + "key": "finished", + "value": { + "name": "boolean", + "required": true + } + } + ] + } + } + } + ], + "return": { + "name": "void" + } + } }, "description": "\nOptional callback that will be called after the closing animation finished running normally", "defaultValue": { @@ -3226,19 +3254,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -3449,37 +3471,7 @@ "elevation": { "required": false, "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] + "name": "Elevation" }, "description": "Changes Card shadow and background on iOS and Android.", "defaultValue": { @@ -3503,19 +3495,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -3543,6 +3529,19 @@ "name": "boolean" }, "description": "Pass down accessible from card props to touchable" + }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "Reference to the card container." } } }, @@ -4590,19 +4589,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -4646,6 +4639,19 @@ }, "description": "Specifies the largest possible scale a text font can reach." }, + "ref": { + "required": false, + "tsType": { + "name": "ReactRef", + "raw": "React.Ref", + "elements": [ + { + "name": "View" + } + ] + }, + "description": "Reference to the chip container." + }, "role": { "defaultValue": { "value": "'button'", @@ -5273,19 +5279,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -6124,10 +6124,16 @@ "name": "StyleProp", "elements": [ { - "name": "ViewStyle" + "name": "AnimatedStyle", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "AnimatedStyle" } ], - "raw": "StyleProp" + "raw": "StyleProp>" }, "description": "Style for positioning the FAB. The visual treatment (size, shape, color)\nis driven by `variant` and `size`." }, @@ -6339,10 +6345,16 @@ "name": "StyleProp", "elements": [ { - "name": "ViewStyle" + "name": "AnimatedStyle", + "elements": [ + { + "name": "ViewStyle" + } + ], + "raw": "AnimatedStyle" } ], - "raw": "StyleProp" + "raw": "StyleProp>" }, "description": "Style for positioning the FAB. The visual treatment (size, shape, color)\nis driven by `variant` and `size`." }, @@ -7929,19 +7941,19 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", + "name": "AnimatedStyle", "elements": [ { "name": "ViewStyle" } ], - "raw": "StyleProp" + "raw": "AnimatedStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp>" }, "description": "" }, @@ -9059,19 +9071,13 @@ "contentStyle": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "Style of menu's inner content." }, @@ -9375,10 +9381,10 @@ "Modal": { "filepath": "Modal.tsx", "title": "Modal", - "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n\n const containerStyle = { padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", "link": "modal", "data": { - "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", + "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n\n const containerStyle = { padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", "displayName": "Modal", "methods": [], "statics": [], @@ -9457,21 +9463,42 @@ "contentContainerStyle": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, - "description": "Style for the content of the modal" + "description": "Style for the content of the modal.\n\nBackground color and border radius should be specified via props instead:\n- `contentBackgroundColor`\n- `contentBorderRadius`" + }, + "contentBackgroundColor": { + "required": false, + "tsType": { + "name": "SurfaceProps['backgroundColor']", + "raw": "SurfaceProps['backgroundColor']" + }, + "description": "Background color of the modal content. Defaults to transparent.", + "defaultValue": { + "value": "'transparent'", + "computed": false + } + }, + "contentBorderRadius": { + "required": false, + "tsType": { + "name": "SurfaceProps['borderRadius']", + "raw": "SurfaceProps['borderRadius']" + }, + "description": "Border radius of the modal content." + }, + "contentElevation": { + "required": false, + "tsType": { + "name": "Elevation" + }, + "description": "Elevation level of the modal content. Defaults to level 1." }, "style": { "required": false, @@ -10546,37 +10573,7 @@ "elevation": { "required": false, "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] + "name": "Elevation" }, "description": "@supported Available in v5.x with theme version 3\nChanges Searchbar shadow and background on iOS and Android.", "defaultValue": { @@ -10600,19 +10597,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -11018,37 +11009,7 @@ "elevation": { "required": false, "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] + "name": "Elevation" }, "description": "@supported Available in v5.x with theme version 3\nChanges Snackbar shadow and background on iOS and Android.", "defaultValue": { @@ -11092,19 +11053,13 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" + "name": "SurfaceStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, "description": "" }, @@ -11145,54 +11100,186 @@ "Surface": { "filepath": "Surface.tsx", "title": "Surface", - "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", + "description": "Surface is a basic container that can give depth to an element with elevation shadow.\n\nOn Android, Surface uses the native `elevation` style,\nand falls back to shadows that approximate the elevation on other platforms.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n height: 80,\n width: 80,\n padding: 8,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", "link": "surface", "data": { - "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", + "description": "Surface is a basic container that can give depth to an element with elevation shadow.\n\nOn Android, Surface uses the native `elevation` style,\nand falls back to shadows that approximate the elevation on other platforms.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n height: 80,\n width: 80,\n padding: 8,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", "displayName": "Surface", "methods": [], "statics": [], "props": { - "children": { - "required": true, + "backgroundColor": { + "required": false, "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" + "name": "ColorValue" }, - "description": "Content of the `Surface`." + "description": "Background color of the Surface. Overrides the color derived from\n`elevation`." + }, + "borderRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of every corner of the Surface." + }, + "borderBottomEndRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the bottom-end corner of the Surface." + }, + "borderBottomLeftRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the bottom-left corner of the Surface." + }, + "borderBottomRightRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the bottom-right corner of the Surface." + }, + "borderBottomStartRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the bottom-start corner of the Surface." + }, + "borderEndEndRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the end-end corner of the Surface." + }, + "borderEndStartRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the end-start corner of the Surface." + }, + "borderStartEndRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the start-end corner of the Surface." + }, + "borderStartStartRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the start-start corner of the Surface." + }, + "borderTopEndRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the top-end corner of the Surface." + }, + "borderTopLeftRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the top-left corner of the Surface." + }, + "borderTopRightRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the top-right corner of the Surface." + }, + "borderTopStartRadius": { + "required": false, + "tsType": { + "name": "Extract['borderRadius']", + "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" + }, + "description": "Radius of the top-start corner of the Surface." + }, + "borderCurve": { + "required": false, + "tsType": { + "name": "ViewStyle['borderCurve']", + "raw": "ViewStyle['borderCurve']" + }, + "description": "Corner curve of the Surface on iOS.", + "defaultValue": { + "value": "'continuous'", + "computed": false + } + }, + "transitionDuration": { + "required": false, + "tsType": { + "name": "number" + }, + "description": "Duration of the background, elevation, and shadow transitions in\nmilliseconds." }, "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", + "name": "AnimatedStyle", "elements": [ { - "name": "ViewStyle" + "name": "Omit", + "elements": [ + { + "name": "ViewStyle" + }, + { + "name": "union", + "raw": "keyof SurfaceVisualProps | 'elevation'", + "elements": [ + { + "name": "unknown" + }, + { + "name": "literal", + "value": "'elevation'" + } + ] + } + ], + "raw": "Omit" } ], - "raw": "StyleProp" + "raw": "AnimatedStyle<\n Omit\n>" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp" }, - "description": "" + "description": "Style of the Surface.\n\nThis doesn't support all View style properties:\n- Background color and border radius should be specified via props instead.\n- `overflow: 'hidden'` is not supported with `elevation` as it can clip the shadow.\n To achieve the same effect, wrap the content in a child View with the overflow style." }, "elevation": { "required": false, "tsType": { - "name": "union", - "raw": "Elevation | Animated.Value", - "elements": [ - { - "name": "Elevation" - }, - { - "name": "Animated.Value" - } - ] + "name": "Elevation" }, "description": "@supported Available in v5.x with theme version 3\nChanges shadows and background on iOS and Android.\nUsed to create UI hierarchy between components.\n\nNote: If `mode` is set to `flat`, Surface doesn't have a shadow.\n\nNote: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`.\nIt's no longer supported with theme version 3 and you should use `elevation` property instead.", "defaultValue": { @@ -11229,16 +11316,20 @@ }, "description": "" }, + "children": { + "required": true, + "tsType": { + "name": "ReactReactNode", + "raw": "React.ReactNode" + }, + "description": "Content of the `Surface`." + }, "testID": { "required": false, "tsType": { "name": "string" }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'surface'", - "computed": false - } + "description": "TestID used for testing purposes" }, "ref": { "required": false, @@ -11252,13 +11343,6 @@ ] }, "description": "" - }, - "container": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@internal" } } }, @@ -11780,19 +11864,19 @@ "style": { "required": false, "tsType": { - "name": "Animated.WithAnimatedValue", + "name": "StyleProp", "elements": [ { - "name": "StyleProp", + "name": "AnimatedStyle", "elements": [ { "name": "ViewStyle" } ], - "raw": "StyleProp" + "raw": "AnimatedStyle" } ], - "raw": "Animated.WithAnimatedValue>" + "raw": "StyleProp>" }, "description": "" }, diff --git a/example/src/Examples/BannerExample.tsx b/example/src/Examples/BannerExample.tsx index 5679a92518..2a99b38866 100644 --- a/example/src/Examples/BannerExample.tsx +++ b/example/src/Examples/BannerExample.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { Dimensions, Image, Platform, StyleSheet, View } from 'react-native'; -import type { LayoutChangeEvent } from 'react-native'; import { Banner, FAB, Palette, useTheme } from 'react-native-paper'; @@ -15,13 +14,6 @@ const BannerExample = () => { const [useCustomTheme, setUseCustomTheme] = React.useState(false); const defaultTheme = useTheme(); - const [height, setHeight] = React.useState(0); - - const handleLayout = ({ nativeEvent }: LayoutChangeEvent) => { - const { height: layoutHeight } = nativeEvent.layout; - setHeight(layoutHeight); - }; - const customTheme = { ...defaultTheme, colors: { @@ -37,23 +29,7 @@ const BannerExample = () => { return ( - - - {PHOTOS.map((uri) => ( - - - - ))} - - - setVisible(!visible)} /> { console.log('Completed closing animation') } theme={useCustomTheme ? customTheme : defaultTheme} - style={styles.banner} > Two line text string with two actions. One to two lines is preferable on mobile. + + + {PHOTOS.map((uri) => ( + + + + ))} + + + setVisible(!visible)} /> ); }; @@ -117,12 +107,6 @@ const styles = StyleSheet.create({ }, }, }), - banner: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - }, photo: { flex: 1, }, diff --git a/example/src/Examples/ButtonExample.tsx b/example/src/Examples/ButtonExample.tsx index e250644756..2a164e8e77 100644 --- a/example/src/Examples/ButtonExample.tsx +++ b/example/src/Examples/ButtonExample.tsx @@ -283,18 +283,22 @@ const ButtonExample = () => { - @@ -380,17 +384,7 @@ const styles = StyleSheet.create({ width: '100%', marginTop: 10, }, - customRadius: { - borderTopLeftRadius: 16, - borderTopRightRadius: 0, - borderBottomLeftRadius: 0, - borderBottomRightRadius: 16, - }, - noRadius: { - borderRadius: 0, - }, - customRadiusAndPadding: { - borderRadius: 4, + customPadding: { paddingHorizontal: 12, paddingVertical: 6, }, diff --git a/example/src/Examples/CardExample.tsx b/example/src/Examples/CardExample.tsx index 61c7ebe822..1061d4f904 100644 --- a/example/src/Examples/CardExample.tsx +++ b/example/src/Examples/CardExample.tsx @@ -104,17 +104,24 @@ const CardExample = () => { /> - - { accessibilityIgnoresInvertColors /> } - style={[styles.chip, styles.customBorderRadius]} + style={styles.chip} + theme={{ shapes: { corner: { small: 16 } } }} > Compact with custom border radius @@ -258,7 +259,8 @@ const ChipExample = () => { accessibilityIgnoresInvertColors /> } - style={[styles.chip, styles.customBorderRadius]} + style={styles.chip} + theme={{ shapes: { corner: { small: 16 } } }} > Compact with custom border radius @@ -275,12 +277,15 @@ const ChipExample = () => { {}} - style={[ - styles.chip, - { - backgroundColor: color(customColor).alpha(0.2).rgb().string(), + style={styles.chip} + theme={{ + colors: { + secondaryContainer: color(customColor) + .alpha(0.2) + .rgb() + .string(), }, - ]} + }} selectedColor={customColor} > Flat selected chip with custom color @@ -296,12 +301,12 @@ const ChipExample = () => { selected mode="outlined" onPress={() => {}} - style={[ - styles.chip, - { - backgroundColor: color(customColor).alpha(0.2).rgb().string(), + style={styles.chip} + theme={{ + colors: { + surface: color(customColor).alpha(0.2).rgb().string(), }, - ]} + }} selectedColor={customColor} > Outlined selected chip with custom color @@ -403,9 +408,6 @@ const styles = StyleSheet.create({ marginVertical: 4, marginHorizontal: 12, }, - customBorderRadius: { - borderRadius: 16, - }, }); export default ChipExample; diff --git a/example/src/Examples/Dialogs/DialogWithCustomColors.tsx b/example/src/Examples/Dialogs/DialogWithCustomColors.tsx index f3625b835e..75b5ee6b6e 100644 --- a/example/src/Examples/Dialogs/DialogWithCustomColors.tsx +++ b/example/src/Examples/Dialogs/DialogWithCustomColors.tsx @@ -13,8 +13,10 @@ const DialogWithCustomColors = ({ diff --git a/example/src/Examples/SurfaceExample.tsx b/example/src/Examples/SurfaceExample.tsx index fe420830ce..e941c7c843 100644 --- a/example/src/Examples/SurfaceExample.tsx +++ b/example/src/Examples/SurfaceExample.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Animated, ScrollView, StyleSheet, View } from 'react-native'; +import { ScrollView, StyleSheet, View } from 'react-native'; import { Surface, Text, Palette, List, IconButton } from 'react-native-paper'; import type { Elevation } from 'react-native-paper'; @@ -12,19 +12,10 @@ const AnimatedSurface = () => { const [index, setIndex] = React.useState(3); const level = elevationLevels[index]; - const elevation = React.useRef(new Animated.Value(level)).current; - - React.useEffect(() => { - Animated.timing(elevation, { - toValue: level, - duration: 250, - useNativeDriver: false, - }).start(); - }, [elevation, level]); return ( - + {`Elevation ${level}`} @@ -49,7 +40,13 @@ const SurfaceExample = () => { const elevationValues: Elevation[] = [0, 1, 2, 3, 4, 5]; const renderSurface = (index: Elevation, mode: 'flat' | 'elevated') => ( - + {`Elevation ${index}`} ); @@ -93,10 +90,14 @@ const SurfaceExample = () => { - + Top - + Bottom @@ -121,7 +122,6 @@ const styles = StyleSheet.create({ surface: { height: 120, width: 120, - borderRadius: 8, alignItems: 'center', justifyContent: 'center', }, @@ -153,6 +153,8 @@ const styles = StyleSheet.create({ }, verticalSurface: { height: '48%', + }, + verticalSurfaceContent: { justifyContent: 'center', }, diff --git a/jest/reanimatedSnapshotSerializer.js b/jest/reanimatedSnapshotSerializer.js new file mode 100644 index 0000000000..0d20dec218 --- /dev/null +++ b/jest/reanimatedSnapshotSerializer.js @@ -0,0 +1,27 @@ +const reanimatedTestProps = new Set([ + 'jestAnimatedProps', + 'jestAnimatedStyle', + 'jestInlineStyle', +]); + +module.exports = { + test(value) { + return ( + value !== null && + typeof value === 'object' && + value.props !== null && + typeof value.props === 'object' && + Object.keys(value.props).some((prop) => reanimatedTestProps.has(prop)) + ); + }, + print(value, serialize) { + return serialize({ + ...value, + props: Object.fromEntries( + Object.entries(value.props).filter( + ([prop]) => !reanimatedTestProps.has(prop) + ) + ), + }); + }, +}; diff --git a/jest/testSetup.js b/jest/testSetup.js index c00e611084..1033126c45 100644 --- a/jest/testSetup.js +++ b/jest/testSetup.js @@ -4,13 +4,10 @@ jest.useFakeTimers(); jest.mock('react-native-safe-area-context', () => mockSafeAreaContext); -jest.mock('react-native-worklets', () => - require('react-native-worklets/lib/module/mock') -); - -jest.mock('react-native-reanimated', () => - require('react-native-reanimated/mock') -); +jest.mock('react-native-worklets', () => ({ + ...require('react-native-worklets/lib/module/mock'), + isUIRuntime: () => false, +})); jest.mock('@react-native-vector-icons/material-design-icons', () => { const React = require('react'); diff --git a/package.json b/package.json index 25ca91fb73..6db11f9fef 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,9 @@ "/jest/jestSetupAfterEnv.js", "@testing-library/react-native" ], + "snapshotSerializers": [ + "/jest/reanimatedSnapshotSerializer.js" + ], "cacheDirectory": "./cache/jest", "testPathIgnorePatterns": [ "\\.d\\.ts$" diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index 8ef9965819..7e62fd4df9 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -1,19 +1,24 @@ import * as React from 'react'; -import { Animated, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import type { ColorValue, StyleProp, ViewProps, ViewStyle } from 'react-native'; import AppbarContent from './AppbarContent'; import { getAppbarBackgroundColor, + getAppbarBorders, modeAppbarHeight, renderAppbarContent, filterAppbarActions, } from './utils'; import type { AppbarModes, AppbarChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { Elevation, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import Surface from '../Surface'; +const APPBAR_HORIZONTAL_PADDING = 4; + +export type AppbarStyle = Omit; + export type Props = Omit, 'style'> & { /** * Whether the background color is a dark color. A dark appbar will render light text and vice-versa. @@ -51,7 +56,7 @@ export type Props = Omit, 'style'> & { * @optional */ theme?: ThemeProp; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; }; /** @@ -145,30 +150,26 @@ const Appbar = ({ dark, style, mode = 'small', - elevated, + elevated = false, safeAreaInsets, theme: themeOverrides, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); const flattenedStyle = StyleSheet.flatten(style); - const { - backgroundColor: customBackground, - elevation = elevated ? 2 : 0, - ...restStyle - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - } = (flattenedStyle || {}) as Exclude & { - elevation?: Elevation; + const { backgroundColor: customBackground, ...restStyle } = (flattenedStyle || + {}) as Exclude & { backgroundColor?: ColorValue; }; const backgroundColor = getAppbarBackgroundColor( theme, - elevation, - customBackground, - elevated + elevated, + customBackground ); + const borderStyles = getAppbarBorders(restStyle); + const isMode = (modeToCompare: AppbarModes) => { return mode === modeToCompare; }; @@ -210,24 +211,25 @@ const Appbar = ({ const insets = { paddingBottom: safeAreaInsets?.bottom, paddingTop: safeAreaInsets?.top, - paddingLeft: safeAreaInsets?.left, - paddingRight: safeAreaInsets?.right, + paddingLeft: (safeAreaInsets?.left ?? 0) + APPBAR_HORIZONTAL_PADDING, + paddingRight: (safeAreaInsets?.right ?? 0) + APPBAR_HORIZONTAL_PADDING, }; return ( {shouldAddLeftSpacing ? : null} {(isMode('small') || isMode('center-aligned')) && ( @@ -308,7 +310,6 @@ const styles = StyleSheet.create({ appbar: { flexDirection: 'row', alignItems: 'center', - paddingHorizontal: 4, }, v3Spacing: { width: 52, diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index 400271a988..c6d68a362e 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -1,11 +1,7 @@ import * as React from 'react'; -import type { - Animated, - ColorValue, - StyleProp, - View, - ViewStyle, -} from 'react-native'; +import type { ColorValue, StyleProp, View, ViewStyle } from 'react-native'; + +import type { AnimatedStyle } from 'react-native-reanimated'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; @@ -43,7 +39,7 @@ export type Props = React.ComponentPropsWithoutRef & { * Whether it's the leading button. Note: If `Appbar.BackAction` is present, it will be rendered before any `isLeading` icons. */ isLeading?: boolean; - style?: Animated.WithAnimatedValue>; + style?: StyleProp>; ref?: React.Ref; /** * @optional diff --git a/src/components/Appbar/AppbarBackAction.tsx b/src/components/Appbar/AppbarBackAction.tsx index 2835c27c91..c4249bd18a 100644 --- a/src/components/Appbar/AppbarBackAction.tsx +++ b/src/components/Appbar/AppbarBackAction.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import type { - Animated, ColorValue, GestureResponderEvent, StyleProp, @@ -8,6 +7,8 @@ import type { ViewStyle, } from 'react-native'; +import type { AnimatedStyle } from 'react-native-reanimated'; + import type { $Omit } from './../../types'; import AppbarAction from './AppbarAction'; import AppbarBackIcon from './AppbarBackIcon'; @@ -36,7 +37,7 @@ export type Props = $Omit< * Function to execute on press. */ onPress?: (e: GestureResponderEvent) => void; - style?: Animated.WithAnimatedValue>; + style?: StyleProp>; ref?: React.Ref; }; diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index 0252cc89ba..1c486f9ccd 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -1,22 +1,18 @@ import * as React from 'react'; -import { Animated, Platform, StyleSheet, View } from 'react-native'; -import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; +import { Platform, StyleSheet } from 'react-native'; +import type { ColorValue, StyleProp } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Appbar } from './Appbar'; -import { - getAppbarBackgroundColor, - modeAppbarHeight, - getAppbarBorders, -} from './utils'; +import type { AppbarStyle } from './Appbar'; +import { getAppbarBackgroundColor, modeAppbarHeight } from './utils'; import { useInternalTheme } from '../../core/theming'; -import { shadow } from '../../theme/tokens/sys/elevation'; import type { ThemeProp } from '../../types'; export type Props = Omit< React.ComponentProps, - 'safeAreaInsets' + 'safeAreaInsets' | 'style' > & { /** * Whether the background color is a dark color. A dark header will render light text and vice-versa. @@ -52,7 +48,7 @@ export type Props = Omit< * @optional */ theme?: ThemeProp; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; }; /** @@ -100,64 +96,53 @@ const AppbarHeader = ({ const flattenedStyle = StyleSheet.flatten(style); const { height = modeAppbarHeight[mode], - elevation = elevated ? 2 : 0, backgroundColor: customBackground, zIndex = elevated ? 1 : 0, ...restStyle - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion } = (flattenedStyle || {}) as Exclude & { - height?: number; - elevation?: number; + height?: AppbarStyle['height']; backgroundColor?: ColorValue; zIndex?: number; }; - const borderRadius = getAppbarBorders(restStyle); - const backgroundColor = getAppbarBackgroundColor( theme, - elevation, - customBackground, - elevated + elevated, + customBackground ); const { top, left, right } = useSafeAreaInsets(); + const topInset = statusBarHeight ?? top; + const horizontalInset = Math.max(left, right); + const headerHeight = typeof height === 'number' ? height + topInset : height; return ( - - - + safeAreaInsets={{ + top: topInset, + left: horizontalInset, + right: horizontalInset, + }} + dark={dark} + elevated={elevated} + {...rest} + mode={mode} + theme={theme} + /> ); }; AppbarHeader.displayName = 'Appbar.Header'; -const styles = StyleSheet.create({ - appbar: { - elevation: 0, - }, -}); - export default AppbarHeader; // @component-docs ignore-next-line diff --git a/src/components/Appbar/utils.ts b/src/components/Appbar/utils.ts index a6ea546b34..29978636b9 100644 --- a/src/components/Appbar/utils.ts +++ b/src/components/Appbar/utils.ts @@ -1,6 +1,6 @@ import React from 'react'; import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; -import { StyleSheet, Animated } from 'react-native'; +import { StyleSheet } from 'react-native'; import { white } from '../../theme/colors'; import type { InternalTheme, ThemeProp } from '../../types'; @@ -15,17 +15,25 @@ export type AppbarChildProps = { const borderStyleProperties = [ 'borderRadius', + 'borderBottomEndRadius', + 'borderBottomStartRadius', + 'borderEndEndRadius', + 'borderEndStartRadius', + 'borderStartEndRadius', + 'borderStartStartRadius', + 'borderTopEndRadius', + 'borderTopStartRadius', 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius', + 'borderCurve', ] satisfies readonly (keyof ViewStyle)[]; export const getAppbarBackgroundColor = ( theme: InternalTheme, - _elevation: number, - customBackground?: ColorValue, - elevated?: boolean + elevated: boolean, + customBackground?: ColorValue ) => { const { colors } = theme; if (customBackground) { @@ -54,19 +62,14 @@ export const getAppbarColor = ({ return undefined; }; -export const getAppbarBorders = ( - style: - | Animated.Value - | Animated.AnimatedInterpolation - | Animated.WithAnimatedObject -) => { - const borders: Record = {}; +export const getAppbarBorders = (style: ViewStyle) => { + let borders: ViewStyle = {}; for (const property of borderStyleProperties) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const value = style[property as keyof typeof style]; - if (value) { - borders[property] = value; + const value = style[property]; + + if (typeof value === 'number' || typeof value === 'string') { + borders = { ...borders, [property]: value }; } } diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 869adf6820..4567055f6b 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -1,7 +1,7 @@ import type { StyleProp, TextProps, TextStyle } from 'react-native'; import { StyleSheet } from 'react-native'; -import Animated from 'react-native-reanimated'; +import Animated, { type AnimatedStyle } from 'react-native-reanimated'; import { useInternalTheme } from '../core/theming'; import { cornerFull } from '../theme/tokens/sys/shape'; @@ -21,7 +21,7 @@ export type Props = TextProps & { * Content of the `Badge`. */ children?: string | number; - style?: StyleProp; + style?: StyleProp>; /** * @optional */ diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index f994396931..dddcf330c0 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -1,21 +1,37 @@ import * as React from 'react'; -import { Animated, StyleSheet, View } from 'react-native'; -import type { StyleProp, ViewStyle } from 'react-native'; -import type { LayoutChangeEvent } from 'react-native'; +import { StyleSheet, View } from 'react-native'; +import type { + LayoutChangeEvent, + StyleProp, + ViewProps, + ViewStyle, +} from 'react-native'; +import Animated, { + Easing, + interpolate, + ReduceMotion, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; +import { scheduleOnRN } from 'react-native-worklets'; import useLatestCallback from 'use-latest-callback'; import Button from './Button/Button'; import Icon from './Icon'; import type { IconSource } from './Icon'; import Surface from './Surface'; +import type { SurfaceStyle } from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; -import type { $Omit, $RemoveChildren, ThemeProp } from '../types'; +import type { $RemoveChildren, Elevation, ThemeProp } from '../types'; const DEFAULT_MAX_WIDTH = 960; -export type Props = $Omit<$RemoveChildren, 'mode'> & { +type AnimationFinishedCallback = (result: { finished: boolean }) => void; + +export type Props = Omit & { /** * Whether banner is currently visible. */ @@ -51,12 +67,12 @@ export type Props = $Omit<$RemoveChildren, 'mode'> & { * @supported Available in v5.x with theme version 3 * Changes Banner shadow and background on iOS and Android. */ - elevation?: 0 | 1 | 2 | 3 | 4 | 5 | Animated.Value; + elevation?: Elevation; /** * Specifies the largest possible scale a text font can reach. */ maxFontSizeMultiplier?: number; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; ref?: React.RefObject; /** * @optional @@ -66,12 +82,12 @@ export type Props = $Omit<$RemoveChildren, 'mode'> & { * @optional * Optional callback that will be called after the opening animation finished running normally */ - onShowAnimationFinished?: Animated.EndCallback; + onShowAnimationFinished?: AnimationFinishedCallback; /** * @optional * Optional callback that will be called after the closing animation finished running normally */ - onHideAnimationFinished?: Animated.EndCallback; + onHideAnimationFinished?: AnimationFinishedCallback; }; /** @@ -134,9 +150,9 @@ const Banner = ({ }: Props) => { const theme = useInternalTheme(themeOverrides); const { colors } = theme; - const { current: position } = React.useRef( - new Animated.Value(visible ? 1 : 0) - ); + + const position = useSharedValue(visible ? 1 : 0); + const [layout, setLayout] = React.useState<{ height: number; measured: boolean; @@ -149,30 +165,33 @@ const Banner = ({ const hideCallback = useLatestCallback(onHideAnimationFinished); const { scale } = theme.animation; - - const opacity = position.interpolate({ - inputRange: [0, 0.1, 1], - outputRange: [0, 1, 1], - }); + const animationDuration = (visible ? 250 : 200) * scale; React.useEffect(() => { - if (visible) { - // show - Animated.timing(position, { - duration: 250 * scale, - toValue: 1, - useNativeDriver: false, - }).start(showCallback); - } else { - // hide - Animated.timing(position, { - duration: 200 * scale, - toValue: 0, - useNativeDriver: false, - }).start(hideCallback); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [visible, position, scale]); + const callback = visible ? showCallback : hideCallback; + + position.value = withTiming( + visible ? 1 : 0, + { + duration: animationDuration, + easing: Easing.inOut(Easing.ease), + reduceMotion: ReduceMotion.Never, + }, + (finished) => scheduleOnRN(callback, { finished: finished ?? false }) + ); + }, [animationDuration, hideCallback, position, showCallback, visible]); + + const surfaceStyle = useAnimatedStyle(() => ({ + opacity: interpolate(position.value, [0, 0.1, 1], [0, 1, 1]), + })); + + const spacerStyle = useAnimatedStyle(() => ({ + height: position.value * layout.height, + })); + + const contentAnimationStyle = useAnimatedStyle(() => ({ + transform: [{ translateY: (position.value - 1) * layout.height }], + })); const handleLayout = ({ nativeEvent }: LayoutChangeEvent) => { const { height } = nativeEvent.layout; @@ -186,29 +205,22 @@ const Banner = ({ // Once we have the height, we apply the height to the spacer and switch the banner to position: absolute // We need this because we need to move the content below as if banner's height was being animated // However we can't animated banner's height directly as it'll also resize the content inside - const height = Animated.multiply(position, layout.height); - - const translateY = Animated.multiply( - Animated.add(position, -1), - layout.height - ); return ( - + ({ backgroundColor: customBackground, // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion } = (StyleSheet.flatten(style) || {}) as { - elevation?: number; backgroundColor?: ColorValue; }; @@ -462,9 +460,14 @@ const BottomNavigationBar = ({ bottom: safeAreaInsets?.bottom ?? bottom, }; + const pointerEvents = layout.measured + ? keyboardHidesNavigationBar && keyboardVisible + ? 'none' + : 'auto' + : 'none'; + return ( - ({ } : null, style, + { pointerEvents }, ]} - pointerEvents={ - layout.measured - ? keyboardHidesNavigationBar && keyboardVisible - ? 'none' - : 'auto' - : 'none' - } onLayout={onLayout} - container > ({ // This trick gives the illusion that we are animating between active and inactive colors. // This is to ensure that we can use native driver, as colors cannot be animated with native driver. const activeOpacity = active; + const inactiveOpacity = active.interpolate({ inputRange: [0, 1], outputRange: [1, 0], }); const v3ActiveOpacity = focused ? 1 : 0; + const v3InactiveOpacity = shifting ? inactiveOpacity : focused @@ -769,7 +767,7 @@ const BottomNavigationBar = ({ })} - + ); }; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index bfc7782d66..00f0242f0f 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Animated, Platform, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import type { ColorValue, GestureResponderEvent, @@ -7,24 +7,25 @@ import type { Role, StyleProp, TextStyle, + ViewProps, ViewStyle, } from 'react-native'; import { getButtonColors, getButtonTouchableRippleStyle } from './utils'; import type { ButtonMode } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $Omit, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; -import { splitStyles } from '../../utils/splitStyles'; import ActivityIndicator from '../ActivityIndicator'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; import Surface from '../Surface'; +import type { SurfaceStyle } from '../Surface'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; -export type Props = $Omit, 'mode'> & { +export type Props = Omit & { /** * Mode of the button. You can change the mode to adjust the styling to give it desired emphasis. * - `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options. @@ -122,7 +123,7 @@ export type Props = $Omit, 'mode'> & { * Sets additional distance outside of element in which a press can be detected. */ hitSlop?: TouchableRippleProps['hitSlop']; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; /** * Style for the button text. */ @@ -192,15 +193,15 @@ const Button = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const isMode = React.useCallback( (modeToCompare: ButtonMode) => { return mode === modeToCompare; }, [mode] ); - const { animation } = theme; + const uppercase = uppercaseProp ?? false; - const isWeb = Platform.OS === 'web'; const hasPassedTouchHandler = hasTouchHandler({ onPress, @@ -213,52 +214,33 @@ const Button = ({ const initialElevation = 1; const activeElevation = 2; - const { current: elevation } = React.useRef( - new Animated.Value(isElevationEntitled ? initialElevation : 0) - ); + const [pressed, setPressed] = React.useState(false); - React.useEffect(() => { - // Workaround not to call setValue on Animated.Value, because it breaks styles. - // https://github.com/callstack/react-native-paper/issues/4559 - Animated.timing(elevation, { - toValue: isElevationEntitled ? initialElevation : 0, - duration: 0, - useNativeDriver: true, - }); - }, [isElevationEntitled, elevation, initialElevation]); + const elevation = isElevationEntitled + ? pressed + ? activeElevation + : initialElevation + : 0; const handlePressIn = (e: GestureResponderEvent) => { onPressIn?.(e); - if (isMode('elevated')) { - const { scale } = animation; - Animated.timing(elevation, { - toValue: activeElevation, - duration: 200 * scale, - useNativeDriver: - isWeb || Platform.constants.reactNativeVersion.minor <= 72, - }).start(); + + if (isElevationEntitled) { + setPressed(true); } }; const handlePressOut = (e: GestureResponderEvent) => { onPressOut?.(e); - if (isMode('elevated')) { - const { scale } = animation; - Animated.timing(elevation, { - toValue: initialElevation, - duration: 150 * scale, - useNativeDriver: - isWeb || Platform.constants.reactNativeVersion.minor <= 72, - }).start(); + + if (isElevationEntitled) { + setPressed(false); } }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; - const [, borderRadiusStyles] = splitStyles( - flattenedStyles, - (style) => style.startsWith('border') && style.endsWith('Radius') - ); + const elevationTransitionDuration = + theme.motion.duration[pressed ? 'short4' : 'short3'] * + theme.animation.scale; const borderRadius = theme.shapes.corner.largeIncreased; const iconSize = 18; @@ -279,17 +261,7 @@ const Button = ({ dark, }); - const touchableStyle = { - ...borderRadiusStyles, - borderRadius: borderRadiusStyles.borderRadius ?? borderRadius, - }; - - const buttonStyle = { - backgroundColor: backgroundOpacity < 1 ? 'transparent' : backgroundColor, - borderColor, - borderWidth, - ...touchableStyle, - }; + const touchableStyle = { borderRadius }; const { color: customLabelColor, fontSize: customLabelSize } = StyleSheet.flatten(labelStyle) || {}; @@ -321,9 +293,19 @@ const Button = ({ {...rest} ref={ref} testID={`${testID}-container`} - style={[styles.button, compact && styles.compact, buttonStyle, style]} + backgroundColor={backgroundOpacity < 1 ? 'transparent' : backgroundColor} + {...touchableStyle} + style={[ + styles.button, + compact && styles.compact, + { + borderColor, + borderWidth, + }, + style, + ]} elevation={elevation} - container + transitionDuration={elevationTransitionDuration} > {backgroundOpacity < 1 && ( , 'mode'> & { +export type Props = Omit & { /** * Mode of the Card. * - `elevated` - Card with elevation. @@ -73,12 +76,12 @@ export type Props = $Omit, 'mode'> & { /** * Changes Card shadow and background on iOS and Android. */ - elevation?: 0 | 1 | 2 | 3 | 4 | 5 | Animated.Value; + elevation?: Elevation; /** * Style of card's inner content. */ contentStyle?: StyleProp; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; /** * @optional */ @@ -91,6 +94,10 @@ export type Props = $Omit, 'mode'> & { * Pass down accessible from card props to touchable */ accessible?: boolean; + /** + * Reference to the card container. + */ + ref?: React.Ref; }; /** @@ -141,6 +148,7 @@ const Card = ({ ...rest }: (OutlinedCardProps | ElevatedCardProps | ContainedCardProps) & Props) => { const theme = useInternalTheme(themeOverrides); + const isMode = React.useCallback( (modeToCompare: Mode) => { return cardMode === modeToCompare; @@ -155,34 +163,23 @@ const Card = ({ onPressOut, }); - const { current: elevation } = React.useRef( - new Animated.Value(cardElevation) - ); - const { animation } = theme; - - const animationDuration = 150 * animation.scale; - - const runElevationAnimation = (pressType: HandlePressType) => { - if (isMode('contained')) { - return; - } - - const isPressTypeIn = pressType === 'in'; - Animated.timing(elevation, { - toValue: isPressTypeIn ? 2 : cardElevation, - duration: animationDuration, - useNativeDriver: false, - }).start(); - }; + const [pressed, setPressed] = React.useState(false); + const elevation = isMode('elevated') ? (pressed ? 2 : cardElevation) : 0; const handlePressIn = useLatestCallback((e: GestureResponderEvent) => { onPressIn?.(e); - runElevationAnimation('in'); + + if (isMode('elevated')) { + setPressed(true); + } }); const handlePressOut = useLatestCallback((e: GestureResponderEvent) => { onPressOut?.(e); - runElevationAnimation('out'); + + if (isMode('elevated')) { + setPressed(false); + } }); const total = React.Children.count(children); @@ -204,15 +201,7 @@ const Card = ({ const { borderColor = themedBorderColor } = flattenedStyles; - const [, borderRadiusStyles] = splitStyles( - flattenedStyles, - (style) => style.startsWith('border') && style.endsWith('Radius') - ); - - const borderRadiusCombinedStyles = { - borderRadius: theme.shapes.corner.medium, - ...borderRadiusStyles, - }; + const borderRadius = theme.shapes.corner.medium; const content = ( @@ -222,7 +211,6 @@ const Card = ({ index, total, siblings, - borderRadiusStyles, }) : child )} @@ -232,15 +220,12 @@ const Card = ({ return ( {isMode('outlined') && ( @@ -252,7 +237,7 @@ const Card = ({ borderColor, }, styles.outline, - borderRadiusCombinedStyles, + { borderRadius }, ]} /> )} diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index b6482e9209..3bbbe32cf5 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; -import { Animated, Platform, StyleSheet, Pressable, View } from 'react-native'; +import { Platform, StyleSheet, Pressable, View } from 'react-native'; import type { ColorValue, GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, TextStyle, - ViewStyle, + ViewProps, } from 'react-native'; import useLatestCallback from 'use-latest-callback'; @@ -15,17 +15,18 @@ import { getChipColors } from './helpers'; import type { ChipAvatarProps } from './helpers'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; -import type { $Omit, EllipsizeProp, ThemeProp } from '../../types'; +import type { EllipsizeProp, ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import Surface from '../Surface'; +import type { SurfaceStyle } from '../Surface'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; -export type Props = $Omit, 'mode'> & { +export type Props = Omit & { /** * Mode of the chip. * - `flat` - flat chip without outline. @@ -123,7 +124,7 @@ export type Props = $Omit, 'mode'> & { * Style of chip's text */ textStyle?: StyleProp; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; /** * Sets additional distance outside of element in which a press can be detected. */ @@ -144,6 +145,10 @@ export type Props = $Omit, 'mode'> & { * Specifies the largest possible scale a text font can reach. */ maxFontSizeMultiplier?: number; + /** + * Reference to the chip container. + */ + ref?: React.Ref; }; /** @@ -201,11 +206,9 @@ const Chip = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); - const isWeb = Platform.OS === 'web'; - const { current: elevation } = React.useRef( - new Animated.Value(elevated ? 1 : 0) - ); + const [pressed, setPressed] = React.useState(false); + const elevation = elevated ? (pressed ? 2 : 1) : 0; const hasPassedTouchHandler = hasTouchHandler({ onPress, @@ -217,36 +220,24 @@ const Chip = ({ const isOutlined = mode === 'outlined'; const handlePressIn = useLatestCallback((e: GestureResponderEvent) => { - const { scale } = theme.animation; onPressIn?.(e); - Animated.timing(elevation, { - toValue: elevated ? 2 : 0, - duration: 200 * scale, - useNativeDriver: - isWeb || Platform.constants.reactNativeVersion.minor <= 72, - }).start(); + setPressed(true); }); const handlePressOut = useLatestCallback((e: GestureResponderEvent) => { - const { scale } = theme.animation; onPressOut?.(e); - Animated.timing(elevation, { - toValue: elevated ? 1 : 0, - duration: 150 * scale, - useNativeDriver: - isWeb || Platform.constants.reactNativeVersion.minor <= 72, - }).start(); + setPressed(false); }); + const elevationTransitionDuration = + theme.motion.duration[pressed ? 'short4' : 'short3'] * + theme.animation.scale; + const opacity = 0.38; const defaultBorderRadius = theme.shapes.corner.small; const iconSize = 18; - const { - backgroundColor: customBackgroundColor, - borderRadius = defaultBorderRadius, - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - } = (StyleSheet.flatten(style) || {}) as ViewStyle; + const borderRadius = defaultBorderRadius; const { borderColor, @@ -259,12 +250,11 @@ const Chip = ({ isOutlined, theme, selectedColor, - customBackgroundColor, disabled, }); - const elevationStyle = elevation; const multiplier = compact ? 1.5 : 2; + const labelSpacings = { marginRight: onClose ? 0 : 8 * multiplier, marginLeft: @@ -272,30 +262,26 @@ const Chip = ({ ? 4 * multiplier : 8 * multiplier, }; + const contentSpacings = { paddingRight: onClose ? 34 : 0, }; + const labelTextStyle = { color: textColor, ...theme.fonts.labelLarge, }; + return ( { +}: BaseProps & { selectedColor?: ColorValue }) => { const isSelectedColor = selectedColor !== undefined; const { colors } = md3(theme); @@ -85,19 +85,8 @@ const getDefaultBackgroundColor = ({ return colors.secondaryContainer; }; -const getBackgroundColor = ({ - theme, - isOutlined, - disabled, - customBackgroundColor, -}: BaseProps & { - customBackgroundColor?: ColorValue; -}) => { +const getBackgroundColor = ({ theme, isOutlined, disabled }: BaseProps) => { const { colors } = md3(theme); - if (typeof customBackgroundColor === 'string') { - return customBackgroundColor; - } - if (disabled) { if (isOutlined) { return 'transparent'; @@ -108,22 +97,6 @@ const getBackgroundColor = ({ return getDefaultBackgroundColor({ theme, isOutlined }); }; -const getSelectedBackgroundColor = ({ - theme, - isOutlined, - disabled, - customBackgroundColor, -}: BaseProps & { - customBackgroundColor?: ColorValue; -}) => { - return getBackgroundColor({ - theme, - disabled, - isOutlined, - customBackgroundColor, - }); -}; - const getIconColor = ({ theme, isOutlined, @@ -153,10 +126,8 @@ export const getChipColors = ({ isOutlined, theme, selectedColor, - customBackgroundColor, disabled, }: BaseProps & { - customBackgroundColor?: ColorValue; disabled?: boolean; selectedColor?: ColorValue; }) => { @@ -164,12 +135,6 @@ export const getChipColors = ({ const backgroundColor = getBackgroundColor({ ...baseChipColorProps, - customBackgroundColor, - }); - - const selectedBackgroundColor = getSelectedBackgroundColor({ - ...baseChipColorProps, - customBackgroundColor, }); const contentOpacity = disabled @@ -180,7 +145,6 @@ export const getChipColors = ({ borderColor: getBorderColor({ ...baseChipColorProps, selectedColor, - backgroundColor, }), textColor: getTextColor({ ...baseChipColorProps, @@ -192,6 +156,6 @@ export const getChipColors = ({ }), contentOpacity, backgroundColor, - selectedBackgroundColor, + selectedBackgroundColor: backgroundColor, }; }; diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index a7b082fe89..89832fe658 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { Animated, Platform, StyleSheet } from 'react-native'; -import type { StyleProp, ViewStyle } from 'react-native'; +import { Platform, StyleSheet } from 'react-native'; +import type { StyleProp } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -10,8 +10,9 @@ import DialogIcon from './DialogIcon'; import DialogScrollArea from './DialogScrollArea'; import DialogTitle from './DialogTitle'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { Elevation, ThemeProp } from '../../types'; import Modal from '../Modal'; +import type { SurfaceStyle } from '../Surface'; import type { DialogChildProps } from './utils'; export type Props = { @@ -35,7 +36,7 @@ export type Props = { * Content of the `Dialog`. */ children: React.ReactNode; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; /** * @optional */ @@ -46,7 +47,7 @@ export type Props = { testID?: string; }; -const DIALOG_ELEVATION: number = 24; +const DIALOG_ELEVATION: Elevation = 3; /** * Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. @@ -99,6 +100,7 @@ const Dialog = ({ testID, }: Props) => { const { right, left } = useSafeAreaInsets(); + const theme = useInternalTheme(themeOverrides); const borderRadius = theme.shapes.corner.extraLarge; @@ -110,10 +112,11 @@ const Dialog = ({ dismissableBackButton={dismissableBackButton} onDismiss={onDismiss} visible={visible} + contentBackgroundColor={backgroundColor} + contentBorderRadius={borderRadius} + contentElevation={DIALOG_ELEVATION} contentContainerStyle={[ { - borderRadius, - backgroundColor, marginHorizontal: Math.max(left, right, 26), }, styles.container, @@ -158,7 +161,6 @@ const styles = StyleSheet.create({ * dialog (44 pixel from the top and bottom) it won't be dismissed. */ marginVertical: Platform.OS === 'android' ? 44 : 0, - elevation: DIALOG_ELEVATION, justifyContent: 'flex-start', }, }); diff --git a/src/components/FAB/Extended.tsx b/src/components/FAB/Extended.tsx index 06a67cab1f..ff3c500545 100644 --- a/src/components/FAB/Extended.tsx +++ b/src/components/FAB/Extended.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { StyleSheet, View } from 'react-native'; +import { Platform, StyleSheet, View } from 'react-native'; import type { ColorValue, GestureResponderEvent, @@ -8,14 +8,15 @@ import type { ViewStyle, } from 'react-native'; -import Reanimated, { +import Animated, { + type AnimatedStyle, measure, useAnimatedRef, useAnimatedStyle, useSharedValue, withSpring, } from 'react-native-reanimated'; -import { scheduleOnUI } from 'react-native-worklets'; +import { isUIRuntime, scheduleOnUI } from 'react-native-worklets'; import Shell from './Shell'; import type { Size, Variant } from './tokens'; @@ -101,7 +102,7 @@ export type Props = { * Style for positioning the FAB. The visual treatment (size, shape, color) * is driven by `variant` and `size`. */ - style?: StyleProp; + style?: StyleProp>; /** * TestID used for testing purposes. */ @@ -177,7 +178,7 @@ const Extended = ({ const dimensions = getDimensions({ theme, size }); - const offscreenLabelRef = useAnimatedRef(); + const offscreenLabelRef = useAnimatedRef(); const widthValue = useSharedValue(dimensions.width); const labelOpacity = useSharedValue(expanded ? 1 : 0); @@ -190,15 +191,20 @@ const Extended = ({ iconLabelGap, trailing, } = dimensions; + const targetOpacity = expanded ? 1 : 0; if (reduceMotion) { scheduleOnUI(() => { 'worklet'; - const m = measure(offscreenLabelRef); - const lw = m?.width ?? 0; + + const labelWidth = + Platform.OS === 'web' || isUIRuntime() + ? (measure(offscreenLabelRef)?.width ?? 0) + : 0; + widthValue.value = expanded - ? leading + iconSize + iconLabelGap + lw + trailing + ? leading + iconSize + iconLabelGap + labelWidth + trailing : collapsedWidth; labelOpacity.value = targetOpacity; }); @@ -218,9 +224,15 @@ const Extended = ({ scheduleOnUI(() => { 'worklet'; - const m = measure(offscreenLabelRef); - const lw = m?.width ?? 0; - const expandedWidth = leading + iconSize + iconLabelGap + lw + trailing; + + const labelWidth = + Platform.OS === 'web' || isUIRuntime() + ? (measure(offscreenLabelRef)?.width ?? 0) + : 0; + + const expandedWidth = + leading + iconSize + iconLabelGap + labelWidth + trailing; + widthValue.value = withSpring( expanded ? expandedWidth : collapsedWidth, widthSpring @@ -267,7 +279,7 @@ const Extended = ({ testID={testID} theme={themeOverrides} /> - {label} - + ); }; diff --git a/src/components/FAB/FAB.tsx b/src/components/FAB/FAB.tsx index fa4de1288c..fac6460c6a 100644 --- a/src/components/FAB/FAB.tsx +++ b/src/components/FAB/FAB.tsx @@ -8,6 +8,8 @@ import type { ViewStyle, } from 'react-native'; +import type { AnimatedStyle } from 'react-native-reanimated'; + import Shell from './Shell'; import type { Size, Variant } from './tokens'; import type { ThemeProp } from '../../types'; @@ -73,7 +75,7 @@ export type Props = { * Style for positioning the FAB. The visual treatment (size, shape, color) * is driven by `variant` and `size`. */ - style?: StyleProp; + style?: StyleProp>; /** * TestID used for testing purposes. */ diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index 2d2d60e4cd..b9f6279194 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -8,9 +8,10 @@ import type { ViewStyle, } from 'react-native'; -import Reanimated, { +import Animated, { useAnimatedStyle, useSharedValue, + withSpring, } from 'react-native-reanimated'; import type { SharedValue } from 'react-native-reanimated'; import type { AnimatedStyle } from 'react-native-reanimated'; @@ -24,12 +25,14 @@ import { } from './tokens'; import type { Size, Variant } from './tokens'; import { useFocusRing } from './useFocusRing'; -import { useVisibility } from './useVisibility'; import { getDimensions, resolveColors } from './utils'; import { useInternalTheme } from '../../core/theming'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; +import { toRawSpring } from '../../theme/tokens/sys/motion'; import type { ShapeToken } from '../../theme/utils/shape'; import type { Elevation, ThemeProp } from '../../types'; import type { IconSource } from '../Icon'; +import Surface from '../Surface'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; export type ShellProps = { @@ -159,7 +162,7 @@ export type ShellProps = { * Outer-positioning style. Visual treatment (size, shape, color) comes from * `variant` and `size`. */ - style?: StyleProp; + style?: StyleProp>; /** * TestID used for testing purposes. */ @@ -174,7 +177,7 @@ export type ShellProps = { /** * Internal shell used by every FAB-flavored component (regular, Extended, * morphing menu trigger). Owns the outer container, ripple, clip, and the - * visibility animation (scale + alpha + shadow). Consumers that need to + * visibility animation (scale + alpha). Consumers that need to * animate the outer's width/height/borderRadius pass shared values; the * static size-driven defaults are used otherwise. * @@ -225,11 +228,30 @@ const Shell = ({ [theme, variant, containerColor, contentColor] ); - const { scale, alpha, shadowStyle } = useVisibility({ - visible, - theme, - elevation, - }); + const reduceMotion = useReduceMotion(); + + const scale = useSharedValue(visible ? 1 : 0); + const alpha = useSharedValue(visible ? 1 : 0); + + React.useEffect(() => { + const target = visible ? 1 : 0; + + if (reduceMotion) { + scale.value = target; + alpha.value = target; + return; + } + + scale.value = withSpring( + target, + toRawSpring(theme.motion.spring.fast.spatial) + ); + + alpha.value = withSpring( + target, + toRawSpring(theme.motion.spring.fast.effects) + ); + }, [visible, theme, reduceMotion, scale, alpha]); // Fallback shared values track the static size-driven dimensions. Consumers // that don't supply their own animated shared values get these. Keeping @@ -238,6 +260,7 @@ const Shell = ({ const fallbackWidth = useSharedValue(dimensions.width); const fallbackHeight = useSharedValue(dimensions.height); const fallbackBorderRadius = useSharedValue(dimensions.borderRadius); + React.useEffect(() => { fallbackWidth.value = dimensions.width; fallbackHeight.value = dimensions.height; @@ -262,10 +285,8 @@ const Shell = ({ opacity: alpha.value, width: width.value, height: height.value, - borderRadius: borderRadius.value, - backgroundColor: containerBg, }), - [width, height, borderRadius, containerBg] + [width, height] ); const clipStyle = useAnimatedStyle( @@ -277,6 +298,7 @@ const Shell = ({ ); const { focusedSV, onFocus, onBlur } = useFocusRing(); + const focusRingStyle = useAnimatedStyle( () => ({ opacity: focusedSV.value ? 1 : 0, @@ -286,18 +308,21 @@ const Shell = ({ ); return ( - - + {overlay} )} - - + - + ); }; diff --git a/src/components/FAB/useVisibility.ts b/src/components/FAB/useVisibility.ts deleted file mode 100644 index f6c4b2e645..0000000000 --- a/src/components/FAB/useVisibility.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as React from 'react'; -import { Platform, type ViewStyle } from 'react-native'; - -import { - useAnimatedStyle, - useSharedValue, - withSpring, - type AnimatedStyle, - type SharedValue, -} from 'react-native-reanimated'; - -import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; -import { - androidElevationLevels, - shadow, - shadowLayers, -} from '../../theme/tokens/sys/elevation'; -import { toRawSpring } from '../../theme/tokens/sys/motion'; -import type { Elevation, InternalTheme } from '../../types'; - -type UseVisibilityArgs = { - visible: boolean; - theme: InternalTheme; - initialScale?: number; - transformOrigin?: ViewStyle['transformOrigin']; - /** - * Elevation level when shown. Shadow fades in/out with the FAB. - */ - elevation?: Elevation; -}; - -type UseVisibilityResult = { - scale: SharedValue; - alpha: SharedValue; - transformOrigin: ViewStyle['transformOrigin']; - shadowStyle: AnimatedStyle; -}; - -/** - * Animates a FAB in and out: scale + alpha together. - * Reduce-motion: snap to the final value, no animation. - * - * Returns `shadowStyle` too. Put it on the same view as the transform so the - * shadow stays in sync (Android uses `elevation`, iOS uses `shadow*`, Web uses - * `boxShadow` -- the outer container's `opacity: alpha.value` handles the - * visibility fade on Web so the shadow string can be static). - */ -export function useVisibility({ - visible, - theme, - initialScale = 0, - transformOrigin = 'center', - elevation = 0, -}: UseVisibilityArgs): UseVisibilityResult { - const reduceMotion = useReduceMotion(); - const scale = useSharedValue(visible ? 1 : initialScale); - const alpha = useSharedValue(visible ? 1 : 0); - - React.useEffect(() => { - const targetScale = visible ? 1 : initialScale; - const targetAlpha = visible ? 1 : 0; - if (reduceMotion) { - scale.value = targetScale; - alpha.value = targetAlpha; - return; - } - scale.value = withSpring( - targetScale, - toRawSpring(theme.motion.spring.fast.spatial) - ); - alpha.value = withSpring( - targetAlpha, - toRawSpring(theme.motion.spring.fast.effects) - ); - }, [visible, theme, reduceMotion, scale, alpha, initialScale]); - - const restingElevationDp = androidElevationLevels[elevation]; - const shadowOffsetHeight = shadowLayers[0].height[elevation]; - const shadowRadius = shadowLayers[0].shadowRadius[elevation]; - const shadowOpacity = elevation ? shadowLayers[0].shadowOpacity : 0; - const shadowColor = theme.colors.shadow; - - const webShadow = - Platform.OS === 'web' ? shadow(elevation, shadowColor) : null; - - const shadowStyle = useAnimatedStyle(() => { - if (Platform.OS === 'android') { - return { elevation: alpha.value * restingElevationDp }; - } - if (Platform.OS === 'web') { - return webShadow ?? {}; - } - return { - shadowColor, - shadowOpacity: alpha.value * shadowOpacity, - shadowOffset: { width: 0, height: shadowOffsetHeight }, - shadowRadius, - }; - }); - - return { scale, alpha, transformOrigin, shadowStyle }; -} diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 270c9289ac..52cb5fa3b2 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Animated, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import type { ColorValue, GestureResponderEvent, @@ -7,6 +7,8 @@ import type { ViewStyle, } from 'react-native'; +import Animated, { type AnimatedStyle } from 'react-native-reanimated'; + import { getIconButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { $RemoveChildren, ThemeProp } from '../../types'; @@ -14,7 +16,6 @@ import ActivityIndicator from '../ActivityIndicator'; import CrossFadeIcon from '../CrossFadeIcon'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; -import Surface from '../Surface'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; const PADDING = 8; @@ -69,7 +70,7 @@ export type Props = Omit<$RemoveChildren, 'style'> & { * Function to execute on press. */ onPress?: (e: GestureResponderEvent) => void; - style?: Animated.WithAnimatedValue>; + style?: StyleProp>; ref?: React.Ref; /** * TestID used for testing purposes @@ -147,34 +148,26 @@ const IconButton = ({ const buttonSize = size + 2 * PADDING; - const { - borderWidth = mode === 'outlined' && !selected ? 1 : 0, - borderRadius = buttonSize / 2, - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - } = (StyleSheet.flatten(style) || {}) as ViewStyle; - const borderStyles = { - borderWidth, - borderRadius, + borderWidth: mode === 'outlined' && !selected ? 1 : 0, + borderRadius: buttonSize / 2, borderColor, }; return ( - {backgroundOpacity < 1 && ( - + ); }; const styles = StyleSheet.create({ container: { - overflow: 'hidden', margin: 6, - elevation: 0, + overflow: 'hidden', }, touchable: { flexGrow: 1, diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index ec4c256dad..63fb0faf1f 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -1,14 +1,12 @@ import * as React from 'react'; import { - Animated, Dimensions, - Easing, Keyboard, Platform, + Pressable, ScrollView, StyleSheet, View, - Pressable, } from 'react-native'; import type { KeyboardEvent as RNKeyboardEvent } from 'react-native'; import type { @@ -20,7 +18,16 @@ import type { ViewStyle, } from 'react-native'; +import Animated, { + Easing, + ReduceMotion, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { scheduleOnRN } from 'react-native-worklets'; +import useLatestCallback from 'use-latest-callback'; import MenuItem from './MenuItem'; import { useLocale } from '../../core/locale'; @@ -30,6 +37,7 @@ import { addEventListener } from '../../utils/addEventListener'; import { BackHandler } from '../../utils/BackHandler/BackHandler'; import Portal from '../Portal/Portal'; import Surface from '../Surface'; +import type { SurfaceStyle } from '../Surface'; export type Props = { /** @@ -67,7 +75,7 @@ export type Props = { /** * Style of menu's inner content. */ - contentStyle?: Animated.WithAnimatedValue>; + contentStyle?: StyleProp; style?: StyleProp; /** * Elevation level of the menu's content. Shadow styles are calculated based on this value. Default `backgroundColor` is taken from the corresponding `theme.colors.elevation` property. By default equals `2`. @@ -127,8 +135,6 @@ const isCoordinate = (anchor: any): anchor is { x: number; y: number } => typeof anchor?.x === 'number' && typeof anchor?.y === 'number'; -const isBrowser = () => Platform.OS === 'web' && 'document' in global; - /** * Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them. * @@ -193,9 +199,10 @@ const Menu = ({ keyboardShouldPersistTaps, }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); - const { colors: md3Colors } = theme; const insets = useSafeAreaInsets(); + const [rendered, setRendered] = React.useState(visible); const [left, setLeft] = React.useState(0); const [top, setTop] = React.useState(0); @@ -209,13 +216,15 @@ const Menu = ({ height: WINDOW_LAYOUT.height, }); - const opacityAnimationRef = React.useRef(new Animated.Value(0)); - const scaleAnimationRef = React.useRef(new Animated.ValueXY({ x: 0, y: 0 })); + const opacity = useSharedValue(0); + const scaleX = useSharedValue(0); + const scaleY = useSharedValue(0); + const keyboardHeightRef = React.useRef(0); const prevVisible = React.useRef(null); const anchorRef = React.useRef(null); const menuRef = React.useRef(null); - const prevRendered = React.useRef(false); + const isShownRef = React.useRef(false); const keyboardDidShow = React.useCallback((e: RNKeyboardEvent) => { const keyboardHeight = e.endCoordinates.height; @@ -258,7 +267,10 @@ const Menu = ({ const removeListeners = React.useCallback(() => { backHandlerSubscriptionRef.current?.remove(); dimensionsSubscriptionRef.current?.remove(); - isBrowser() && document.removeEventListener('keyup', handleKeypress); + + if (Platform.OS === 'web' && 'document' in global) { + document.removeEventListener('keyup', handleKeypress); + } }, [handleKeypress]); const attachListeners = React.useCallback(() => { @@ -301,6 +313,26 @@ const Menu = ({ [anchor] ); + const handleShowAnimationFinished = useLatestCallback((finished: boolean) => { + if (!finished || !prevVisible.current) { + return; + } + + isShownRef.current = true; + focusFirstDOMNode(menuRef.current); + }); + + const handleHideAnimationFinished = useLatestCallback((finished: boolean) => { + if (!finished || prevVisible.current) { + return; + } + + setMenuLayout({ width: 0, height: 0 }); + setRendered(false); + isShownRef.current = false; + focusFirstDOMNode(anchorRef.current); + }); + const show = React.useCallback(async () => { const windowLayoutResult = Dimensions.get('window'); const [menuLayoutResult, anchorLayoutResult] = await Promise.all([ @@ -308,6 +340,10 @@ const Menu = ({ measureAnchorLayout(), ]); + if (!prevVisible.current) { + return; + } + // When visible is true for first render // native views can be still not rendered and // measureMenuLayout/measureAnchorLayout functions @@ -344,45 +380,54 @@ const Menu = ({ }); attachListeners(); + requestAnimationFrame(() => { + if (!prevVisible.current) { + return; + } + const { animation } = theme; - Animated.parallel([ - Animated.timing(scaleAnimationRef.current, { - toValue: { x: menuLayoutResult.width, y: menuLayoutResult.height }, - duration: ANIMATION_DURATION * animation.scale, - easing: EASING, - useNativeDriver: true, - }), - Animated.timing(opacityAnimationRef.current, { - toValue: 1, - duration: ANIMATION_DURATION * animation.scale, - easing: EASING, - useNativeDriver: true, - }), - ]).start(() => { - focusFirstDOMNode(menuRef.current); - prevRendered.current = true; - }); + + const config = { + duration: ANIMATION_DURATION * animation.scale, + easing: EASING, + reduceMotion: ReduceMotion.Never, + }; + + scaleX.value = withTiming(menuLayoutResult.width, config); + scaleY.value = withTiming(menuLayoutResult.height, config); + + opacity.value = withTiming(1, config, (finished) => + scheduleOnRN(handleShowAnimationFinished, finished ?? false) + ); }); - }, [anchor, attachListeners, measureAnchorLayout, theme]); + }, [ + anchor, + attachListeners, + handleShowAnimationFinished, + measureAnchorLayout, + opacity, + scaleX, + scaleY, + theme, + ]); const hide = React.useCallback(() => { removeListeners(); + isShownRef.current = false; const { animation } = theme; - Animated.timing(opacityAnimationRef.current, { - toValue: 0, - duration: ANIMATION_DURATION * animation.scale, - easing: EASING, - useNativeDriver: true, - }).start(() => { - setMenuLayout({ width: 0, height: 0 }); - setRendered(false); - prevRendered.current = false; - focusFirstDOMNode(anchorRef.current); - }); - }, [removeListeners, theme]); + opacity.value = withTiming( + 0, + { + duration: ANIMATION_DURATION * animation.scale, + easing: EASING, + reduceMotion: ReduceMotion.Never, + }, + (finished) => scheduleOnRN(handleHideAnimationFinished, finished ?? false) + ); + }, [handleHideAnimationFinished, opacity, removeListeners, theme]); const updateVisibility = React.useCallback( async (display: boolean) => { @@ -390,7 +435,7 @@ const Menu = ({ // We need to do the same here so that the ref is up-to-date await Promise.resolve(); - if (display && !prevRendered.current) { + if (display && !isShownRef.current) { await show(); return; } @@ -403,8 +448,6 @@ const Menu = ({ ); React.useEffect(() => { - const opacityAnimation = opacityAnimationRef.current; - const scaleAnimation = scaleAnimationRef.current; keyboardDidShowListenerRef.current = Keyboard.addListener( 'keyboardDidShow', keyboardDidShow @@ -418,26 +461,24 @@ const Menu = ({ removeListeners(); keyboardDidShowListenerRef.current?.remove(); keyboardDidHideListenerRef.current?.remove(); - scaleAnimation.removeAllListeners(); - opacityAnimation?.removeAllListeners(); }; }, [removeListeners, keyboardDidHide, keyboardDidShow]); + if (visible && !rendered) { + // Mount the Portal before attempting to show. + setRendered(true); + } + React.useEffect(() => { if (prevVisible.current !== visible) { prevVisible.current = visible; - if (visible) { - if (!rendered) { - // Mount the Portal before attempting to show. - setRendered(true); - } - } else { + if (!visible) { // Keep the Portal mounted so the hide animation can finish. void updateVisibility(false); } } - }, [visible, rendered, updateVisibility]); + }, [visible, updateVisibility]); React.useEffect(() => { if (rendered && visible) { @@ -452,7 +493,9 @@ const Menu = ({ }); // We need to translate menu while animating scale to imitate transform origin for scale animation - const positionTransforms = []; + let startTranslateX = 0; + let startTranslateY = 0; + let leftTransformation = left; let topTransformation = !isCoordinate(anchorRef.current) && anchorPosition === 'bottom' @@ -461,24 +504,14 @@ const Menu = ({ // Check if menu fits horizontally and if not align it to right. if (left <= windowLayout.width - menuLayout.width - SCREEN_INDENT) { - positionTransforms.push({ - translateX: scaleAnimationRef.current.x.interpolate({ - inputRange: [0, menuLayout.width], - outputRange: [-(menuLayout.width / 2), 0], - }), - }); + startTranslateX = -(menuLayout.width / 2); // Check if menu position has enough space from left side if (leftTransformation < SCREEN_INDENT) { leftTransformation = SCREEN_INDENT; } } else { - positionTransforms.push({ - translateX: scaleAnimationRef.current.x.interpolate({ - inputRange: [0, menuLayout.width], - outputRange: [menuLayout.width / 2, 0], - }), - }); + startTranslateX = menuLayout.width / 2; leftTransformation += anchorLayout.width - menuLayout.width; @@ -559,24 +592,14 @@ const Menu = ({ // And bottom side of the screen has more space than top side topTransformation <= windowLayout.height - topTransformation) ) { - positionTransforms.push({ - translateY: scaleAnimationRef.current.y.interpolate({ - inputRange: [0, menuLayout.height], - outputRange: [-((scrollableMenuHeight || menuLayout.height) / 2), 0], - }), - }); + startTranslateY = -((scrollableMenuHeight || menuLayout.height) / 2); // Check if menu position has enough space from top side if (topTransformation < SCREEN_INDENT) { topTransformation = SCREEN_INDENT; } } else { - positionTransforms.push({ - translateY: scaleAnimationRef.current.y.interpolate({ - inputRange: [0, menuLayout.height], - outputRange: [(scrollableMenuHeight || menuLayout.height) / 2, 0], - }), - }); + startTranslateY = (scrollableMenuHeight || menuLayout.height) / 2; topTransformation += anchorLayout.height - (scrollableMenuHeight || menuLayout.height); @@ -598,25 +621,38 @@ const Menu = ({ } } - const shadowMenuContainerStyle = { - opacity: opacityAnimationRef.current, + const shadowMenuContainerStyle: ViewStyle = scrollableMenuHeight + ? { height: scrollableMenuHeight } + : {}; + + const positionTransformsStyle = useAnimatedStyle(() => { + const scaleXProgress = menuLayout.width + ? scaleX.value / menuLayout.width + : 0; + + const scaleYProgress = menuLayout.height + ? scaleY.value / menuLayout.height + : 0; + + return { + transform: [ + { translateX: startTranslateX * (1 - scaleXProgress) }, + { translateY: startTranslateY * (1 - scaleYProgress) }, + ], + }; + }); + + const shadowMenuAnimationStyle = useAnimatedStyle(() => ({ + opacity: opacity.value, transform: [ { - scaleX: scaleAnimationRef.current.x.interpolate({ - inputRange: [0, menuLayout.width], - outputRange: [0, 1], - }), + scaleX: menuLayout.width ? scaleX.value / menuLayout.width : 0, }, { - scaleY: scaleAnimationRef.current.y.interpolate({ - inputRange: [0, menuLayout.height], - outputRange: [0, 1], - }), + scaleY: menuLayout.height ? scaleY.value / menuLayout.height : 0, }, ], - borderRadius: theme.shapes.corner.extraSmall, - ...(scrollableMenuHeight ? { height: scrollableMenuHeight } : {}), - }; + })); const positionStyle = { top: isCoordinate(anchor) @@ -659,33 +695,36 @@ const Menu = ({ > - {(scrollableMenuHeight && ( - - {children} - - )) || {children}} + + {(scrollableMenuHeight && ( + + {children} + + )) || {children}} + @@ -703,8 +742,13 @@ const styles = StyleSheet.create({ }, shadowMenuContainer: { opacity: 0, + }, + menuContent: { paddingVertical: 8, }, + fill: { + height: '100%', + }, pressableOverlay: { ...Platform.select({ web: { diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 3c66c885b3..22a3f7ba8e 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,17 +1,21 @@ import * as React from 'react'; -import { Animated, Easing, StyleSheet, Pressable, View } from 'react-native'; +import { StyleSheet, Pressable, View } from 'react-native'; import type { StyleProp, ViewStyle } from 'react-native'; +import Animated, { + cubicBezier, + type AnimatedStyle, +} from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import useLatestCallback from 'use-latest-callback'; import Surface from './Surface'; +import type { Props as SurfaceProps, SurfaceStyle } from './Surface'; import { useInternalTheme } from '../core/theming'; import { tokens } from '../theme/tokens'; -import type { ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../types'; import { addEventListener } from '../utils/addEventListener'; import { BackHandler } from '../utils/BackHandler/BackHandler'; -import useAnimatedValue from '../utils/useAnimatedValue'; const scrimAlpha = tokens.md.sys.scrim.alpha; @@ -41,9 +45,25 @@ export type Props = { */ children: React.ReactNode; /** - * Style for the content of the modal + * Style for the content of the modal. + * + * Background color and border radius should be specified via props instead: + * - `contentBackgroundColor` + * - `contentBorderRadius` */ - contentContainerStyle?: Animated.WithAnimatedValue>; + contentContainerStyle?: StyleProp; + /** + * Background color of the modal content. Defaults to transparent. + */ + contentBackgroundColor?: SurfaceProps['backgroundColor']; + /** + * Border radius of the modal content. + */ + contentBorderRadius?: SurfaceProps['borderRadius']; + /** + * Elevation level of the modal content. Defaults to level 1. + */ + contentElevation?: Elevation; /** * Style for the wrapper of the modal. * Use this prop to change the default wrapper style or to override safe area insets with marginTop and marginBottom. @@ -77,12 +97,18 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); * * const showModal = () => setVisible(true); * const hideModal = () => setVisible(false); - * const containerStyle = { backgroundColor: 'white', padding: 20 }; + * + * const containerStyle = { padding: 20 }; * * return ( * * - * + * * Example Modal. Click outside this area to dismiss. * * @@ -104,55 +130,46 @@ function Modal({ onDismiss = () => {}, children, contentContainerStyle, + contentBackgroundColor = 'transparent', + contentBorderRadius, + contentElevation, style, theme: themeOverrides, testID = 'modal', }: Props) { const theme = useInternalTheme(themeOverrides); + const onDismissCallback = useLatestCallback(onDismiss); - const { scale } = theme.animation; + const { top, bottom } = useSafeAreaInsets(); - const opacity = useAnimatedValue(visible ? 1 : 0); + const [visibleInternal, setVisibleInternal] = React.useState(visible); + const [animatedVisible, setAnimatedVisible] = React.useState(visible); - const showModalAnimation = React.useCallback(() => { - Animated.timing(opacity, { - toValue: 1, - duration: scale * DEFAULT_DURATION, - easing: Easing.out(Easing.cubic), - useNativeDriver: true, - }).start(); - }, [opacity, scale]); - - const hideModalAnimation = React.useCallback(() => { - Animated.timing(opacity, { - toValue: 0, - duration: scale * DEFAULT_DURATION, - easing: Easing.out(Easing.cubic), - useNativeDriver: true, - }).start(({ finished }) => { - if (!finished) { - return; - } + if (visible && !visibleInternal) { + setVisibleInternal(true); + } - setVisibleInternal(false); - }); - }, [opacity, scale]); + const { scale } = theme.animation; React.useEffect(() => { - if (visibleInternal === visible) { - return; - } + const timeout = setTimeout(() => setAnimatedVisible(visible), 0); - if (!visibleInternal && visible) { - setVisibleInternal(true); - return showModalAnimation(); - } + return () => clearTimeout(timeout); + }, [visible]); - if (visibleInternal && !visible) { - return hideModalAnimation(); + React.useEffect(() => { + if (visible || !visibleInternal) { + return undefined; } - }, [visible, showModalAnimation, hideModalAnimation, visibleInternal]); + + const timeout = setTimeout( + () => setVisibleInternal(false), + scale * DEFAULT_DURATION + ); + + return () => clearTimeout(timeout); + }, [scale, visible, visibleInternal]); React.useEffect(() => { if (!visible) { @@ -172,10 +189,33 @@ function Modal({ 'hardwareBackPress', onHardwareBackPress ); + return () => subscription.remove(); }, [dismissable, dismissableBackButton, onDismissCallback, visible]); - if (!visibleInternal) { + const transitionTimingFunction = cubicBezier(1 / 3, 1, 2 / 3, 1); + + const backdropTransitionStyle: AnimatedStyle = { + transitionDuration: scale * DEFAULT_DURATION, + transitionProperty: 'opacity', + transitionTimingFunction, + }; + + const contentTransitionStyle: AnimatedStyle = { + transitionProperty: 'opacity', + transitionTimingFunction, + }; + + const backdropStyle: AnimatedStyle = { + backgroundColor: theme.colors.scrim, + opacity: animatedVisible ? scrimAlpha : 0, + }; + + const contentStyle: AnimatedStyle = { + opacity: animatedVisible ? 1 : 0, + }; + + if (!visible && !visibleInternal) { return null; } @@ -194,16 +234,7 @@ function Modal({ disabled={!dismissable} onPress={dismissable ? onDismissCallback : undefined} importantForAccessibility="no" - style={[ - styles.backdrop, - { - backgroundColor: theme.colors.scrim, - opacity: opacity.interpolate({ - inputRange: [0, 1], - outputRange: [0, scrimAlpha], - }), - }, - ]} + style={[styles.backdrop, backdropStyle, backdropTransitionStyle]} testID={`${testID}-backdrop`} /> {children} @@ -238,9 +277,7 @@ const styles = StyleSheet.create({ ...StyleSheet.absoluteFill, justifyContent: 'center', }, - // eslint-disable-next-line react-native/no-color-literals content: { - backgroundColor: 'transparent', justifyContent: 'center', }, }); diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index b0819b5710..8d7e038c31 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; -import { Animated, Platform, StyleSheet, TextInput, View } from 'react-native'; +import { Platform, StyleSheet, TextInput, View } from 'react-native'; import type { ColorValue, GestureResponderEvent, StyleProp, TextInputProps, TextStyle, - ViewStyle, } from 'react-native'; import ActivityIndicator from './ActivityIndicator'; @@ -15,10 +14,11 @@ import type { IconSource } from './Icon'; import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; import Surface from './Surface'; +import type { SurfaceStyle } from './Surface'; import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import { cornerNone } from '../theme/tokens/sys/shape'; -import type { ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../types'; interface Style { marginRight: number; @@ -111,12 +111,12 @@ export type Props = TextInputProps & { * @supported Available in v5.x with theme version 3 * Changes Searchbar shadow and background on iOS and Android. */ - elevation?: 0 | 1 | 2 | 3 | 4 | 5 | Animated.Value; + elevation?: Elevation; /** * Set style of the TextInput component inside the searchbar */ inputStyle?: StyleProp; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; /** * Custom flag for replacing clear button with activity indicator. */ @@ -188,8 +188,10 @@ const Searchbar = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); const { colors, fonts } = theme; + const root = React.useRef(null); React.useImperativeHandle(ref, () => ({ @@ -230,18 +232,11 @@ const Searchbar = ({ return ( , 'mode'> & { +export type Props = Omit & { /** * Whether the Snackbar is currently visible. */ @@ -58,7 +68,7 @@ export type Props = $Omit, 'mode'> & { * @supported Available in v5.x with theme version 3 * Changes Snackbar shadow and background on iOS and Android. */ - elevation?: 0 | 1 | 2 | 3 | 4 | 5 | Animated.Value; + elevation?: Elevation; /** * Specifies the largest possible scale a text font can reach. */ @@ -71,7 +81,7 @@ export type Props = $Omit, 'mode'> & { * Style for the content of the snackbar */ contentStyle?: StyleProp; - style?: Animated.WithAnimatedValue>; + style?: StyleProp; ref?: React.RefObject; /** * @optional @@ -152,82 +162,83 @@ const Snackbar = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); + const { bottom, right, left } = useSafeAreaInsets(); - const { current: opacity } = React.useRef( - new Animated.Value(0.0) - ); + const opacity = useSharedValue(0); + const hideTimeout = React.useRef | undefined>( undefined ); + const isMounted = React.useRef(true); const [hidden, setHidden] = React.useState(!visible); const { scale } = theme.animation; - const animateShow = useLatestCallback(() => { - if (hideTimeout.current) clearTimeout(hideTimeout.current); - - Animated.timing(opacity, { - toValue: 1, - duration: 200 * scale, - easing: Easing.out(Easing.ease), - useNativeDriver: true, - }).start(({ finished }) => { - if (finished) { - const isInfinity = - duration === Number.POSITIVE_INFINITY || - duration === Number.NEGATIVE_INFINITY; - - if (!isInfinity) { - hideTimeout.current = setTimeout(onDismiss, duration); - } - } - }); - }); - - const handleOnVisible = useLatestCallback(() => { - // show + if (visible && hidden) { setHidden(false); + } + + const handleShowAnimationFinished = useLatestCallback((finished: boolean) => { + if (!finished || !visible || !isMounted.current) { + return; + } + + const isInfinity = + duration === Number.POSITIVE_INFINITY || + duration === Number.NEGATIVE_INFINITY; + + if (!isInfinity) { + hideTimeout.current = setTimeout(onDismiss, duration); + } }); - const handleOnHidden = useLatestCallback(() => { - // hide + React.useEffect(() => { if (hideTimeout.current) { clearTimeout(hideTimeout.current); + hideTimeout.current = undefined; } - Animated.timing(opacity, { - toValue: 0, - duration: 100 * scale, - useNativeDriver: true, - }).start(({ finished }) => { - if (finished) { - setHidden(true); + opacity.value = withTiming( + visible ? 1 : 0, + { + duration: (visible ? 200 : 100) * scale, + easing: visible ? Easing.out(Easing.ease) : Easing.inOut(Easing.ease), + reduceMotion: ReduceMotion.Never, + }, + (finished) => { + if (visible) { + scheduleOnRN(handleShowAnimationFinished, finished ?? false); + } else if (finished) { + scheduleOnRN(setHidden, true); + } } - }); - }); + ); + }, [handleShowAnimationFinished, opacity, scale, visible]); React.useEffect(() => { - if (!hidden) { - animateShow(); - } - }, [animateShow, hidden]); + isMounted.current = true; - React.useEffect(() => { return () => { - if (hideTimeout.current) clearTimeout(hideTimeout.current); + isMounted.current = false; + + if (hideTimeout.current) { + clearTimeout(hideTimeout.current); + } }; }, []); - React.useLayoutEffect(() => { - if (visible) { - handleOnVisible(); - } else { - handleOnHidden(); - } - }, [visible, handleOnVisible, handleOnHidden]); + const animatedStyle = useAnimatedStyle(() => ({ + opacity: opacity.value, + transform: [ + { + scale: visible ? interpolate(opacity.value, [0, 1], [0.9, 1]) : 1, + }, + ], + })); const { colors } = theme; @@ -255,26 +266,21 @@ const Snackbar = ({ paddingHorizontal: Math.max(left, right), }; - const renderChildrenWithWrapper = () => { - if (typeof children === 'string') { - return ( - - {children} - - ); - } - - return ( + const content = + typeof children === 'string' ? ( + + {children} + + ) : ( {/* View is added to allow multiple lines support for Text component as children */} {children} ); - }; return ( - {renderChildrenWithWrapper()} + {content} {(action || isIconButton) && ( {action ? ( @@ -385,8 +373,8 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'space-between', margin: 8, - borderRadius: 4, minHeight: 48, + pointerEvents: 'box-none', }, content: { marginHorizontal: 16, diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index b0b0f0c4aa..fa84f49051 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -1,205 +1,147 @@ import * as React from 'react'; -import { Animated, Platform, StyleSheet, View } from 'react-native'; -import type { - ColorValue, - ShadowStyleIOS, - StyleProp, - ViewProps, - ViewStyle, -} from 'react-native'; +import { Platform, StyleSheet, View } from 'react-native'; +import type { ColorValue, StyleProp, ViewProps, ViewStyle } from 'react-native'; + +import Animated, { + cubicBezier, + isSharedValue, + type AnimatedStyle, + useAnimatedStyle, +} from 'react-native-reanimated'; import { useInternalTheme } from '../core/theming'; -import { - androidElevationLevels, - elevationInputRange, - shadow, - shadowLayers, -} from '../theme/tokens/sys/elevation'; +import { androidElevationLevels, shadow } from '../theme/tokens/sys/elevation'; import type { Elevation, ThemeProp } from '../types'; -import { isAnimatedValue } from '../utils/animations'; -import { splitStyles } from '../utils/splitStyles'; -type SurfaceElevation = Elevation | Animated.Value; +type AnimatedStyleProp = Extract< + AnimatedStyle>>, + Record +>[Key]; + +type BorderRadius = AnimatedStyleProp<'borderRadius'>; -export type Props = Omit & { +type SurfaceVisualProps = { + /** + * Background color of the Surface. Overrides the color derived from + * `elevation`. + */ + backgroundColor?: ColorValue; + /** + * Radius of every corner of the Surface. + */ + borderRadius?: BorderRadius; + /** + * Radius of the bottom-end corner of the Surface. + */ + borderBottomEndRadius?: BorderRadius; + /** + * Radius of the bottom-left corner of the Surface. + */ + borderBottomLeftRadius?: BorderRadius; /** - * Content of the `Surface`. + * Radius of the bottom-right corner of the Surface. */ - children: React.ReactNode; - style?: Animated.WithAnimatedValue>; + borderBottomRightRadius?: BorderRadius; /** - * @supported Available in v5.x with theme version 3 - * Changes shadows and background on iOS and Android. - * Used to create UI hierarchy between components. - * - * Note: If `mode` is set to `flat`, Surface doesn't have a shadow. - * - * Note: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`. - * It's no longer supported with theme version 3 and you should use `elevation` property instead. + * Radius of the bottom-start corner of the Surface. */ - elevation?: SurfaceElevation; + borderBottomStartRadius?: BorderRadius; /** - * @supported Available in v5.x with theme version 3 - * Mode of the Surface. - * - `elevated` - Surface with a shadow and background color corresponding to set `elevation` value. - * - `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value. + * Radius of the end-end corner of the Surface. */ - mode?: 'flat' | 'elevated'; + borderEndEndRadius?: BorderRadius; /** - * @optional + * Radius of the end-start corner of the Surface. */ - theme?: ThemeProp; + borderEndStartRadius?: BorderRadius; /** - * TestID used for testing purposes + * Radius of the start-end corner of the Surface. */ - testID?: string; - ref?: React.Ref; + borderStartEndRadius?: BorderRadius; /** - * @internal + * Radius of the start-start corner of the Surface. */ - container?: boolean; + borderStartStartRadius?: BorderRadius; + /** + * Radius of the top-end corner of the Surface. + */ + borderTopEndRadius?: BorderRadius; + /** + * Radius of the top-left corner of the Surface. + */ + borderTopLeftRadius?: BorderRadius; + /** + * Radius of the top-right corner of the Surface. + */ + borderTopRightRadius?: BorderRadius; + /** + * Radius of the top-start corner of the Surface. + */ + borderTopStartRadius?: BorderRadius; + /** + * Corner curve of the Surface on iOS. + */ + borderCurve?: ViewStyle['borderCurve']; }; -const outerLayerStyleProperties: (keyof ViewStyle)[] = [ - 'position', - 'alignSelf', - 'top', - 'right', - 'bottom', - 'left', - 'start', - 'end', - 'flex', - 'flexShrink', - 'flexGrow', - 'width', - 'height', - 'transform', - 'opacity', -]; +export type SurfaceStyle = AnimatedStyle< + Omit +>; -function getStyleForShadowLayer( - elevation: SurfaceElevation, - layer: 0 | 1, - shadowColor: ColorValue -): Animated.WithAnimatedValue { - if (isAnimatedValue(elevation)) { - return { - shadowColor, - shadowOpacity: elevation.interpolate({ - inputRange: [0, 1], - outputRange: [0, shadowLayers[layer].shadowOpacity], - extrapolate: 'clamp', - }), - shadowOffset: { - width: 0, - height: elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: shadowLayers[layer].height, - }), - }, - shadowRadius: elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: shadowLayers[layer].shadowRadius, - }), - }; - } - - return { - shadowColor, - shadowOpacity: elevation ? shadowLayers[layer].shadowOpacity : 0, - shadowOffset: { - width: 0, - height: shadowLayers[layer].height[elevation], - }, - shadowRadius: shadowLayers[layer].shadowRadius[elevation], +export type Props = Omit & + SurfaceVisualProps & { + /** + * Duration of the background, elevation, and shadow transitions in + * milliseconds. + */ + transitionDuration?: number; + /** + * Style of the Surface. + * + * This doesn't support all View style properties: + * - Background color and border radius should be specified via props instead. + * - `overflow: 'hidden'` is not supported with `elevation` as it can clip the shadow. + * To achieve the same effect, wrap the content in a child View with the overflow style. + */ + style?: StyleProp; + /** + * @supported Available in v5.x with theme version 3 + * Changes shadows and background on iOS and Android. + * Used to create UI hierarchy between components. + * + * Note: If `mode` is set to `flat`, Surface doesn't have a shadow. + * + * Note: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`. + * It's no longer supported with theme version 3 and you should use `elevation` property instead. + */ + elevation?: Elevation; + /** + * @supported Available in v5.x with theme version 3 + * Mode of the Surface. + * - `elevated` - Surface with a shadow and background color corresponding to set `elevation` value. + * - `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value. + */ + mode?: 'flat' | 'elevated'; + /** + * @optional + */ + theme?: ThemeProp; + /** + * Content of the `Surface`. + */ + children: React.ReactNode; + /** + * TestID used for testing purposes + */ + testID?: string; + ref?: React.Ref; }; -} - -type SurfaceIOSProps = Omit & { - elevation: SurfaceElevation; - backgroundColor?: - | ColorValue - | Animated.AnimatedInterpolation; - shadowColor: ColorValue; -}; - -const SurfaceIOS = ({ - elevation, - style, - backgroundColor, - shadowColor, - testID, - children, - mode = 'elevated', - container, - ref, - ...props -}: SurfaceIOSProps) => { - const [outerLayerViewStyles, innerLayerViewStyles] = React.useMemo(() => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const flattenedStyles = (StyleSheet.flatten(style) || {}) as ViewStyle; - - const [filteredStyles, outerLayerStyles, borderRadiusStyles] = splitStyles( - flattenedStyles, - (style) => - outerLayerStyleProperties.includes(style) || style.startsWith('margin'), - (style) => style.startsWith('border') && style.endsWith('Radius') - ); - - if ( - process.env.NODE_ENV !== 'production' && - filteredStyles.overflow === 'hidden' && - elevation !== 0 - ) { - console.warn( - 'When setting overflow to hidden on Surface the shadow will not be displayed correctly. Wrap the content of your component in a separate View with the overflow style.' - ); - } - - const bgColor = flattenedStyles.backgroundColor || backgroundColor; - - const isElevated = mode === 'elevated'; - - const outerLayerViewStyles = { - ...(isElevated && getStyleForShadowLayer(elevation, 0, shadowColor)), - ...outerLayerStyles, - ...borderRadiusStyles, - backgroundColor: bgColor, - }; - - const innerLayerViewStyles = { - ...(isElevated && getStyleForShadowLayer(elevation, 1, shadowColor)), - ...filteredStyles, - ...borderRadiusStyles, - flex: - flattenedStyles.height || (!container && flattenedStyles.flex) - ? 1 - : undefined, - backgroundColor: bgColor, - }; - - return [outerLayerViewStyles, innerLayerViewStyles]; - }, [style, elevation, backgroundColor, shadowColor, mode, container]); - - return ( - - - {children} - - - ); -}; /** * Surface is a basic container that can give depth to an element with elevation shadow. - * On dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface. - * See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. - * Overlay and shadow can be applied by specifying the `elevation` property both on Android and iOS. + * + * On Android, Surface uses the native `elevation` style, + * and falls back to shadows that approximate the elevation on other platforms. * * ## Usage * ```js @@ -208,7 +150,7 @@ const SurfaceIOS = ({ * import { StyleSheet } from 'react-native'; * * const MyComponent = () => ( - * + * * Surface * * ); @@ -217,9 +159,9 @@ const SurfaceIOS = ({ * * const styles = StyleSheet.create({ * surface: { - * padding: 8, * height: 80, * width: 80, + * padding: 8, * alignItems: 'center', * justifyContent: 'center', * }, @@ -231,44 +173,92 @@ const Surface = ({ children, theme: overridenTheme, style, - testID = 'surface', + backgroundColor: customBackgroundColor, + borderRadius, + borderBottomEndRadius, + borderBottomLeftRadius, + borderBottomRightRadius, + borderBottomStartRadius, + borderEndEndRadius, + borderEndStartRadius, + borderStartEndRadius, + borderStartStartRadius, + borderTopEndRadius, + borderTopLeftRadius, + borderTopRightRadius, + borderTopStartRadius, + borderCurve = 'continuous', + testID, mode = 'elevated', + transitionDuration: customTransitionDuration, ref, - ...props + ...rest }: Props) => { const theme = useInternalTheme(overridenTheme); const { colors } = theme; - const backgroundColor = (() => { - if (isAnimatedValue(elevation)) { - return elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: elevationInputRange.map((elevation) => { - return colors.elevation?.[`level${elevation}`]; - }), - }); - } + const backgroundColor = + customBackgroundColor ?? colors.elevation?.[`level${elevation}`]; - return colors.elevation?.[`level${elevation}`]; - })(); + const backgroundStyle = { backgroundColor }; + + const shapeProps = { + borderRadius, + borderBottomEndRadius, + borderBottomLeftRadius, + borderBottomRightRadius, + borderBottomStartRadius, + borderEndEndRadius, + borderEndStartRadius, + borderStartEndRadius, + borderStartStartRadius, + borderTopEndRadius, + borderTopLeftRadius, + borderTopRightRadius, + borderTopStartRadius, + borderCurve, + }; + + // Reanimated styles can't be shared between different views + // So we need to create two separate styles for the surface and shadow layers + const visualStyle = useSurfaceVisualStyle(shapeProps); + const shadowVisualStyle = useSurfaceVisualStyle(shapeProps); const isElevated = mode === 'elevated'; + const transitionDuration = + customTransitionDuration ?? + theme.motion.duration.short3 * theme.animation.scale; + const transitionDurationStyle: AnimatedStyle = { + transitionDuration, + }; + const transitionTimingFunction = cubicBezier(...theme.motion.easing.standard); + const transitionProperty = + // FIXME: Reanimated can't animate PlatformColor and DynamicColorIOS + typeof backgroundColor === 'string' ? ['backgroundColor' as const] : []; + if (Platform.OS === 'web') { - const { pointerEvents = 'auto' } = props; + const [elevationShadow] = shadow(elevation, theme.colors.shadow); + + const transitionStyle: AnimatedStyle = { + transitionTimingFunction, + transitionProperty: [...transitionProperty, 'boxShadow'], + }; + return ( {children} @@ -277,40 +267,25 @@ const Surface = ({ } if (Platform.OS === 'android') { - const getElevationAndroid = () => { - if (isAnimatedValue(elevation)) { - return elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: androidElevationLevels, - }); - } + const elevationAndroid = androidElevationLevels[elevation]; - return androidElevationLevels[elevation]; + const transitionStyle: AnimatedStyle = { + transitionTimingFunction, + transitionProperty: [...transitionProperty, 'elevation'], }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const { margin, padding, transform, borderRadius } = (StyleSheet.flatten( - style - ) || {}) as ViewStyle; - - const outerLayerStyles = { margin, padding, transform, borderRadius }; - const sharedStyle = [{ backgroundColor }, style]; - return ( {children} @@ -318,20 +293,97 @@ const Surface = ({ ); } + const [spotShadow, ambientShadow] = shadow(elevation, theme.colors.shadow); + + const transitionStyle: AnimatedStyle = { + transitionTimingFunction, + transitionProperty: [ + ...transitionProperty, + 'shadowOpacity', + 'shadowOffset', + 'shadowRadius', + ], + }; + return ( - + {isElevated ? ( + + ) : null} {children} - + ); }; +const useSurfaceVisualStyle = ({ + borderRadius, + borderBottomEndRadius, + borderBottomLeftRadius, + borderBottomRightRadius, + borderBottomStartRadius, + borderEndEndRadius, + borderEndStartRadius, + borderStartEndRadius, + borderStartStartRadius, + borderTopEndRadius, + borderTopLeftRadius, + borderTopRightRadius, + borderTopStartRadius, + borderCurve, +}: Omit) => + useAnimatedStyle(() => + Object.fromEntries( + Object.entries({ + borderRadius, + borderBottomEndRadius, + borderBottomLeftRadius, + borderBottomRightRadius, + borderBottomStartRadius, + borderEndEndRadius, + borderEndStartRadius, + borderStartEndRadius, + borderStartStartRadius, + borderTopEndRadius, + borderTopLeftRadius, + borderTopRightRadius, + borderTopStartRadius, + borderCurve, + }).map(([property, value]) => [ + property, + isSharedValue(value) ? value.value : value, + ]) + ) + ); + +const styles = StyleSheet.create({ + container: { + pointerEvents: 'auto', + }, + shadow: { + pointerEvents: 'none', + }, +}); + export default Surface; diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index 22598c16b2..50bb50547e 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -1,7 +1,9 @@ import * as React from 'react'; -import { StyleSheet, View, Animated } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; +import type { AnimatedStyle } from 'react-native-reanimated'; + import { ToggleButtonGroupContext } from './ToggleButtonGroup'; import { getToggleButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; @@ -42,7 +44,7 @@ export type Props = { * Status of button. */ status?: 'checked' | 'unchecked'; - style?: Animated.WithAnimatedValue>; + style?: StyleProp>; /** * @optional */ diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 27cd573f9d..5bba9563f4 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -1,7 +1,4 @@ -import { Animated } from 'react-native'; - -import { describe, expect, it, jest } from '@jest/globals'; -import { act } from '@testing-library/react-native'; +import { describe, expect, it } from '@jest/globals'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { getTheme } from '../../../core/theming'; @@ -280,176 +277,53 @@ describe('AppbarContent', () => { }); describe('getAppbarColors', () => { - const elevation = 4; + const elevated = true; const customBackground = 'aquamarine'; it('should return custom color no matter what is the theme version', () => { expect( - getAppbarBackgroundColor(getTheme(), elevation, customBackground) + getAppbarBackgroundColor(getTheme(), elevated, customBackground) ).toBe(customBackground); }); - it('should return v3 light color if theme version is 3', () => { - expect(getAppbarBackgroundColor(getTheme(), elevation)).toBe( - tokens.md.ref.palette.neutral98 + it('returns the light surface container color for an elevated appbar', () => { + expect(getAppbarBackgroundColor(getTheme(), elevated)).toBe( + tokens.md.ref.palette.neutral94 ); }); - it('should return v3 dark color if theme version is 3', () => { - expect(getAppbarBackgroundColor(getTheme(true), elevation)).toBe( - tokens.md.ref.palette.neutral6 + it('returns the dark surface container color for an elevated appbar', () => { + expect(getAppbarBackgroundColor(getTheme(true), elevated)).toBe( + tokens.md.ref.palette.neutral12 ); }); }); -describe('animated value changes correctly', () => { - it('appbar animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - - +describe('getAppbarBorders', () => { + const borderStyles = { + borderRadius: 1, + borderBottomEndRadius: 2, + borderBottomStartRadius: 3, + borderEndEndRadius: 4, + borderEndStartRadius: 5, + borderStartEndRadius: 6, + borderStartStartRadius: 7, + borderTopEndRadius: 8, + borderTopStartRadius: 9, + borderTopLeftRadius: 10, + borderTopRightRadius: 11, + borderBottomRightRadius: 12, + borderBottomLeftRadius: 13, + borderCurve: 'continuous' as const, + }; + + it('returns every border style and excludes unrelated styles', () => { + expect(getAppbarBorders({ ...borderStyles, height: 60, top: 13 })).toEqual( + borderStyles ); - expect(screen.getByTestId('appbar-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - - expect(screen.getByTestId('appbar-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); - }); - - it('action animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - - - ); - expect( - screen.getByTestId('appbar-action-container-outer-layer') - ).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - - expect( - screen.getByTestId('appbar-action-container-outer-layer') - ).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); }); - it('back action animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - - - ); - expect( - screen.getByTestId('appbar-back-action-container-outer-layer') - ).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - - expect( - screen.getByTestId('appbar-back-action-container-outer-layer') - ).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); - }); - - it('header animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - - {null} - - - ); - expect(screen.getByTestId('appbar-header-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - - expect(screen.getByTestId('appbar-header-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); - }); - - it('header bottom border radius applied correctly', async () => { - const style = { borderBottomLeftRadius: 16, borderBottomRightRadius: 16 }; - - await render( - - - {null} - - - ); - expect(screen.getByTestId('appbar-header-root-layer')).toHaveStyle(style); - }); - - describe('getAppbarBorders', () => { - const style = { borderRadius: 10, height: 60, top: 13 }; - - it('should return only border radius styles', () => { - expect(getAppbarBorders(style)).toEqual({ borderRadius: 10 }); - }); - - it('should return empty object if no borders are passed', () => { - const style = { height: 60, top: 13 }; - expect(getAppbarBorders(style)).toEqual({}); - }); + it('returns an empty object when no border styles are passed', () => { + expect(getAppbarBorders({ height: 60, top: 13 })).toEqual({}); }); }); diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index a5d9d95766..30aaf4e66d 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -4,33 +4,43 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` - + - + - + + + - - - - - magnify - - - - + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + magnify + - + + + + + - - - - - close - - - - + ], + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + @@ -420,33 +488,43 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A - - + + + - - + + - - - - - + } + /> - + + - - Examples - - - + Examples + + + + - - - - menu - - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + menu + diff --git a/src/components/__tests__/Banner.test.tsx b/src/components/__tests__/Banner.test.tsx index 80bd3e9017..5cbd07b387 100644 --- a/src/components/__tests__/Banner.test.tsx +++ b/src/components/__tests__/Banner.test.tsx @@ -1,4 +1,4 @@ -import { Animated, Image } from 'react-native'; +import { Image } from 'react-native'; import { afterAll, @@ -11,7 +11,7 @@ import { } from '@jest/globals'; import { act } from '@testing-library/react-native'; -import { render, screen } from '../../test-utils'; +import { render } from '../../test-utils'; import Banner from '../Banner'; it('renders hidden banner, without action buttons and without image', async () => { @@ -356,34 +356,4 @@ describe('animations', () => { expect(nextHideCallback).toHaveBeenCalledTimes(1); }); }); - - it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - Banner - - ); - expect(screen.getByTestId('banner-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.getByTestId('banner-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); - }); }); diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index 21e495742a..e4a275921f 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -1,4 +1,12 @@ -import { Animated, Easing, Platform, StyleSheet, Text } from 'react-native'; +import { + Animated, + Easing, + Keyboard, + Platform, + StyleSheet, + Text, +} from 'react-native'; +import type { KeyboardEvent } from 'react-native'; import { describe, expect, it, jest } from '@jest/globals'; import { act, fireEvent, userEvent } from '@testing-library/react-native'; @@ -431,6 +439,60 @@ it('renders custom background color passed to barStyle property', async () => { expect(wrapper).toHaveStyle({ backgroundColor: Palette.error60 }); }); +it('uses the rendered bar height when hiding it for the keyboard', async () => { + let handleKeyboardShow: ((event: KeyboardEvent) => void) | undefined; + const addKeyboardListener = Keyboard.addListener.bind(Keyboard); + const keyboardListenerSpy = jest + .spyOn(Keyboard, 'addListener') + .mockImplementation((event, listener) => { + if (event.endsWith('Show')) { + handleKeyboardShow = listener; + } + + return addKeyboardListener(event, listener); + }); + + await render( + + ); + + const navigation = screen.getByTestId('bottom-navigation'); + + await fireEvent(navigation, 'layout', { + nativeEvent: { + layout: { height: 72, width: 360 }, + }, + }); + + await act(() => { + handleKeyboardShow?.({ + duration: 0, + easing: 'keyboard', + endCoordinates: { + screenX: 0, + screenY: 500, + width: 360, + height: 300, + }, + }); + jest.runAllTimers(); + }); + + expect(navigation).toHaveStyle({ + height: 96, + position: 'absolute', + transform: [{ translateY: 72 }], + }); + + keyboardListenerSpy.mockRestore(); +}); + it('renders a single tab', async () => { await render( { ); }); -it('barStyle animated value changes correctly', async () => { +it('supports animated styles in bar', async () => { const value = new Animated.Value(1); await render( - {}} - renderScene={renderScene} - testID={'bottom-navigation'} - barStyle={[{ transform: [{ scale: value }] }]} + onTabPress={jest.fn()} + testID="bottom-navigation" + style={[{ transform: [{ scale: value }] }]} /> ); - expect(screen.getByTestId('bottom-navigation-bar-outer-layer')).toHaveStyle({ + + expect(screen.getByTestId('bottom-navigation')).toHaveStyle({ transform: [{ scale: 1 }], }); @@ -614,7 +676,8 @@ it('barStyle animated value changes correctly', async () => { await act(() => { jest.advanceTimersByTime(200); }); - expect(screen.getByTestId('bottom-navigation-bar-outer-layer')).toHaveStyle({ + + expect(screen.getByTestId('bottom-navigation')).toHaveStyle({ transform: [{ scale: 1.5 }], }); }); diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 4da466837a..be3d573d29 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -1,7 +1,7 @@ -import { Animated, StyleSheet } from 'react-native'; +import { StyleSheet } from 'react-native'; import { describe, expect, it, jest } from '@jest/globals'; -import { act, userEvent } from '@testing-library/react-native'; +import { userEvent } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; @@ -16,15 +16,6 @@ const styles = StyleSheet.create({ flexing: { flexDirection: 'row-reverse', }, - customRadius: { - borderTopLeftRadius: 16, - borderTopRightRadius: 0, - borderBottomLeftRadius: 0, - borderBottomRightRadius: 16, - }, - noRadius: { - borderRadius: 0, - }, }); it('renders text button by default', async () => { @@ -155,54 +146,6 @@ it('renders button with an accessibility hint', async () => { expect(tree).toMatchSnapshot(); }); -it('renders button with custom border radius', async () => { - await render( - - ); - - expect(screen.getByTestId('custom-radius-container')).toHaveStyle( - styles.customRadius - ); - expect(screen.getByTestId('custom-radius')).toHaveStyle(styles.customRadius); -}); - -it('renders outlined button with custom border radius', async () => { - await render( - - ); - - expect(screen.getByTestId('custom-radius-container')).toHaveStyle( - styles.customRadius - ); - expect(screen.getByTestId('custom-radius')).toHaveStyle({ - borderTopLeftRadius: 15, // styles.customRadius - 1px outline - borderTopRightRadius: 0, - borderBottomLeftRadius: 0, - borderBottomRightRadius: 15, // styles.customRadius - 1px outline - }); -}); - -it('renders button without border radius', async () => { - await render( - - ); - - expect(screen.getByTestId('custom-radius-container')).toHaveStyle( - styles.noRadius - ); - expect(screen.getByTestId('custom-radius')).toHaveStyle(styles.noRadius); -}); - it('should execute onPressIn', async () => { const onPressInMock = jest.fn(); const onPress = jest.fn(); @@ -709,33 +652,3 @@ describe('getButtonColors - border width', () => { }) ); }); - -it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - ); - expect(screen.getByTestId('button-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('button-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index d9e91d9d73..74637a8a53 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -1,7 +1,6 @@ -import { Animated, StyleSheet, Text } from 'react-native'; +import { Platform, StyleSheet, Text } from 'react-native'; -import { describe, expect, it, jest } from '@jest/globals'; -import { act } from '@testing-library/react-native'; +import { afterEach, describe, expect, it, jest } from '@jest/globals'; import { getTheme } from '../../../core/theming'; import { render, screen } from '../../../test-utils'; @@ -11,9 +10,6 @@ import Card from '../../Card/Card'; import { getCardColors, getCardCoverStyle } from '../../Card/utils'; const styles = StyleSheet.create({ - customBorderRadius: { - borderRadius: 32, - }, customCoverRadius: { borderTopLeftRadius: 4, borderTopRightRadius: 8, @@ -25,6 +21,10 @@ const styles = StyleSheet.create({ }, }); +afterEach(() => { + jest.restoreAllMocks(); +}); + describe('Card', () => { it('renders an outlined card', async () => { const tree = (await render({null})).toJSON(); @@ -32,40 +32,49 @@ describe('Card', () => { expect(tree).toMatchSnapshot(); }); - it('renders an outlined card with custom border radius and color', async () => { + it('renders an outlined card with a custom outline color', async () => { + const testID = 'custom-outline-card'; + await render( {null} ); - expect(screen.getByTestId('card-outline')).toHaveStyle({ - borderRadius: 32, + expect(screen.getByTestId(`${testID}-outline`)).toHaveStyle({ borderColor: 'purple', + borderWidth: 1, }); }); it('renders an outlined card with custom border color', async () => { + const testID = 'custom-border-card'; + await render( {null} ); - expect(screen.getByLabelText('card')).toHaveStyle({ + expect(screen.getByTestId(`${testID}-outline`)).toHaveStyle({ borderColor: Palette.error50, + borderWidth: 1, }); }); - it('renders with a custom theme', async () => { + it('renders with a custom theme background color', async () => { + jest.replaceProperty(Platform, 'OS', 'web'); + await render( { screen.getByTestId('card-actions').props.children[0].props.mode ).toBe('contained'); }); - - it('renders button with custom styles', async () => { - await render( - - - - - - ); - - expect(screen.getByTestId('card-actions-button')).toHaveStyle({ - borderRadius: 32, - }); - }); }); describe('getCardColors - background color', () => { @@ -224,32 +213,3 @@ describe('getCardCoverStyle - border radius', () => { ).toMatchObject({ borderRadius: getTheme().shapes.corner.medium }); }); }); - -it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - {null} - - ); - expect(screen.getByTestId('card-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('card-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); diff --git a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap index d1492cc475..5c39f6bf05 100644 --- a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap +++ b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap @@ -4,27 +4,33 @@ exports[`Card renders an outlined card 1`] = ` - - - + - + "shadowOpacity": 0, + "shadowRadius": 0, + }, + ] + } + /> + + `; diff --git a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap index 54f2e4f7a4..714b65414e 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap @@ -83,6 +83,7 @@ exports[`renders Checkbox with custom testID 1`] = ` } > { }); }); -describe('getChipColor - selected background color', () => { - it('should return custom color, outlined mode', () => { - expect( - getChipColors({ - theme: getTheme(), - customBackgroundColor: 'purple', - isOutlined: true, - }) - ).toMatchObject({ - selectedBackgroundColor: 'purple', - }); - }); - - it('should return custom color, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - selectedBackgroundColor: 'purple', - }); - }); - - it('should return theme color, for theme version 3, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(), - isOutlined: false, - }) - ).toMatchObject({ - selectedBackgroundColor: getTheme().colors.secondaryContainer, - }); - }); -}); - describe('getChipColor - background color', () => { - it('should return custom color', () => { - expect( - getChipColors({ - theme: getTheme(), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - backgroundColor: 'purple', - }); - }); - it('should return theme color, for theme version 3, outlined mode', () => { expect( getChipColors({ @@ -317,18 +265,6 @@ describe('getChipColor - border color', () => { }); }); - it('should return custom color, flat mode', () => { - expect( - getChipColors({ - theme: getTheme(true), - customBackgroundColor: 'purple', - isOutlined: false, - }) - ).toMatchObject({ - borderColor: 'transparent', - }); - }); - it('should return theme color, light mode, outlined mode', () => { expect( getChipColors({ @@ -373,32 +309,3 @@ describe('getChipColor - border color', () => { }); }); }); - -it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - {}} - testID="chip" - style={[{ transform: [{ scale: value }] }]} - > - Example Chip - - ); - expect(screen.getByTestId('chip-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('chip-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx index fd9a21cec9..e8e530484d 100644 --- a/src/components/__tests__/FABExtended.test.tsx +++ b/src/components/__tests__/FABExtended.test.tsx @@ -1,9 +1,30 @@ -import { expect, it, jest } from '@jest/globals'; +import { Platform } from 'react-native'; + +import { afterEach, expect, it, jest } from '@jest/globals'; import { fireEvent, userEvent } from '@testing-library/react-native'; +import * as Reanimated from 'react-native-reanimated'; import { render, screen } from '../../test-utils'; import FAB from '../FAB'; +jest.mock('react-native-reanimated', () => { + const ReanimatedModule = jest.requireActual< + typeof import('react-native-reanimated') + >('react-native-reanimated'); + + return { + __esModule: true, + ...ReanimatedModule, + default: ReanimatedModule.default, + measure: jest.fn(), + }; +}); + +afterEach(() => { + jest.mocked(Reanimated.measure).mockReset(); + jest.restoreAllMocks(); +}); + it('renders extended FAB expanded', async () => { const tree = ( await render() @@ -11,6 +32,33 @@ it('renders extended FAB expanded', async () => { expect(tree).toMatchSnapshot(); }); +it('expands to fit the measured label width', async () => { + jest.replaceProperty(Platform, 'OS', 'web'); + jest.mocked(Reanimated.measure).mockReturnValue({ + x: 0, + y: 0, + width: 80, + height: 20, + pageX: 0, + pageY: 0, + }); + + await render( + + ); + await jest.runAllTimersAsync(); + + expect( + Reanimated.getAnimatedStyle(screen.getByTestId('extended-fab-container')) + ).toMatchObject({ width: 144 }); +}); + it('renders extended FAB collapsed', async () => { const tree = ( await render( diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index b28456c5ce..a8bc540aa9 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -1,7 +1,6 @@ -import { Animated, StyleSheet } from 'react-native'; +import { StyleSheet } from 'react-native'; -import { describe, expect, it, jest } from '@jest/globals'; -import { act } from '@testing-library/react-native'; +import { describe, expect, it } from '@jest/globals'; import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; @@ -318,30 +317,3 @@ describe('getIconButtonColor - border color', () => { }); }); }); - -it('action animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - ); - expect(screen.getByTestId('icon-button-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('icon-button-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index bd14689f70..5fdc76f64d 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -1,4 +1,4 @@ -import { Animated, Dimensions, StyleSheet, View } from 'react-native'; +import { Dimensions, StyleSheet, View } from 'react-native'; import { expect, it, jest } from '@jest/globals'; import { act, screen, waitFor } from '@testing-library/react-native'; @@ -10,13 +10,6 @@ import Button from '../Button/Button'; import Menu from '../Menu/Menu'; import Portal from '../Portal/Portal'; -const styles = StyleSheet.create({ - contentStyle: { - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - }, -}); - it('renders visible menu', async () => { const tree = ( await render( @@ -55,31 +48,12 @@ it('renders not visible menu', async () => { expect(tree).toMatchSnapshot(); }); -it('renders menu with content styles', async () => { - const tree = ( - await render( - - Open menu} - contentStyle={styles.contentStyle} - > - - - - - ) - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - const elevations: Elevation[] = [0, 1, 2, 3, 4, 5]; elevations.forEach((elevation) => it(`renders menu with background color based on elevation value = ${elevation}`, async () => { const theme = getTheme(); + const testID = 'menu-with-elevation'; await render( @@ -88,6 +62,8 @@ elevations.forEach((elevation) => onDismiss={jest.fn()} anchor={} elevation={elevation} + mode="flat" + testID={testID} > @@ -95,13 +71,14 @@ elevations.forEach((elevation) => ); - expect(screen.getByTestId('menu-surface')).toHaveStyle({ + expect(screen.getByTestId(`${testID}-surface`)).toHaveStyle({ backgroundColor: theme.colors.elevation[`level${elevation}`], }); }) ); it('uses the default anchorPosition of top', async () => { + const testID = 'top-positioned-menu'; const dimensionsSpy = jest.spyOn(Dimensions, 'get').mockReturnValue({ width: 400, height: 800, @@ -123,7 +100,7 @@ it('uses the default anchorPosition of top', async () => { Open menu } - contentStyle={styles.contentStyle} + testID={testID} > @@ -145,7 +122,7 @@ it('uses the default anchorPosition of top', async () => { }); await waitFor(() => { - const menu = screen.getByTestId('menu-view'); + const menu = screen.getByTestId(`${testID}-view`); expect(menu).toHaveStyle({ position: 'absolute', left: 100, @@ -158,6 +135,7 @@ it('uses the default anchorPosition of top', async () => { }); it('respects anchorPosition bottom', async () => { + const testID = 'bottom-positioned-menu'; const dimensionsSpy = jest.spyOn(Dimensions, 'get').mockReturnValue({ width: 400, height: 800, @@ -180,7 +158,7 @@ it('respects anchorPosition bottom', async () => { } anchorPosition="bottom" - contentStyle={styles.contentStyle} + testID={testID} > @@ -198,7 +176,7 @@ it('respects anchorPosition bottom', async () => { }); await waitFor(() => { - const menu = screen.getByTestId('menu-view'); + const menu = screen.getByTestId(`${testID}-view`); expect(menu).toHaveStyle({ position: 'absolute', left: 100, @@ -210,40 +188,9 @@ it('respects anchorPosition bottom', async () => { dimensionsSpy.mockRestore(); }); -it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - Open menu} - testID="menu" - contentStyle={[{ transform: [{ scale: value }] }]} - > - - - - ); - expect(screen.getByTestId('menu-surface-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('menu-surface-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); - it('renders menu with mode "elevated"', async () => { + const testID = 'elevated-menu'; + await render( { onDismiss={jest.fn()} anchor={} mode="elevated" + testID={testID} > @@ -258,7 +206,7 @@ it('renders menu with mode "elevated"', async () => { ); - const menuSurface = screen.getByTestId('menu-surface'); + const menuSurface = screen.getByTestId(`${testID}-surface`); // Get flattened styles // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. @@ -269,6 +217,8 @@ it('renders menu with mode "elevated"', async () => { }); it('renders menu with mode "flat"', async () => { + const testID = 'flat-menu'; + await render( { onDismiss={jest.fn()} anchor={} mode="flat" + testID={testID} > @@ -283,7 +234,7 @@ it('renders menu with mode "flat"', async () => { ); - const menuSurface = screen.getByTestId('menu-surface'); + const menuSurface = screen.getByTestId(`${testID}-surface`); // Get flattened styles // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index ff4cf2fd41..83d644e0e1 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -1,4 +1,4 @@ -import { Animated, BackHandler as RNBackHandler, Text } from 'react-native'; +import { BackHandler as RNBackHandler, Text } from 'react-native'; import type { BackHandlerStatic as RNBackHandlerStatic } from 'react-native'; import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'; @@ -111,7 +111,7 @@ describe('Modal', () => { expect(onDismiss).toHaveBeenCalled(); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -123,7 +123,7 @@ describe('Modal', () => { opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -138,7 +138,7 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -150,7 +150,7 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -158,7 +158,7 @@ describe('Modal', () => { opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -166,9 +166,7 @@ describe('Modal', () => { jest.runAllTimers(); }); - expect( - screen.queryByTestId('modal-surface-outer-layer') - ).not.toBeOnTheScreen(); + expect(screen.queryByTestId('modal-surface')).not.toBeOnTheScreen(); expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); }); @@ -182,7 +180,7 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -190,7 +188,7 @@ describe('Modal', () => { BackHandler.mockPressBack(); }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -202,7 +200,7 @@ describe('Modal', () => { opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -225,13 +223,13 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); await userEvent.press(screen.getByTestId('modal-backdrop')); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -243,7 +241,7 @@ describe('Modal', () => { opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); }); @@ -288,7 +286,7 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -296,7 +294,7 @@ describe('Modal', () => { BackHandler.mockPressBack(); }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -308,7 +306,7 @@ describe('Modal', () => { opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); }); @@ -364,7 +362,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: 0, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 0, }); @@ -375,7 +373,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); }); @@ -392,7 +390,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -405,7 +403,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -452,7 +450,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -465,7 +463,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -490,7 +488,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -503,7 +501,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); @@ -526,7 +524,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 1, }); }); @@ -551,7 +549,7 @@ describe('Modal', () => { expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ opacity: 0, }); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('modal-surface')).toHaveStyle({ opacity: 0, }); @@ -577,34 +575,4 @@ describe('Modal', () => { }); }); }); - - it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - {null} - - ); - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.getByTestId('modal-surface-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); - }); }); diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 0671263277..5fea2800ed 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -1,7 +1,5 @@ -import { Animated } from 'react-native'; - import { expect, it, jest } from '@jest/globals'; -import { act, userEvent } from '@testing-library/react-native'; +import { userEvent } from '@testing-library/react-native'; import { render, screen } from '../../test-utils'; import * as Avatar from '../Avatar/Avatar'; @@ -72,33 +70,6 @@ it('renders clear icon wrapper, which is never target of touch events, if search ).toBe('none'); }); -it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - ); - expect(screen.getByTestId('search-bar-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('search-bar-container-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); - it('defines onClearIconPress action and checks if it is called when close button is pressed', async () => { const onClearIconPressMock = jest.fn(); await render( diff --git a/src/components/__tests__/Snackbar.test.tsx b/src/components/__tests__/Snackbar.test.tsx index 16b118da09..5c30f7cc6e 100644 --- a/src/components/__tests__/Snackbar.test.tsx +++ b/src/components/__tests__/Snackbar.test.tsx @@ -1,9 +1,8 @@ -import { Animated, StyleSheet, Text, View } from 'react-native'; +import { StyleSheet, Text, View } from 'react-native'; import { expect, it, jest } from '@jest/globals'; -import { act } from '@testing-library/react-native'; -import { render, screen } from '../../test-utils'; +import { render } from '../../test-utils'; import { red200, white } from '../../theme/colors'; import Snackbar from '../Snackbar'; @@ -92,33 +91,3 @@ it('renders snackbar with View & Text as a child', async () => { expect(tree).toMatchSnapshot(); }); - -it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - Snackbar content - - ); - expect(screen.getByTestId('snack-bar-outer-layer')).toHaveStyle({ - transform: [{ scale: 1 }], - }); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('snack-bar-outer-layer')).toHaveStyle({ - transform: [{ scale: 1.5 }], - }); -}); diff --git a/src/components/__tests__/Surface.test.tsx b/src/components/__tests__/Surface.test.tsx index d0a0437f65..889e51a25a 100644 --- a/src/components/__tests__/Surface.test.tsx +++ b/src/components/__tests__/Surface.test.tsx @@ -1,6 +1,9 @@ -import type { ViewStyle } from 'react-native'; -import { StyleSheet } from 'react-native'; -import { Platform } from 'react-native'; +import { + DynamicColorIOS, + Platform, + PlatformColor, + Pressable, +} from 'react-native'; import { afterEach, @@ -10,34 +13,77 @@ import { it, jest, } from '@jest/globals'; +import { userEvent } from '@testing-library/react-native'; +import { + getAnimatedStyle, + useAnimatedStyle, + useSharedValue, +} from 'react-native-reanimated'; import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; import Surface from '../Surface'; -type StyleCase = { - property: keyof ViewStyle; - value: ViewStyle[keyof ViewStyle]; -}; - const SPOT_SHADOW_OPACITY = 0.19; const AMBIENT_SHADOW_OPACITY = 0.039; +const AnimatedSurface = () => { + const opacity = useSharedValue(0); + const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value })); + + return ( + <> + { + opacity.value = 1; + }} + /> + + {null} + + + ); +}; + +const AnimatedVisualSurface = () => { + const borderRadius = useSharedValue(4); + + return ( + <> + { + borderRadius.value = 8; + }} + /> + + {null} + + + ); +}; + afterEach(() => { jest.restoreAllMocks(); }); describe('Surface', () => { - it('should properly render passed props', async () => { - await render( - - {null} - - ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(screen.getByTestId('surface-container').props.pointerEvents).toBe( - 'box-none' - ); + it('updates styles when a Reanimated shared value changes', async () => { + await render(); + + const surface = screen.getByTestId('animated-surface'); + expect(getAnimatedStyle(surface)).toMatchObject({ opacity: 0 }); + + await userEvent.press(screen.getByTestId('animate-surface')); + await jest.runAllTimersAsync(); + + expect(getAnimatedStyle(surface)).toMatchObject({ opacity: 1 }); }); describe('on iOS', () => { @@ -45,42 +91,13 @@ describe('Surface', () => { jest.replaceProperty(Platform, 'OS', 'ios'); }); - const styles = StyleSheet.create({ - absoluteStyles: { - bottom: 10, - end: 20, - left: 30, - position: 'absolute', - right: 40, - start: 50, - top: 60, - }, - innerLayerViewStyle: { - padding: 13, - }, - restStyle: { - padding: 10, - flexDirection: 'row', - alignContent: 'center', - }, - }); - it('should render Surface with appropriate bg color but without shadow, if mode is set to "flat"', async () => { await render( - + {null} ); - // @ts-expect-error - expect(screen.getByTestId('surface-test-outer-layer')).not.toHaveStyle({ - shadowOpacity: expect.any(Number), - }); // @ts-expect-error expect(screen.getByTestId('surface-test')).not.toHaveStyle({ shadowOpacity: expect.any(Number), @@ -90,192 +107,70 @@ describe('Surface', () => { }); }); - it('should render a spot shadow over an ambient shadow, if mode is elevated', async () => { + it('should render a spot shadow if mode is elevated', async () => { await render( {null} ); - expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle({ + expect(screen.getByTestId('surface-test')).toHaveStyle({ shadowOpacity: SPOT_SHADOW_OPACITY, }); + }); + + it('applies background and shape props in flat mode', async () => { + const backgroundColor = 'rgba(1, 2, 3, 0.5)'; + await render( + + {null} + + ); + expect(screen.getByTestId('surface-test')).toHaveStyle({ - shadowOpacity: AMBIENT_SHADOW_OPACITY, + backgroundColor, + borderRadius: 4, + borderTopLeftRadius: 8, }); }); - it.each([ - { property: 'opacity', value: 0.7 }, - { property: 'transform', value: [{ scale: 1.02 }] }, - { property: 'width', value: '42%' }, - { property: 'height', value: '32.5%' }, - { property: 'margin', value: 13 }, - { property: 'marginLeft', value: 13.1 }, - { property: 'marginRight', value: 13.2 }, - { property: 'marginTop', value: 13.3 }, - { property: 'marginBottom', value: 13.4 }, - { property: 'marginHorizontal', value: 13.5 }, - { property: 'marginVertical', value: 13.6 }, - { property: 'position', value: 'absolute' }, - { property: 'alignSelf', value: 'flex-start' }, - { property: 'top', value: 1.1 }, - { property: 'right', value: 1.2 }, - { property: 'bottom', value: 1.3 }, - { property: 'left', value: 1.4 }, - { property: 'start', value: 1.5 }, - { property: 'end', value: 1.6 }, - { property: 'flex', value: 6 }, - ] satisfies StyleCase[])( - 'applies $property to outer layer only', - async ({ property, value }) => { - const style = { [property]: value }; - - await render( - - {null} - - ); - - expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( - style - ); - expect(screen.getByTestId('surface-test')).not.toHaveStyle(style); - } - ); - - it.each([ - { property: 'padding', value: 12 }, - { property: 'paddingLeft', value: 12.1 }, - { property: 'paddingRight', value: 12.2 }, - { property: 'paddingTop', value: 12.3 }, - { property: 'paddingBottom', value: 12.4 }, - { property: 'paddingHorizontal', value: 12.5 }, - { property: 'paddingVertical', value: 12.6 }, - { property: 'borderWidth', value: 2 }, - { property: 'borderColor', value: 'black' }, - ] satisfies StyleCase[])( - 'applies $property to inner layer only', - async ({ property, value }) => { - const style = { [property]: value }; - - await render( - - {null} - - ); - - expect(screen.getByTestId('surface-test-outer-layer')).not.toHaveStyle( - style - ); - expect(screen.getByTestId('surface-test')).toHaveStyle(style); - } - ); - - it.each([ - { property: 'borderRadius', value: 3 }, - { property: 'borderTopLeftRadius', value: 1 }, - { property: 'borderTopRightRadius', value: 2 }, - { property: 'borderBottomLeftRadius', value: 3 }, - { property: 'borderBottomRightRadius', value: 4 }, - { property: 'backgroundColor', value: 'rgb(4, 5, 6)' }, - ] satisfies StyleCase[])( - 'applies $property to every layer', - async ({ property, value }) => { - const style = { [property]: value }; - - await render( - - {null} - - ); - - expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( - style - ); - expect(screen.getByTestId('surface-test')).toHaveStyle(style); - } - ); - - describe('outer layer', () => { - it('should not render rest style', async () => { - await render( - - {null} - - ); - - expect(screen.getByTestId('surface-test-outer-layer')).not.toHaveStyle( - styles.restStyle - ); - }); + it('updates the animated corner radius', async () => { + await render(); - it('should render absolute position properties on outer layer', async () => { - await render( - - {null} - - ); + const surface = screen.getByTestId('animated-visual-surface'); - expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( - styles.absoluteStyles - ); + expect(getAnimatedStyle(surface)).toMatchObject({ + borderRadius: 4, }); - it('should render absolute position properties on the outer layer', async () => { - await render( - - {null} - - ); + await userEvent.press(screen.getByTestId('animate-visual-props')); + await jest.runAllTimersAsync(); - expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle( - styles.absoluteStyles - ); + expect(getAnimatedStyle(surface)).toMatchObject({ + borderRadius: 8, }); }); - describe('inner layer', () => { - it('should render inner layer styles on the inner layer', async () => { - await render( - - {null} - - ); - - expect(screen.getByTestId('surface-test')).toHaveStyle( - styles.innerLayerViewStyle - ); - }); - }); - - it('applies backgroundColor to every layer', async () => { - const backgroundColor = 'rgb(1, 2, 3)'; + it('does not transition a DynamicColorIOS background', async () => { await render( {null} ); - const style = { backgroundColor }; - expect(screen.getByTestId('surface-test-outer-layer')).toHaveStyle(style); - expect(screen.getByTestId('surface-test')).toHaveStyle(style); - }); - - describe('children wrapper', () => { - it('should render rest styles', async () => { - const combinedStyles = [styles.innerLayerViewStyle, styles.restStyle]; - - await render( - - {null} - - ); - - expect(screen.getByTestId('surface-test')).toHaveStyle(combinedStyles); + expect( + getAnimatedStyle(screen.getByTestId('surface-test')) + ).toMatchObject({ + transitionProperty: expect.not.arrayContaining(['backgroundColor']), }); }); }); @@ -287,12 +182,7 @@ describe('Surface', () => { it('should render Surface with appropriate bg color but without shadow, if mode is set to "flat"', async () => { await render( - + {null} ); @@ -317,6 +207,23 @@ describe('Surface', () => { elevation: 12, }); }); + + it('does not transition a PlatformColor background', async () => { + await render( + + {null} + + ); + + expect( + getAnimatedStyle(screen.getByTestId('surface-container')) + ).toMatchObject({ + transitionProperty: expect.not.arrayContaining(['backgroundColor']), + }); + }); }); describe('on Web', () => { diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx index 1ea9e20bae..bd798e8180 100644 --- a/src/components/__tests__/ToggleButton.test.tsx +++ b/src/components/__tests__/ToggleButton.test.tsx @@ -1,10 +1,7 @@ -import { Animated } from 'react-native'; - -import { describe, expect, it, jest } from '@jest/globals'; -import { act } from '@testing-library/react-native'; +import { describe, expect, it } from '@jest/globals'; import { getTheme } from '../../core/theming'; -import { render, screen } from '../../test-utils'; +import { render } from '../../test-utils'; import ToggleButton from '../ToggleButton'; import { getToggleButtonColor } from '../ToggleButton/utils'; @@ -55,36 +52,3 @@ describe('getToggleButtonColor', () => { ); }); }); - -it('animated value changes correctly', async () => { - const value = new Animated.Value(1); - await render( - - ); - expect(screen.getByTestId('toggle-button-container-outer-layer')).toHaveStyle( - { - transform: [{ scale: 1 }], - } - ); - - Animated.timing(value, { - toValue: 1.5, - useNativeDriver: false, - duration: 200, - }).start(); - - await act(() => { - jest.advanceTimersByTime(200); - }); - expect(screen.getByTestId('toggle-button-container-outer-layer')).toHaveStyle( - { - transform: [{ scale: 1.5 }], - } - ); -}); diff --git a/src/components/__tests__/__snapshots__/Badge.test.tsx.snap b/src/components/__tests__/__snapshots__/Badge.test.tsx.snap index 0090bbfbc0..3ffc31ac78 100644 --- a/src/components/__tests__/__snapshots__/Badge.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Badge.test.tsx.snap @@ -3,6 +3,7 @@ exports[`renders badge 1`] = ` @@ -280,132 +398,193 @@ exports[`renders hidden banner, without action buttons and without image 1`] = ` - + + - - + + - - - Two line text string with two actions. One to two lines is preferable on mobile. - - - + > + Two line text string with two actions. One to two lines is preferable on mobile. + + @@ -415,148 +594,242 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` - + + + - - - - - + + - Two line text string with two actions. One to two lines is preferable on mobile. - - - - + + + - + + - + + - - - first - - - + undefined, + ], + ], + ] + } + testID="button-text" + > + first + @@ -713,126 +1010,220 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` - + + + - - - - Two line text string with two actions. One to two lines is preferable on mobile. - - - - + + + - + + - + + - - - first - - - + undefined, + ], + ], + ] + } + testID="button-text" + > + first + - + - + + - + + - - - second - - - + undefined, + ], + ], + ] + } + testID="button-text" + > + second + @@ -1141,142 +1614,202 @@ exports[`renders visible banner, without action buttons and with image 1`] = ` - + + + - - - - - + + - Two line text string with two actions. One to two lines is preferable on mobile. - - - + > + Two line text string with two actions. One to two lines is preferable on mobile. + + @@ -1286,120 +1819,180 @@ exports[`renders visible banner, without action buttons and without image 1`] = - + + + - - - - Two line text string with two actions. One to two lines is preferable on mobile. - - - + > + Two line text string with two actions. One to two lines is preferable on mobile. + + diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index 33855bb467..82f7a0cc74 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -74,118 +74,107 @@ exports[`allows customizing Route's type via generics 1`] = ` @@ -194,276 +183,275 @@ exports[`allows customizing Route's type via generics 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "transform": [ + { + "scaleX": 1, + }, + ], + "width": 64, } } - > - + - + - + - + - - + /> + + - - - First - - - - + + + - First - - + ], + ] + } + > + First + - + + @@ -471,208 +459,192 @@ exports[`allows customizing Route's type via generics 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } - > - + - + - + - - + /> + + - - - Second - - - - + + + - Second - - + ], + ] + } + > + Second + @@ -757,119 +729,108 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` @@ -878,216 +839,215 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "transform": [ + { + "scaleX": 1, + }, + ], + "width": 64, + } + } + /> + - - - + + + - magnify - - - + magnify + + + - - magnify - - - + - - + /> - + + @@ -1095,200 +1055,199 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - camera - - - + camera + + + - - camera - - - + - - + /> - + + @@ -1296,147 +1255,131 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - inbox - - - + inbox + + + - - inbox - - - + - - + /> @@ -1521,119 +1464,108 @@ exports[`hides labels in shifting bottom navigation 1`] = ` @@ -1642,216 +1574,215 @@ exports[`hides labels in shifting bottom navigation 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "transform": [ + { + "scaleX": 1, + }, + ], + "width": 64, + } + } + /> + - - - + + + - magnify - - - + magnify + + + - - magnify - - - + - - + /> - + + @@ -1859,200 +1790,199 @@ exports[`hides labels in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - - camera - - - - + + + - camera - - - + camera + + + + - - + /> - + + @@ -2060,147 +1990,131 @@ exports[`hides labels in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - inbox - - - + inbox + + + - - inbox - - - + - - + /> @@ -2423,118 +2337,107 @@ exports[`renders bottom navigation with getLazy 1`] = ` @@ -2543,336 +2446,335 @@ exports[`renders bottom navigation with getLazy 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "transform": [ + { + "scaleX": 1, + }, + ], + "width": 64, + } + } + /> + - - - + + + - magnify - - - + magnify + + + - - magnify - - - + - - + /> + + - - - Route: 0 - - - - + + + - Route: 0 - - + ], + ] + } + > + Route: 0 + - + + @@ -2880,320 +2782,319 @@ exports[`renders bottom navigation with getLazy 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - camera - - - + camera + + + - - camera - - - + - - + /> + + - - - Route: 1 - - - - + + + - Route: 1 - - + ], + ] + } + > + Route: 1 + - + + @@ -3201,320 +3102,319 @@ exports[`renders bottom navigation with getLazy 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - inbox - - - + inbox + + + - - inbox - - - + - - + /> + + - - - Route: 2 - - - - + + + - Route: 2 - - + ], + ] + } + > + Route: 2 + - + + @@ -3522,320 +3422,319 @@ exports[`renders bottom navigation with getLazy 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - heart - - - + heart + + + - - heart - - - + - - + /> + + - - - Route: 3 - - - - + + + - Route: 3 - - + ], + ] + } + > + Route: 3 + - + + @@ -3843,268 +3742,252 @@ exports[`renders bottom navigation with getLazy 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - - shopping-music - - - - - shopping-music - - - - - + ], + ] + } + > + shopping-music + - - + shopping-music + + + + + + + + + - Route: 4 - - - - + + + - Route: 4 - - + ], + ] + } + > + Route: 4 + @@ -4189,118 +4072,112 @@ exports[`renders bottom navigation with scene animation 1`] = ` @@ -4309,285 +4186,284 @@ exports[`renders bottom navigation with scene animation 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, "transform": [ { - "translateY": 0, + "scaleX": 1, }, ], - "width": 32, + "width": 64, + } + } + /> + - - - + + + - magnify - - - + magnify + + + - - magnify - - - + - - + /> + + - - - Route: 0 - - + ], + ] + } + > + Route: 0 + - + + @@ -4595,269 +4471,268 @@ exports[`renders bottom navigation with scene animation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - camera - - - + camera + + + - - camera - - - + - - + /> + + - - - Route: 1 - - + ], + ] + } + > + Route: 1 + - + + @@ -4865,269 +4740,139 @@ exports[`renders bottom navigation with scene animation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - - inbox - - - - - inbox - - - - - + ], + ] + } + > + inbox + - - - Route: 2 - - + }, + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + inbox + + + + - - @@ -5135,269 +4880,397 @@ exports[`renders bottom navigation with scene animation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 0, } } > - - - heart - - - - - heart - - - + + + + + + + + + - - + ], + ] + } + > + heart + - + heart + + + + - + + + + + - Route: 3 - - + ], + ] + } + > + Route: 3 + - + + @@ -5405,217 +5278,196 @@ exports[`renders bottom navigation with scene animation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - - shopping-music - - - - + + + - shopping-music - - - + shopping-music + + + + - - + /> + + - - - Route: 4 - - + ], + ] + } + > + Route: 4 + @@ -5700,118 +5552,107 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` @@ -5820,211 +5661,210 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "transform": [ + { + "scaleX": 1, + }, + ], + "width": 64, } } - > - + - + - + - + - - + /> + + - - - Route: 0 - - - + + + - - Route: 0 - - + Route: 0 + - + + @@ -6032,195 +5872,194 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } - > - + - + - + - - + /> + + - - - Route: 1 - - - + + + - - Route: 1 - - + Route: 1 + - + + @@ -6228,143 +6067,127 @@ exports[`renders custom icon and label in non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } - > - + - + - + - - + /> + + - - - Route: 2 - - - + + + - - Route: 2 - - + Route: 2 + @@ -6449,118 +6272,112 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` @@ -6569,193 +6386,192 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, "transform": [ { - "translateY": 0, + "scaleX": 1, }, ], - "width": 32, + "width": 64, } } - > - + - + - + - + - - + /> + + - - - Route: 0 - - + Route: 0 + - + + @@ -6763,177 +6579,176 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } - > - + - + - + - - + /> + + - - - Route: 1 - - + Route: 1 + - + + @@ -6941,177 +6756,176 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } - > - + - + - + - - + /> + + - - - Route: 2 - - + Route: 2 + - + + @@ -7119,177 +6933,176 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } - > - + - + - + - - + /> + + - - - Route: 3 - - + Route: 3 + - + + @@ -7297,125 +7110,104 @@ exports[`renders custom icon and label in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } - > - + - + - + - - + /> + + - - - Route: 4 - - + Route: 4 + @@ -7500,118 +7292,107 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom @@ -7620,336 +7401,335 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "transform": [ + { + "scaleX": 1, + }, + ], + "width": 64, + } + } + /> + - - - + + + - magnify - - - + magnify + + + - - magnify - - - + - - + /> + + - - - Route: 0 - - - - + + + - Route: 0 - - + ], + ] + } + > + Route: 0 + - + + @@ -7957,320 +7737,319 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - - camera - - - - + + + - camera - - - + camera + + + + - - + /> + + - - - Route: 1 - - - - + + + - Route: 1 - - + ], + ] + } + > + Route: 1 + - + + @@ -8278,268 +8057,252 @@ exports[`renders custom icon and label with custom colors in non-shifting bottom collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - inbox - - - + inbox + + + - - inbox - - - + - - + /> + + - - - Route: 2 - - - - + + + - Route: 2 - - + ], + ] + } + > + Route: 2 + @@ -8624,118 +8387,112 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav @@ -8744,285 +8501,284 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, "transform": [ { - "translateY": 0, + "scaleX": 1, }, ], - "width": 32, + "width": 64, + } + } + /> + - - - + + + - magnify - - - + magnify + + + - - magnify - - - + - - + /> + + - - - Route: 0 - - + ], + ] + } + > + Route: 0 + - + + @@ -9030,269 +8786,268 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - camera - - - + camera + + + - - camera - - - + - - + /> + + - - - Route: 1 - - + ], + ] + } + > + Route: 1 + - + + @@ -9300,217 +9055,196 @@ exports[`renders custom icon and label with custom colors in shifting bottom nav collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - inbox - - - + inbox + + + - - inbox - - - + - - + /> + + - - - Route: 2 - - + ], + ] + } + > + Route: 2 + @@ -9595,118 +9329,107 @@ exports[`renders non-shifting bottom navigation 1`] = ` @@ -9714,337 +9437,336 @@ exports[`renders non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, + "height": 32, + "transform": [ + { + "scaleX": 1, + }, + ], + "width": 64, + } + } + /> + + + magnify + + + - - - - magnify - - - + + - - magnify - - - + - - + /> + + - - - Route: 0 - - - - + + + - Route: 0 - - + ], + ] + } + > + Route: 0 + - + + @@ -10052,320 +9774,319 @@ exports[`renders non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - camera - - - + camera + + + - - camera - - - + - - + /> + + - - - Route: 1 - - - - + + + - Route: 1 - - + ], + ] + } + > + Route: 1 + - + + @@ -10373,268 +10094,252 @@ exports[`renders non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - inbox - - - + inbox + + + - - inbox - - - + - - + /> + + - - - Route: 2 - - - - + + + - Route: 2 - - + ], + ] + } + > + Route: 2 + @@ -10719,118 +10424,112 @@ exports[`renders shifting bottom navigation 1`] = ` @@ -10839,285 +10538,284 @@ exports[`renders shifting bottom navigation 1`] = ` style={ { "alignSelf": "center", + "backgroundColor": "rgba(232, 222, 248, 1)", + "borderRadius": 16, "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, "transform": [ { - "translateY": 0, + "scaleX": 1, }, ], - "width": 32, + "width": 64, + } + } + /> + - - - + + + - magnify - - - + magnify + + + - - magnify - - - + - - + /> + + - - - Route: 0 - - + ], + ] + } + > + Route: 0 + - + + @@ -11125,269 +10823,268 @@ exports[`renders shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - - camera - - - - + + + - camera - - - + camera + + + + - - + /> + + - - - Route: 1 - - + ], + ] + } + > + Route: 1 + - + + @@ -11395,269 +11092,268 @@ exports[`renders shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - inbox - - - + inbox + + + - - inbox - - - + - - + /> + + - - - Route: 2 - - + ], + ] + } + > + Route: 2 + - + + @@ -11665,269 +11361,268 @@ exports[`renders shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, + } + } + > + + heart + + + - - - heart - - - + heart + + + - - heart - - - + - - + /> + + - - - Route: 3 - - + ], + ] + } + > + Route: 3 + - + + @@ -11935,217 +11630,196 @@ exports[`renders shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignSelf": "center", - "height": 32, - "justifyContent": "center", - "marginBottom": 4, - "marginHorizontal": 12, - "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], - "width": 32, + "alignItems": "center", + "bottom": 0, + "left": 0, + "opacity": 0, + "position": "absolute", + "right": 0, + "top": 4, } } > - - + + + - shopping-music - - - + shopping-music + + + - - shopping-music - - - + - - + /> + + - - - Route: 4 - - + ], + ] + } + > + Route: 4 + diff --git a/src/components/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/__tests__/__snapshots__/Button.test.tsx.snap index bbc1fff1be..ff911ae1d0 100644 --- a/src/components/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Button.test.tsx.snap @@ -4,31 +4,39 @@ exports[`renders button with an accessibility hint 1`] = ` - + + - + - - - Button with accessibility hint - - + ], + ] + } + testID="button-text" + > + Button with accessibility hint + @@ -155,34 +210,42 @@ exports[`renders button with an accessibility hint 1`] = ` exports[`renders button with an accessibility label 1`] = ` - + + - + - - - Button with accessibility label - - + ], + ] + } + testID="button-text" + > + Button with accessibility label + @@ -310,31 +420,39 @@ exports[`renders button with button color 1`] = ` - + + - + - - - Custom Button - - - - + ], + ] + } + testID="button-text" + > + Custom Button + + + `; @@ -462,31 +627,39 @@ exports[`renders button with color 1`] = ` - + + - + - - - Custom Button - - + ], + ] + } + testID="button-text" + > + Custom Button + @@ -614,31 +834,39 @@ exports[`renders button with custom testID 1`] = ` - + + - + - - - Button with custom testID - - + ], + ] + } + testID="custom:testID-text" + > + Button with custom testID + @@ -766,31 +1041,39 @@ exports[`renders button with icon 1`] = ` - + + - + - - - camera - - - + camera + + + - Icon Button - - + ], + ] + } + testID="button-text" + > + Icon Button + @@ -967,31 +1297,39 @@ exports[`renders button with icon in reverse order 1`] = ` - + + - + - - - chevron-right - - - + chevron-right + + + - Right Icon - - + ], + ] + } + testID="button-text" + > + Right Icon + @@ -1170,31 +1555,39 @@ exports[`renders contained contained with mode 1`] = ` - + + - + - - - Contained Button - - + ], + ] + } + testID="button-text" + > + Contained Button + @@ -1323,31 +1763,39 @@ exports[`renders disabled button 1`] = ` - + + - + - - - Disabled Button - - + ], + ] + } + testID="button-text" + > + Disabled Button + @@ -1475,31 +1970,39 @@ exports[`renders loading button 1`] = ` - + + - + @@ -1610,13 +2175,13 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "alignItems": "center", - "bottom": 0, - "justifyContent": "center", - "left": 0, - "position": "absolute", - "right": 0, - "top": 0, + "height": 18, + "transform": [ + { + "rotate": "45deg", + }, + ], + "width": 18, } } > @@ -1624,12 +2189,8 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 18, - "transform": [ - { - "rotate": "45deg", - }, - ], + "height": 9, + "overflow": "hidden", "width": 18, } } @@ -1638,8 +2199,15 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 9, - "overflow": "hidden", + "height": 18, + "transform": [ + { + "translateY": 0, + }, + { + "rotate": "-165deg", + }, + ], "width": 18, } } @@ -1648,15 +2216,8 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 18, - "transform": [ - { - "translateY": 0, - }, - { - "rotate": "-165deg", - }, - ], + "height": 9, + "overflow": "hidden", "width": 18, } } @@ -1665,40 +2226,44 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 9, - "overflow": "hidden", + "borderColor": "rgba(103, 80, 164, 1)", + "borderRadius": 9, + "borderWidth": 1.8, + "height": 18, "width": 18, } } - > - - + /> + + @@ -1706,12 +2271,9 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 18, - "transform": [ - { - "rotate": "45deg", - }, - ], + "height": 9, + "overflow": "hidden", + "top": 9, "width": 18, } } @@ -1720,9 +2282,15 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 9, - "overflow": "hidden", - "top": 9, + "height": 18, + "transform": [ + { + "translateY": -9, + }, + { + "rotate": "345deg", + }, + ], "width": 18, } } @@ -1731,15 +2299,8 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 18, - "transform": [ - { - "translateY": -9, - }, - { - "rotate": "345deg", - }, - ], + "height": 9, + "overflow": "hidden", "width": 18, } } @@ -1748,114 +2309,111 @@ exports[`renders loading button 1`] = ` collapsable={false} style={ { - "height": 9, - "overflow": "hidden", + "borderColor": "rgba(103, 80, 164, 1)", + "borderRadius": 9, + "borderWidth": 1.8, + "height": 18, "width": 18, } } - > - - + /> - + - Loading Button - - + ], + ] + } + testID="button-text" + > + Loading Button + `; -exports[`renders outlined button with mode 1`] = ` - - + + - + - - - Outlined Button - - + ], + ] + } + testID="button-text" + > + Outlined Button + @@ -1984,31 +2589,39 @@ exports[`renders text button by default 1`] = ` - + + - + - - - Text Button - - + ], + ] + } + testID="button-text" + > + Text Button + @@ -2136,31 +2796,39 @@ exports[`renders text button with mode 1`] = ` - + + - + - - - Text Button - - + ], + ] + } + testID="button-text" + > + Text Button + diff --git a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap index 7bf18dde0e..04359a41f9 100644 --- a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap @@ -4,31 +4,41 @@ exports[`renders chip with close button 1`] = ` - + + - + - - - information - - - Example Chip + information - - - - - - close - - - + { + "marginLeft": 8, + "marginRight": 0, + }, + undefined, + ], + ], + ] + } + > + Example Chip + - -`; - -exports[`renders chip with custom close button 1`] = ` - - - - information - - - Example Chip + close - - - - - arrow-down - - - - `; -exports[`renders chip with icon 1`] = ` +exports[`renders chip with custom close button 1`] = ` - + + - + - - - information - - - + information + + + - Example Chip - - + ], + ] + } + > + Example Chip + - -`; - -exports[`renders chip with onPress 1`] = ` - - Example Chip + arrow-down @@ -961,35 +710,45 @@ exports[`renders chip with onPress 1`] = ` `; -exports[`renders outlined disabled chip 1`] = ` +exports[`renders chip with icon 1`] = ` - + + - + + information + + + - Example Chip - - + ], + ] + } + > + Example Chip + `; -exports[`renders selected chip 1`] = ` +exports[`renders chip with onPress 1`] = ` + + + + Example Chip + + + + +`; + +exports[`renders outlined disabled chip 1`] = ` + + + - + + + Example Chip + + + + +`; + +exports[`renders selected chip 1`] = ` + + + + - - - check - - - + check + + + - Example Chip - - + ], + ] + } + > + Example Chip + diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index e9bc774f78..349fa1d224 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -382,280 +382,240 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` - + - + - - - chevron-left - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + - + - + - - - chevron-right - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + @@ -720,560 +680,480 @@ exports[`DataTable.Pagination renders data table pagination with fast-forward bu + - - - page-first - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-first + - + - + - - - chevron-left - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + - + - + - - - chevron-right - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + - + - + - - - page-last - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-last + @@ -1338,280 +1218,240 @@ exports[`DataTable.Pagination renders data table pagination with label 1`] = ` - + - + - - - chevron-left - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + - + - + - - - chevron-right - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + @@ -1684,200 +1524,256 @@ exports[`DataTable.Pagination renders data table pagination with options select - + + - - - menu-down - - - + menu-down + + + - 2 - - + ], + ] + } + testID="button-text" + > + 2 + @@ -1924,560 +1820,480 @@ exports[`DataTable.Pagination renders data table pagination with options select - + - + - - - page-first - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-first + - + - + - - - chevron-left - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-left + - + - + - - - chevron-right - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + chevron-right + - + - + - - - page-last - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + page-last + diff --git a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap index cec33bb556..391413183a 100644 --- a/src/components/__tests__/__snapshots__/FAB.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FAB.test.tsx.snap @@ -2,15 +2,15 @@ exports[`renders FAB large size 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - camera - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + camera + @@ -146,139 +126,119 @@ exports[`renders icon button by default 1`] = ` - + - + - - - camera - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + camera + @@ -288,139 +248,119 @@ exports[`renders icon button with color 1`] = ` - + - + - - - camera - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + camera + @@ -430,139 +370,119 @@ exports[`renders icon button with size 1`] = ` - + - + - - - camera - - + ], + ] + } + > + camera + @@ -572,172 +492,152 @@ exports[`renders icon change animated 1`] = ` - + - + + - - - - camera - - + camera + diff --git a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap index f06b87045f..032971c85e 100644 --- a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap @@ -120,31 +120,41 @@ exports[`renders list item with custom description 1`] = ` - + + - + - - - file-pdf-box - - - + file-pdf-box + + + - DOCS.pdf - - + ], + ] + } + > + DOCS.pdf + diff --git a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap index 3e027106ae..afcd649f4f 100644 --- a/src/components/__tests__/__snapshots__/Menu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Menu.test.tsx.snap @@ -1,25 +1,54 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders menu with content styles 1`] = ` -<> +exports[`renders not visible menu 1`] = ` + - - + - - - - Open menu - - - - - - - - - - + - - - - - - - Undo - - - - - - - - - Redo - - - - - + ], + ] + } + testID="button-text" + > + Open menu + - - -`; - -exports[`renders not visible menu 1`] = ` - + + +`; + +exports[`renders visible menu 1`] = ` +<> + - -`; - -exports[`renders visible menu 1`] = ` -<> - - - - - - - - Open menu - - - - - - - + + /> - - + + + - - - - magnify - - - + magnify + - + + + @@ -245,13 +292,13 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "alignItems": "center", - "bottom": 0, - "justifyContent": "center", - "left": 0, - "position": "absolute", - "right": 0, - "top": 0, + "height": 24, + "transform": [ + { + "rotate": "45deg", + }, + ], + "width": 24, } } > @@ -259,12 +306,8 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 24, - "transform": [ - { - "rotate": "45deg", - }, - ], + "height": 12, + "overflow": "hidden", "width": 24, } } @@ -273,8 +316,15 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 12, - "overflow": "hidden", + "height": 24, + "transform": [ + { + "translateY": 0, + }, + { + "rotate": "-165deg", + }, + ], "width": 24, } } @@ -283,15 +333,8 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 24, - "transform": [ - { - "translateY": 0, - }, - { - "rotate": "-165deg", - }, - ], + "height": 12, + "overflow": "hidden", "width": 24, } } @@ -300,40 +343,44 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 12, - "overflow": "hidden", + "borderColor": "rgba(103, 80, 164, 1)", + "borderRadius": 12, + "borderWidth": 2.4, + "height": 24, "width": 24, } } - > - - + /> + + @@ -341,12 +388,9 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 24, - "transform": [ - { - "rotate": "45deg", - }, - ], + "height": 12, + "overflow": "hidden", + "top": 12, "width": 24, } } @@ -355,9 +399,15 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 12, - "overflow": "hidden", - "top": 12, + "height": 24, + "transform": [ + { + "translateY": -12, + }, + { + "rotate": "345deg", + }, + ], "width": 24, } } @@ -366,15 +416,8 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 24, - "transform": [ - { - "translateY": -12, - }, - { - "rotate": "345deg", - }, - ], + "height": 12, + "overflow": "hidden", "width": 24, } } @@ -383,25 +426,14 @@ exports[`activity indicator snapshot test 1`] = ` collapsable={false} style={ { - "height": 12, - "overflow": "hidden", + "borderColor": "rgba(103, 80, 164, 1)", + "borderRadius": 12, + "borderWidth": 2.4, + "height": 24, "width": 24, } } - > - - + /> @@ -416,29 +448,34 @@ exports[`renders with placeholder 1`] = ` - - + + + - - - - magnify - - - + magnify + - + + + - - - - - close - - - + close + @@ -789,29 +833,34 @@ exports[`renders with text 1`] = ` - - + + + - - - - magnify - - - + magnify + - + + + - - - - - close - - - + close + diff --git a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap index 76d4857c89..095b214bb2 100644 --- a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap @@ -21,69 +21,122 @@ exports[`renders snackbar with Text as a child 1`] = ` } > - + + - - - - Snackbar content - - + + + Snackbar content + @@ -109,94 +162,147 @@ exports[`renders snackbar with View & Text as a child 1`] = ` } > - + + - + - + "alignItems": "center", + "flexDirection": "row", + } + } + > - - + - Error Message which is veryyyyyyyyyyyy longggggggg Error Message which is veryyyyyyyyyyyy longggggggg - - + } + > + Error Message which is veryyyyyyyyyyyy longggggggg Error Message which is veryyyyyyyyyyyy longggggggg + @@ -223,109 +329,195 @@ exports[`renders snackbar with action button 1`] = ` } > - + - + - Snackbar content - + ], + ] + } + > + Snackbar content + + - - + + - + + - - - Undo - - - + false, + false, + { + "color": "rgba(208, 188, 255, 1)", + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + undefined, + ], + ], + ] + } + testID="button-text" + > + Undo + @@ -494,87 +710,140 @@ exports[`renders snackbar with content 1`] = ` } > - + - + - Snackbar content - - + ], + ] + } + > + Snackbar content + `; diff --git a/src/components/__tests__/__snapshots__/Switch.test.tsx.snap b/src/components/__tests__/__snapshots__/Switch.test.tsx.snap index 3589a26ee9..e38a5145e1 100644 --- a/src/components/__tests__/__snapshots__/Switch.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Switch.test.tsx.snap @@ -96,6 +96,7 @@ exports[`Switch render renders disabled off 1`] = ` - + - + - - - magnify - - + ], + ] + } + > + magnify + - + - + - - - close - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + @@ -572,6 +548,7 @@ exports[`renders filled TextInput with TextInput.Icon accessories when error is } /> - + - + - - - magnify - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + magnify + - + - + - - - close - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + @@ -1074,6 +1026,7 @@ exports[`renders filled TextInput with label and value 1`] = ` } /> - + - + - - - magnify - - + ], + ] + } + > + magnify + - + - + - - - close - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + close + @@ -1753,6 +1682,7 @@ exports[`renders outlined TextInput with TextInput.Icon accessories when error i /> - + - + - - - magnify - - + ], + ] + } + > + magnify + - + - + - - - close - - + ], + ] + } + > + close + @@ -2236,6 +2140,7 @@ exports[`renders outlined TextInput with label and value 1`] = ` /> + - - - heart - - + ], + ] + } + > + heart + @@ -144,137 +134,127 @@ exports[`renders toggle button 1`] = ` + - - - heart - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + heart + @@ -284,137 +264,127 @@ exports[`renders unchecked toggle button 1`] = ` + - - - heart - - + { + "backgroundColor": "transparent", + }, + ], + ] + } + > + heart + diff --git a/src/theme/tokens/sys/elevation.ts b/src/theme/tokens/sys/elevation.ts index 18e92dcb63..9a1181ecbe 100644 --- a/src/theme/tokens/sys/elevation.ts +++ b/src/theme/tokens/sys/elevation.ts @@ -1,34 +1,11 @@ // M3 elevation tokens and shadow builder per spec: // https://m3.material.io/styles/elevation/tokens -import { - Animated, - Platform, - type ColorValue, - type ViewStyle, - type Animated as AnimatedNS, -} from 'react-native'; +import { Platform, type ColorValue, type ViewStyle } from 'react-native'; import color from 'color'; -import { isAnimatedValue } from '../../../utils/animations'; -import type { Elevation, ThemeElevation } from '../../types'; - -type AnimatedNativeShadowStyle = { - shadowColor: ColorValue; - shadowOffset: { - width: AnimatedNS.Value; - height: AnimatedNS.AnimatedInterpolation; - }; - shadowOpacity: AnimatedNS.AnimatedInterpolation; - shadowRadius: AnimatedNS.AnimatedInterpolation; -}; - -type AnimatedBoxShadowStyle = { - boxShadow: AnimatedNS.AnimatedInterpolation; -}; - -type AnimatedShadowStyle = AnimatedNativeShadowStyle | AnimatedBoxShadowStyle; +import type { ThemeElevation } from '../../types'; export const defaultElevation: ThemeElevation = { level0: 0, @@ -39,8 +16,6 @@ export const defaultElevation: ThemeElevation = { level5: 5, }; -export const elevationInputRange: Elevation[] = Object.values(defaultElevation); - export const androidElevationLevels = [0, 1, 3, 6, 8, 12]; /** @@ -110,7 +85,7 @@ const IOS_SHADOW_RADIUS_FACTOR = 0.5; const getShadowRadius = (blurRadius: number[]) => blurRadius.map((radius) => round(radius * IOS_SHADOW_RADIUS_FACTOR)); -export const shadowLayers = [ +const shadowLayers = [ { height: androidElevationLevels.map((dp) => round( @@ -130,94 +105,58 @@ export const shadowLayers = [ }, ]; -const getShadowColor = (shadowColor: ColorValue, shadowOpacity: number) => { - if (typeof shadowColor !== 'string') { - throw new Error( - `Expected a string shadow color on Web, but received a ${typeof shadowColor}.` - ); - } - - return color(shadowColor).alpha(shadowOpacity).rgb().string(); +type NativeShadowStyle = { + shadowColor: ColorValue; + shadowOpacity: number; + shadowOffset: { + width: number; + height: number; + }; + shadowRadius: number; }; -const getBoxShadowValue = (elevation: number, layerColors: readonly string[]) => - shadowLayers - .map( - (layer, index) => - `0px ${layer.height[elevation]}px ${layer.blurRadius[elevation]}px ${layerColors[index]}` - ) - .join(', '); +type ShadowStyle = + | NativeShadowStyle + | { boxShadow: NonNullable }; -export function shadow(elevation: number, shadowColor: ColorValue): ViewStyle; -// eslint-disable-next-line no-redeclare export function shadow( - elevation: Animated.Value, + elevation: number, shadowColor: ColorValue -): AnimatedShadowStyle; -// eslint-disable-next-line no-redeclare -export function shadow( - elevation: number | Animated.Value, - shadowColor: ColorValue -): ViewStyle | AnimatedShadowStyle; -// eslint-disable-next-line no-redeclare -export function shadow( - elevation: number | Animated.Value = 0, - shadowColor: ColorValue -): ViewStyle | AnimatedShadowStyle { +): [ShadowStyle, NativeShadowStyle | undefined] { if (Platform.OS === 'web') { - const layerColors = shadowLayers.map((layer) => - getShadowColor(shadowColor, layer.shadowOpacity) - ); - - if (isAnimatedValue(elevation)) { - return { - boxShadow: elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: elevationInputRange.map((value) => - getBoxShadowValue(value, layerColors) - ), - }), - }; + if (typeof shadowColor !== 'string') { + throw new Error( + `Expected a string shadow color on Web, but received a ${typeof shadowColor}.` + ); } - return { - boxShadow: getBoxShadowValue(elevation, layerColors), - }; - } - - // For a single view, we can only draw one shadow - // So we pick the spot shadow, as it shows the depth - const [spotShadow] = shadowLayers; - - if (isAnimatedValue(elevation)) { - return { - shadowColor, - shadowOffset: { - width: new Animated.Value(0), - height: elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: spotShadow.height, - }), + return [ + { + boxShadow: shadowLayers + .map( + (layer) => + `0px ${layer.height[elevation]}px ${layer.blurRadius[elevation]}px ${color( + shadowColor + ) + .alpha(layer.shadowOpacity) + .rgb() + .string()}` + ) + .join(', '), }, - shadowOpacity: elevation.interpolate({ - inputRange: [0, 1], - outputRange: [0, spotShadow.shadowOpacity], - extrapolate: 'clamp', - }), - shadowRadius: elevation.interpolate({ - inputRange: elevationInputRange, - outputRange: spotShadow.shadowRadius, - }), - }; + undefined, + ]; } - return { + const [spotShadow, ambientShadow] = shadowLayers.map((layer) => ({ shadowColor, - shadowOpacity: elevation ? spotShadow.shadowOpacity : 0, + shadowOpacity: elevation ? layer.shadowOpacity : 0, shadowOffset: { width: 0, - height: spotShadow.height[elevation], + height: layer.height[elevation], }, - shadowRadius: spotShadow.shadowRadius[elevation], - }; + shadowRadius: layer.shadowRadius[elevation], + })); + + return [spotShadow, ambientShadow]; } diff --git a/src/utils/animations.ts b/src/utils/animations.ts deleted file mode 100644 index 7af142bbf3..0000000000 --- a/src/utils/animations.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Animated } from 'react-native'; - -export const isAnimatedValue = ( - it: number | string | Animated.AnimatedInterpolation -): it is Animated.Value => it instanceof Animated.Value; From 488274a38935a78b5d808e95b6896c3f57b0edc2 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 2 Sep 2026 14:15:42 +0200 Subject: [PATCH 58/94] refactor: migrate components to Reanimated (#5015) --- docs/6.x/docs/guides/migration.md | 5 +- eslint.config.mjs | 14 + example/src/Examples/ProgressBarExample.tsx | 1 + src/components/ActivityIndicator.tsx | 1 + src/components/Badge.tsx | 2 +- .../BottomNavigation/BottomNavigation.tsx | 1 + .../BottomNavigation/BottomNavigationBar.tsx | 1 + .../BottomNavigationRouteScreen.tsx | 1 + src/components/Checkbox/Checkbox.tsx | 204 +++---- src/components/Checkbox/utils.ts | 4 - src/components/CrossFadeIcon.tsx | 83 ++- src/components/DataTable/DataTableTitle.tsx | 39 +- src/components/Drawer/DrawerCollapsedItem.tsx | 46 +- src/components/FAB/Content.tsx | 6 +- src/components/ProgressBar.tsx | 1 + .../RadioButton/RadioButtonAndroid.tsx | 1 + src/components/Searchbar.tsx | 2 +- .../SegmentedButtons/SegmentedButtonItem.tsx | 68 ++- src/components/TouchableRipple/Pressable.tsx | 8 +- src/components/Typography/AnimatedText.tsx | 12 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 60 +- .../__tests__/BottomNavigation.test.tsx | 2 + .../__snapshots__/Checkbox.test.tsx.snap | 30 +- .../__snapshots__/CheckboxItem.test.tsx.snap | 12 +- .../__tests__/Checkbox/utils.test.tsx | 4 +- src/components/__tests__/ProgressBar.test.tsx | 1 + .../__snapshots__/DataTable.test.tsx.snap | 44 +- .../__snapshots__/FABExtended.test.tsx.snap | 336 +++++++---- .../__snapshots__/FABMenu.test.tsx.snap | 522 ++++++++++++------ .../__snapshots__/IconButton.test.tsx.snap | 30 +- src/utils/useAnimatedValue.tsx | 1 + src/utils/useAnimatedValueArray.tsx | 1 + 32 files changed, 880 insertions(+), 663 deletions(-) diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index e488b101ef..a4d7123a09 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -2,13 +2,11 @@ title: Migration from Paper 5.x to 6.x --- -TBC - ## General changes ### Animations -React Native Paper 6 uses [Reanimated](https://docs.swmansion.com/react-native-reanimated/) for animations as opposed to the built-in React Native `Animated` API. +React Native Paper 6 uses [Reanimated](https://docs.swmansion.com/react-native-reanimated/) for most animations instead of the built-in React Native `Animated` API. So make sure to install `react-native-reanimated` 4.3.0 or later and `react-native-worklets` 0.8.1 or later, then complete the Reanimated setup. See the [getting started guide](./getting-started) for Expo and Community CLI instructions. The following props now accept animated styles returned from `useAnimatedStyle`. They no longer accept `Animated.Value` or `Animated.AnimatedInterpolation` where these were previously supported: @@ -19,6 +17,7 @@ The following props now accept animated styles returned from `useAnimatedStyle`. - `Card`: `style` - `Chip`: `style` - `Dialog`: `style` +- `Drawer.CollapsedItem`: `style` - `FAB` and `FAB.Extended`: `style` - `IconButton`: `style` - `Menu`: `contentStyle` diff --git a/eslint.config.mjs b/eslint.config.mjs index b0415543d2..7c2aeb73a3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -111,6 +111,20 @@ export default defineConfig( }, ], + 'no-restricted-imports': [ + 'error', + { + paths: [ + { + name: 'react-native', + importNames: ['Animated', 'PanResponder'], + message: + 'Use react-native-reanimated for animations and react-native-gesture-handler for gestures.', + }, + ], + }, + ], + '@react-native/platform-colors': 'error', 'promise/no-callback-in-promise': 'error', diff --git a/example/src/Examples/ProgressBarExample.tsx b/example/src/Examples/ProgressBarExample.tsx index a42474b99d..e6ef656cb7 100644 --- a/example/src/Examples/ProgressBarExample.tsx +++ b/example/src/Examples/ProgressBarExample.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: remove after ProgressBar migrates to Reanimated. import { View, StyleSheet, Animated } from 'react-native'; import { diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 58ef633c9b..7ab61dfaa9 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: migrate ActivityIndicator to Reanimated. import { Animated, Easing, Platform, StyleSheet, View } from 'react-native'; import type { ColorValue, StyleProp, ViewProps, ViewStyle } from 'react-native'; diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 4567055f6b..23a8cd973e 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -12,7 +12,7 @@ const LARGE_SIZE = 16; const MAX_LARGE_WIDTH = 36; const LARGE_PADDING = 4; -export type Props = TextProps & { +export type Props = Omit & { /** * Whether the badge is visible */ diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index df7e28cc76..7b0cd12111 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: migrate BottomNavigation to Reanimated. import { Animated, Platform, StyleSheet, View } from 'react-native'; import type { ColorValue, diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index 6305faa227..b5cd6da15a 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: migrate BottomNavigation to Reanimated. import { Animated, Platform, StyleSheet, Pressable, View } from 'react-native'; import type { ColorValue, diff --git a/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx b/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx index 5773e2e1fd..affffc6793 100644 --- a/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx +++ b/src/components/BottomNavigation/BottomNavigationRouteScreen.tsx @@ -1,5 +1,6 @@ import React from 'react'; import type { ReactNode } from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: migrate BottomNavigation to Reanimated. import { Animated, Platform, View } from 'react-native'; import type { ViewProps } from 'react-native'; diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index f2527383d7..02df81b0b4 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -9,13 +9,7 @@ import type { ViewStyle, } from 'react-native'; -import Animated, { - Easing, - ReduceMotion, - useAnimatedStyle, - useSharedValue, - withTiming, -} from 'react-native-reanimated'; +import Animated, { cubicBezier, type CSSStyle } from 'react-native-reanimated'; import { CheckboxTokens } from './tokens'; import { getSelectionVisualState } from './utils'; @@ -123,7 +117,9 @@ const Checkbox = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const reduceMotion = useReduceMotion(); + const { direction } = useLocale(); // Web (react-native-web) doesn't auto-mirror layout, so flip the mask // anchor manually for RTL. Native handles it via `I18nManager`. @@ -132,56 +128,6 @@ const Checkbox = ({ const selected = status === 'checked' || status === 'indeterminate'; - const reanimatedReduceMotion = reduceMotion - ? ReduceMotion.Always - : ReduceMotion.Never; - - const fillTimingConfig = React.useMemo( - () => ({ - duration: theme.motion.duration.short2, - easing: Easing.bezier(...theme.motion.easing.standard), - reduceMotion: reanimatedReduceMotion, - }), - [ - theme.motion.duration.short2, - theme.motion.easing.standard, - reanimatedReduceMotion, - ] - ); - const checkTimingConfig = React.useMemo( - () => ({ - duration: theme.motion.duration.short3, - easing: Easing.bezier(...theme.motion.easing.standard), - reduceMotion: reanimatedReduceMotion, - }), - [ - theme.motion.duration.short3, - theme.motion.easing.standard, - reanimatedReduceMotion, - ] - ); - - // 0 = unselected (outline only), 1 = selected (filled + drawn icon). - const fillProgress = useSharedValue(selected ? 1 : 0); - const checkProgress = useSharedValue(selected ? 1 : 0); - const firstRender = React.useRef(true); - - React.useEffect(() => { - if (firstRender.current) { - firstRender.current = false; - return; - } - const target = selected ? 1 : 0; - fillProgress.value = withTiming(target, fillTimingConfig); - checkProgress.value = withTiming(target, checkTimingConfig); - }, [ - selected, - fillProgress, - checkProgress, - fillTimingConfig, - checkTimingConfig, - ]); - // Visual state (colors + opacity) for the static layers. `hovered` / // `pressed` aren't tracked here — `TouchableRipple` owns the press ripple // and hover overlay. @@ -194,19 +140,45 @@ const Checkbox = ({ customUncheckedColor: uncheckedColor, }); - // Outline fades out as fill fades in (and vice versa). - const outlineAnimatedStyle = useAnimatedStyle(() => ({ - opacity: 1 - fillProgress.value, - })); + const fillTransitionTimingFunction = cubicBezier( + ...theme.motion.easing.standard + ); + + const fillTransitionDuration = reduceMotion + ? 0 + : theme.motion.duration.short2; + + const checkTransitionDuration = reduceMotion + ? 0 + : theme.motion.duration.short3; + + const checkTransitionTimingFunction = cubicBezier( + ...theme.motion.easing.standard + ); - const fillAnimatedStyle = useAnimatedStyle(() => ({ - opacity: fillProgress.value, - })); + const outlineStyle: CSSStyle = { + borderColor: visual.outlineColor, + opacity: selected ? 0 : 1, + transitionDuration: fillTransitionDuration, + transitionProperty: ['opacity'], + transitionTimingFunction: fillTransitionTimingFunction, + }; - const maskAnimatedStyle = useAnimatedStyle(() => ({ - width: checkProgress.value * CONTAINER_SIZE, - opacity: checkProgress.value, - })); + const fillStyle: CSSStyle = { + backgroundColor: visual.containerColor, + opacity: selected ? 1 : 0, + transitionDuration: fillTransitionDuration, + transitionProperty: ['opacity'], + transitionTimingFunction: fillTransitionTimingFunction, + }; + + const maskStyle: CSSStyle = { + width: selected ? CONTAINER_SIZE : 0, + opacity: selected ? 1 : 0, + transitionDuration: checkTransitionDuration, + transitionProperty: ['width', 'opacity'], + transitionTimingFunction: checkTransitionTimingFunction, + }; // Remember the last drawn glyph so the reveal-mask can finish collapsing // when `selected` flips back to false. Computed via the "derive state @@ -283,92 +255,48 @@ const Checkbox = ({ + - {showIndeterminate ? ( - + > + {showIndeterminate ? ( - - ) : ( - - )} + ) : ( + + + + )} + ); }; -/** - * Reveal-mask checkmark: an L-shape (borderLeftWidth + borderBottomWidth - * rotated -45deg) inside a directional-anchored View whose width animates - * 0 → CONTAINER_SIZE so the stroke "draws in" along the writing direction. - */ -const Checkmark = ({ - color, - maskAnimatedStyle, - flipMaskForWebRTL, - isRTL, - autoSwapsBorders, -}: { - color: ColorValue; - maskAnimatedStyle: ReturnType; - flipMaskForWebRTL: boolean; - isRTL: boolean; - // True on platforms whose layout engine auto-swaps `borderLeftWidth` - // ↔ `borderRightWidth` in RTL (Android, iOS); false on web (RNW). - autoSwapsBorders: boolean; -}) => { - return ( - - - - - - ); -}; - // Web-only style; not in StyleSheet because `outline` is outside ViewStyle. // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const webNoOutline = { outline: 'none' } as unknown as ViewStyle; diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index acd3c61126..75b7398a77 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -62,13 +62,9 @@ const getOutlineColor = ({ const getIconColor = ({ theme, - selected, disabled, error, }: SelectionState): ColorValue => { - if (!selected) { - return 'transparent'; - } if (disabled) { return theme.colors[CheckboxTokens.disabledIconColor]; } diff --git a/src/components/CrossFadeIcon.tsx b/src/components/CrossFadeIcon.tsx index db1fd244d8..686109a501 100644 --- a/src/components/CrossFadeIcon.tsx +++ b/src/components/CrossFadeIcon.tsx @@ -1,7 +1,16 @@ import * as React from 'react'; -import { Animated, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import type { ColorValue } from 'react-native'; +import Animated, { + Easing, + interpolate, + ReduceMotion, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; + import Icon, { isEqualIcon, isValidIcon } from './Icon'; import type { IconSource } from './Icon'; import { useInternalTheme } from '../core/theming'; @@ -38,15 +47,15 @@ const CrossFadeIcon = ({ testID = 'cross-fade-icon', }: Props) => { const theme = useInternalTheme(themeOverrides); + const [currentIcon, setCurrentIcon] = React.useState( () => source ); const [previousIcon, setPreviousIcon] = React.useState( null ); - const { current: fade } = React.useRef(new Animated.Value(1)); - const { scale } = theme.animation; + const fade = useSharedValue(1); if (currentIcon !== source) { setPreviousIcon(() => currentIcon); @@ -55,35 +64,37 @@ const CrossFadeIcon = ({ React.useEffect(() => { if (isValidIcon(previousIcon) && !isEqualIcon(previousIcon, currentIcon)) { - fade.setValue(1); + fade.value = 1; - Animated.timing(fade, { - duration: scale * 200, - toValue: 0, - useNativeDriver: true, - }).start(); + fade.value = withTiming(0, { + duration: theme.animation.scale * 200, + easing: Easing.inOut(Easing.ease), + reduceMotion: ReduceMotion.Never, + }); } - }, [currentIcon, previousIcon, fade, scale]); + }, [currentIcon, fade, previousIcon, theme.animation.scale]); - const opacityPrev = fade; - const opacityNext = previousIcon - ? fade.interpolate({ - inputRange: [0, 1], - outputRange: [1, 0], - }) - : 1; + const hasPreviousIcon = Boolean(previousIcon); - const rotatePrev = fade.interpolate({ - inputRange: [0, 1], - outputRange: ['-90deg', '0deg'], - }); + const previousIconStyle = useAnimatedStyle(() => ({ + opacity: fade.value, + transform: [ + { + rotate: `${interpolate(fade.value, [0, 1], [-90, 0])}deg`, + }, + ], + })); - const rotateNext = previousIcon - ? fade.interpolate({ - inputRange: [0, 1], - outputRange: ['0deg', '-180deg'], - }) - : '0deg'; + const currentIconStyle = useAnimatedStyle(() => ({ + opacity: hasPreviousIcon ? 1 - fade.value : 1, + transform: [ + { + rotate: hasPreviousIcon + ? `${interpolate(fade.value, [0, 1], [0, -180])}deg` + : '0deg', + }, + ], + })); return ( - {previousIcon ? ( + {hasPreviousIcon ? ( ) : null} diff --git a/src/components/DataTable/DataTableTitle.tsx b/src/components/DataTable/DataTableTitle.tsx index 4a972341e6..0015c8753f 100644 --- a/src/components/DataTable/DataTableTitle.tsx +++ b/src/components/DataTable/DataTableTitle.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Animated, PixelRatio, Pressable, StyleSheet } from 'react-native'; +import { PixelRatio, Pressable, StyleSheet } from 'react-native'; import type { GestureResponderEvent, PressableProps, @@ -8,8 +8,17 @@ import type { ViewStyle, } from 'react-native'; +import Animated, { + Easing, + ReduceMotion, + useAnimatedStyle, + useSharedValue, + withTiming, +} from 'react-native-reanimated'; + import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import type { ThemeProp } from '../../types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import Text from '../Typography/Text'; @@ -89,30 +98,30 @@ const DataTableTitle = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const reduceMotion = useReduceMotion(); + const { direction } = useLocale(); - const { current: spinAnim } = React.useRef( - new Animated.Value(sortDirection === 'ascending' ? 0 : 1) - ); + + const spin = useSharedValue(sortDirection === 'ascending' ? 0 : 180); React.useEffect(() => { - Animated.timing(spinAnim, { - toValue: sortDirection === 'ascending' ? 0 : 1, + spin.value = withTiming(sortDirection === 'ascending' ? 0 : 180, { duration: 150, - useNativeDriver: true, - }).start(); - }, [sortDirection, spinAnim]); + easing: Easing.inOut(Easing.ease), + reduceMotion: reduceMotion ? ReduceMotion.Always : ReduceMotion.Never, + }); + }, [reduceMotion, sortDirection, spin]); + + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ rotate: `${spin.value}deg` }], + })); const textColor = theme.colors.onSurface; const alphaTextColor = theme.colors.onSurfaceVariant; - const spin = spinAnim.interpolate({ - inputRange: [0, 1], - outputRange: ['0deg', '180deg'], - }); - const icon = sortDirection ? ( - + ( - new Animated.Value(active ? 1 : 0.5) - ); + const outlineScale = useSharedValue(active ? 1 : 0.5); React.useEffect(() => { if (!active) { - animScale.setValue(0.5); + outlineScale.value = 0.5; } - }, [animScale, active]); + }, [active, outlineScale]); const handlePressOut = () => { - Animated.timing(animScale, { - toValue: 1, + outlineScale.value = withTiming(1, { duration: 150 * scale, - useNativeDriver: true, - }).start(); + easing: Easing.inOut(Easing.ease), + reduceMotion: ReduceMotion.Never, + }); }; const iconPadding = ((!label ? itemSize : outlineHeight) - iconSize) / 2; @@ -135,9 +141,11 @@ const DrawerCollapsedItem = ({ const backgroundColor = active ? theme.colors.secondaryContainer : 'transparent'; + const labelColor = active ? theme.colors.onSurface : theme.colors.onSurfaceVariant; + const iconColor = active ? theme.colors.onSecondaryContainer : theme.colors.onSurfaceVariant; @@ -161,6 +169,12 @@ const DrawerCollapsedItem = ({ const icon = !active && unfocusedIcon !== undefined ? unfocusedIcon : focusedIcon; + const animatedOutlineStyle = useAnimatedStyle(() => ({ + transform: [ + label ? { scaleX: outlineScale.value } : { scale: outlineScale.value }, + ], + })); + return ( diff --git a/src/components/FAB/Content.tsx b/src/components/FAB/Content.tsx index 0e5728c2e3..6114bcd62c 100644 --- a/src/components/FAB/Content.tsx +++ b/src/components/FAB/Content.tsx @@ -1,7 +1,7 @@ import { StyleSheet, View } from 'react-native'; import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; -import Reanimated from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; import type { AnimatedStyle } from 'react-native-reanimated'; import type { TypescaleKey } from '../../theme/types'; @@ -72,7 +72,7 @@ const Content = ({ ) : null} {hasLabel ? ( - {label} - + ) : null} diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index c3a120b1fd..34b69bba3d 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: migrate ProgressBar to Reanimated. import { Animated, Platform, StyleSheet, View } from 'react-native'; import type { LayoutChangeEvent, diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index 86740cafa8..b82d793a1e 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: migrate RadioButtonAndroid to Reanimated. import { Animated, StyleSheet, View } from 'react-native'; import { RadioButtonContext } from './RadioButtonGroup'; diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index 8d7e038c31..cae3ae4334 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -24,7 +24,7 @@ interface Style { marginRight: number; } -export type Props = TextInputProps & { +export type Props = Omit & { /** * Hint text shown when the input is empty. */ diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 4f701f0b48..15e34dd170 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Animated, StyleSheet, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import type { GestureResponderEvent, PressableAndroidRippleConfig, @@ -8,12 +8,20 @@ import type { ViewStyle, } from 'react-native'; +import Animated, { + ReduceMotion, + useAnimatedStyle, + useSharedValue, + withSpring, +} from 'react-native-reanimated'; + import { getSegmentedButtonBorderRadius, getSegmentedButtonColors, getSegmentedButtonDensityPadding, } from './utils'; import { useInternalTheme } from '../../core/theming'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import type { ThemeProp } from '../../types'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; @@ -21,6 +29,12 @@ import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; +const CHECK_SPRING_CONFIG = { + stiffness: 230.2, + damping: 22, + mass: 1, +}; + export type Props = { /** * Whether the segmented button is checked @@ -121,25 +135,28 @@ const SegmentedButtonItem = ({ hitSlop, }: Props) => { const theme = useInternalTheme(themeOverrides); + const reduceMotion = useReduceMotion(); - const checkScale = React.useRef(new Animated.Value(0)).current; + const checkScale = useSharedValue(0); React.useEffect(() => { if (!showSelectedCheck) { return; } - if (checked) { - Animated.spring(checkScale, { - toValue: 1, - useNativeDriver: true, - }).start(); - } else { - Animated.spring(checkScale, { - toValue: 0, - useNativeDriver: true, - }).start(); - } - }, [checked, checkScale, showSelectedCheck]); + + checkScale.value = withSpring(checked ? 1 : 0, { + ...CHECK_SPRING_CONFIG, + reduceMotion: reduceMotion ? ReduceMotion.Always : ReduceMotion.Never, + }); + }, [checked, checkScale, reduceMotion, showSelectedCheck]); + + const checkAnimatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: checkScale.value }], + })); + + const iconAnimatedStyle = useAnimatedStyle(() => ({ + transform: [{ scale: label ? 1 - checkScale.value : 1 }], + })); const { borderColor, textColor, textOpacity, borderWidth, backgroundColor } = getSegmentedButtonColors({ @@ -151,26 +168,19 @@ const SegmentedButtonItem = ({ }); const borderRadius = theme.shapes.corner.largeIncreased; + const segmentBorderRadius = getSegmentedButtonBorderRadius({ theme, segment, }); + const showIcon = !icon ? false : label && checked ? !showSelectedCheck : true; const showCheckedIcon = checked && showSelectedCheck; const iconSize = 18; + const iconStyle = { marginRight: label ? 5 : showCheckedIcon ? 3 : 0, - ...(label && { - transform: [ - { - scale: checkScale.interpolate({ - inputRange: [0, 1], - outputRange: [1, 0], - }), - }, - ], - }), }; const buttonStyle: ViewStyle = { @@ -180,11 +190,14 @@ const SegmentedButtonItem = ({ borderRadius, ...segmentBorderRadius, }; + const paddingVertical = getSegmentedButtonDensityPadding({ density }); + const rippleStyle: ViewStyle = { borderRadius, ...segmentBorderRadius, }; + const labelTextStyle: TextStyle = { ...theme.fonts.labelLarge, color: textColor, @@ -212,13 +225,16 @@ const SegmentedButtonItem = ({ {showCheckedIcon ? ( ) : null} {showIcon ? ( - + ) : null} diff --git a/src/components/TouchableRipple/Pressable.tsx b/src/components/TouchableRipple/Pressable.tsx index ebeb45cf9c..1798a634ba 100644 --- a/src/components/TouchableRipple/Pressable.tsx +++ b/src/components/TouchableRipple/Pressable.tsx @@ -1,6 +1,5 @@ import type * as React from 'react'; import type { - Animated, PressableProps as PressableNativeProps, StyleProp, View, @@ -27,12 +26,7 @@ export type PressableProps = Omit< | undefined; style?: | StyleProp - | Animated.WithAnimatedValue> - | (( - state: PressableStateCallbackType - ) => - | StyleProp - | Animated.WithAnimatedValue>) + | ((state: PressableStateCallbackType) => StyleProp) | undefined; }; diff --git a/src/components/Typography/AnimatedText.tsx b/src/components/Typography/AnimatedText.tsx index 497566b9f1..2892d8d495 100644 --- a/src/components/Typography/AnimatedText.tsx +++ b/src/components/Typography/AnimatedText.tsx @@ -1,14 +1,17 @@ import * as React from 'react'; import type { ReactNode } from 'react'; -import { Animated, StyleSheet, Text } from 'react-native'; -import type { StyleProp, TextProps, TextStyle } from 'react-native'; +import { StyleSheet } from 'react-native'; + +import Animated from 'react-native-reanimated'; import type { VariantProp } from './types'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -type Props = Animated.AnimatedProps & { +type AnimatedTextProps = React.ComponentProps; + +type Props = AnimatedTextProps & { /** * Variant defines appropriate text styles for type role and its size. * Available variants: @@ -24,8 +27,6 @@ type Props = Animated.AnimatedProps & { * Body: `bodyLarge`, `bodyMedium`, `bodySmall` */ variant?: VariantProp; - style?: StyleProp; - ref?: React.Ref; /** * @optional */ @@ -45,6 +46,7 @@ function AnimatedText({ ...rest }: Props) { const theme = useInternalTheme(themeOverrides); + const { direction: writingDirection } = useLocale(); if (variant) { diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 30aaf4e66d..882652f92d 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -688,19 +688,23 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A @@ -911,19 +915,23 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index e4a275921f..cd05ff0956 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-restricted-imports -- TODO: remove after BottomNavigation migrates to Reanimated. */ import { Animated, Easing, @@ -6,6 +7,7 @@ import { StyleSheet, Text, } from 'react-native'; +/* eslint-enable no-restricted-imports */ import type { KeyboardEvent } from 'react-native'; import { describe, expect, it, jest } from '@jest/globals'; diff --git a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap index 714b65414e..202a95467a 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/Checkbox.test.tsx.snap @@ -98,8 +98,6 @@ exports[`renders Checkbox with custom testID 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 0, }, ] @@ -120,8 +118,6 @@ exports[`renders Checkbox with custom testID 1`] = ` }, { "backgroundColor": "rgba(103, 80, 164, 1)", - }, - { "opacity": 1, }, ] @@ -286,8 +282,6 @@ exports[`renders checked Checkbox with color 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 0, }, ] @@ -308,8 +302,6 @@ exports[`renders checked Checkbox with color 1`] = ` }, { "backgroundColor": "red", - }, - { "opacity": 1, }, ] @@ -474,8 +466,6 @@ exports[`renders checked Checkbox with onPress 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 0, }, ] @@ -496,8 +486,6 @@ exports[`renders checked Checkbox with onPress 1`] = ` }, { "backgroundColor": "rgba(103, 80, 164, 1)", - }, - { "opacity": 1, }, ] @@ -662,8 +650,6 @@ exports[`renders indeterminate Checkbox 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 0, }, ] @@ -684,8 +670,6 @@ exports[`renders indeterminate Checkbox 1`] = ` }, { "backgroundColor": "rgba(103, 80, 164, 1)", - }, - { "opacity": 1, }, ] @@ -837,8 +821,6 @@ exports[`renders indeterminate Checkbox with color 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 0, }, ] @@ -859,8 +841,6 @@ exports[`renders indeterminate Checkbox with color 1`] = ` }, { "backgroundColor": "red", - }, - { "opacity": 1, }, ] @@ -1012,8 +992,6 @@ exports[`renders unchecked Checkbox with color 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 0, }, ] @@ -1034,8 +1012,6 @@ exports[`renders unchecked Checkbox with color 1`] = ` }, { "backgroundColor": "red", - }, - { "opacity": 1, }, ] @@ -1200,8 +1176,6 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 1, }, ] @@ -1222,8 +1196,6 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` }, { "backgroundColor": "rgba(103, 80, 164, 1)", - }, - { "opacity": 0, }, ] @@ -1278,7 +1250,7 @@ exports[`renders unchecked Checkbox with onPress 1`] = ` "width": 11, }, { - "borderColor": "transparent", + "borderColor": "rgba(255, 255, 255, 1)", }, null, ] diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index bba3d6f506..9617ebefbd 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -150,8 +150,6 @@ exports[`can render leading checkbox control 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 1, }, ] @@ -172,8 +170,6 @@ exports[`can render leading checkbox control 1`] = ` }, { "backgroundColor": "rgba(103, 80, 164, 1)", - }, - { "opacity": 0, }, ] @@ -228,7 +224,7 @@ exports[`can render leading checkbox control 1`] = ` "width": 11, }, { - "borderColor": "transparent", + "borderColor": "rgba(255, 255, 255, 1)", }, null, ] @@ -469,8 +465,6 @@ exports[`renders unchecked 1`] = ` }, { "borderColor": "rgba(73, 69, 79, 1)", - }, - { "opacity": 1, }, ] @@ -491,8 +485,6 @@ exports[`renders unchecked 1`] = ` }, { "backgroundColor": "rgba(103, 80, 164, 1)", - }, - { "opacity": 0, }, ] @@ -547,7 +539,7 @@ exports[`renders unchecked 1`] = ` "width": 11, }, { - "borderColor": "transparent", + "borderColor": "rgba(255, 255, 255, 1)", }, null, ] diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index 3e340bc961..ee0b09f159 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -82,9 +82,9 @@ describe('getSelectionVisualState', () => { }); describe('iconColor (checkmark / dash glyph)', () => { - it('is transparent when unselected (no glyph drawn)', () => { + it('keeps its color when unselected while the mask hides it', () => { expect(getSelectionVisualState({ theme, selected: false })).toMatchObject( - { iconColor: 'transparent' } + { iconColor: theme.colors.onPrimary } ); }); diff --git a/src/components/__tests__/ProgressBar.test.tsx b/src/components/__tests__/ProgressBar.test.tsx index 4939f25c4c..bed20ed08d 100644 --- a/src/components/__tests__/ProgressBar.test.tsx +++ b/src/components/__tests__/ProgressBar.test.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: remove after ProgressBar migrates to Reanimated. import { Animated, Platform, StyleSheet } from 'react-native'; import { afterEach, expect, it } from '@jest/globals'; diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index 349fa1d224..b1b80b7341 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -2348,15 +2348,19 @@ exports[`DataTable.Title renders data table title with press handler 1`] = ` @@ -295,16 +304,23 @@ exports[`renders extended FAB collapsed 1`] = ` collapsable={false} numberOfLines={1} style={ - { - "color": "rgba(29, 27, 32, 1)", - "fontFamily": "System", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + undefined, + ] } > New message @@ -545,16 +561,25 @@ exports[`renders extended FAB expanded 1`] = ` numberOfLines={1} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="extended-floating-action-button-text" > @@ -608,16 +633,23 @@ exports[`renders extended FAB expanded 1`] = ` collapsable={false} numberOfLines={1} style={ - { - "color": "rgba(29, 27, 32, 1)", - "fontFamily": "System", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + undefined, + ] } > New message @@ -858,16 +890,25 @@ exports[`renders extended FAB large size 1`] = ` numberOfLines={1} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 24, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 32, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 24, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 32, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="extended-floating-action-button-text" > @@ -921,16 +962,23 @@ exports[`renders extended FAB large size 1`] = ` collapsable={false} numberOfLines={1} style={ - { - "color": "rgba(29, 27, 32, 1)", - "fontFamily": "System", - "fontSize": 24, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 32, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 24, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 32, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + undefined, + ] } > New message @@ -1171,16 +1219,25 @@ exports[`renders extended FAB medium size 1`] = ` numberOfLines={1} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 22, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 28, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 22, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 28, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="extended-floating-action-button-text" > @@ -1234,16 +1291,23 @@ exports[`renders extended FAB medium size 1`] = ` collapsable={false} numberOfLines={1} style={ - { - "color": "rgba(29, 27, 32, 1)", - "fontFamily": "System", - "fontSize": 22, - "fontWeight": "400", - "letterSpacing": 0, - "lineHeight": 28, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 22, + "fontWeight": "400", + "letterSpacing": 0, + "lineHeight": 28, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + undefined, + ] } > New message @@ -1484,16 +1548,25 @@ exports[`renders extended FAB not visible 1`] = ` numberOfLines={1} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="extended-floating-action-button-text" > @@ -1547,16 +1620,23 @@ exports[`renders extended FAB not visible 1`] = ` collapsable={false} numberOfLines={1} style={ - { - "color": "rgba(29, 27, 32, 1)", - "fontFamily": "System", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + undefined, + ] } > New message @@ -1797,16 +1877,25 @@ exports[`renders extended FAB transitioning to collapsed 1`] = ` numberOfLines={1} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="extended-floating-action-button-text" > @@ -1860,16 +1949,23 @@ exports[`renders extended FAB transitioning to collapsed 1`] = ` collapsable={false} numberOfLines={1} style={ - { - "color": "rgba(29, 27, 32, 1)", - "fontFamily": "System", - "fontSize": 16, - "fontWeight": "500", - "letterSpacing": 0.15, - "lineHeight": 24, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 16, + "fontWeight": "500", + "letterSpacing": 0.15, + "lineHeight": 24, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + undefined, + ] } > New message diff --git a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap index dcbdf08ee2..059c17b9f4 100644 --- a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap @@ -178,16 +178,25 @@ exports[`renders FAB.Menu closed 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-0-text" > @@ -346,16 +355,25 @@ exports[`renders FAB.Menu closed 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-1-text" > @@ -942,16 +960,25 @@ exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-0-text" > @@ -1110,16 +1137,25 @@ exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-1-text" > @@ -1706,16 +1742,25 @@ exports[`renders FAB.Menu open 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-0-text" > @@ -1874,16 +1919,25 @@ exports[`renders FAB.Menu open 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-1-text" > @@ -2470,16 +2524,25 @@ exports[`renders FAB.Menu with 6 items 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-0-text" > @@ -2638,16 +2701,25 @@ exports[`renders FAB.Menu with 6 items 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-1-text" > @@ -2806,16 +2878,25 @@ exports[`renders FAB.Menu with 6 items 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-2-text" > @@ -2974,16 +3055,25 @@ exports[`renders FAB.Menu with 6 items 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-3-text" > @@ -3142,16 +3232,25 @@ exports[`renders FAB.Menu with 6 items 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-4-text" > @@ -3310,16 +3409,25 @@ exports[`renders FAB.Menu with 6 items 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-5-text" > @@ -3907,16 +4015,25 @@ exports[`renders FAB.Menu with center alignment 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-0-text" > @@ -4075,16 +4192,25 @@ exports[`renders FAB.Menu with center alignment 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-1-text" > @@ -4701,16 +4827,25 @@ exports[`renders FAB.Menu with items having icons 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } > Send email @@ -4898,16 +5033,25 @@ exports[`renders FAB.Menu with items having icons 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } > Set reminder @@ -5493,16 +5637,25 @@ exports[`renders FAB.Menu with start alignment 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-0-text" > @@ -5661,16 +5814,25 @@ exports[`renders FAB.Menu with start alignment 1`] = ` collapsable={false} selectable={false} style={ - { - "color": "rgba(33, 0, 93, 1)", - "fontFamily": "System", - "fontSize": 14, - "fontWeight": "500", - "letterSpacing": 0.1, - "lineHeight": 20, - "textAlign": "left", - "writingDirection": "ltr", - } + [ + { + "fontFamily": "System", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.1, + "lineHeight": 20, + }, + { + "textAlign": "left", + }, + { + "color": "rgba(29, 27, 32, 1)", + "writingDirection": "ltr", + }, + { + "color": "rgba(33, 0, 93, 1)", + }, + ] } testID="item-1-text" > diff --git a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap index e383e7d05b..a5564668f6 100644 --- a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap @@ -593,19 +593,23 @@ exports[`renders icon change animated 1`] = ` diff --git a/src/utils/useAnimatedValue.tsx b/src/utils/useAnimatedValue.tsx index 5b61f9f4d7..565a177e0a 100644 --- a/src/utils/useAnimatedValue.tsx +++ b/src/utils/useAnimatedValue.tsx @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-restricted-imports -- TODO: remove after BottomNavigation migrates to Reanimated. import { Animated } from 'react-native'; import useLazyRef from './useLazyRef'; diff --git a/src/utils/useAnimatedValueArray.tsx b/src/utils/useAnimatedValueArray.tsx index 67fbfa0b51..b26ed88793 100644 --- a/src/utils/useAnimatedValueArray.tsx +++ b/src/utils/useAnimatedValueArray.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports -- TODO: remove after BottomNavigation migrates to Reanimated. import { Animated } from 'react-native'; export default function useAnimatedValueArray(initialValues: number[]) { From 52b9bbaf1ae797ba0e7b3c7aae146b93e5a62710 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 2 Sep 2026 16:20:24 +0200 Subject: [PATCH 59/94] chore: rework docs to remove generated files from the repo (#5094) --- .github/workflows/deploy-docs.yml | 4 +- .gitignore | 3 + CONTRIBUTING.md | 40 +- docs/.gitignore | 4 - docs/5.x/_nav.json | 5 - docs/5.x/docs/_meta.json | 5 - .../5.x/docs/components/ActivityIndicator.mdx | 90 - docs/5.x/docs/components/Appbar/Appbar.mdx | 173 - .../docs/components/Appbar/AppbarAction.mdx | 139 - .../components/Appbar/AppbarBackAction.mdx | 94 - .../docs/components/Appbar/AppbarContent.mdx | 139 - .../docs/components/Appbar/AppbarHeader.mdx | 129 - docs/5.x/docs/components/Appbar/_meta.json | 7 - .../5.x/docs/components/Avatar/AvatarIcon.mdx | 72 - .../docs/components/Avatar/AvatarImage.mdx | 115 - .../5.x/docs/components/Avatar/AvatarText.mdx | 88 - docs/5.x/docs/components/Avatar/_meta.json | 5 - docs/5.x/docs/components/Badge.mdx | 99 - docs/5.x/docs/components/Banner.mdx | 172 - .../BottomNavigation/BottomNavigation.mdx | 319 - .../BottomNavigation/BottomNavigationBar.mdx | 295 - .../components/BottomNavigation/_meta.json | 4 - docs/5.x/docs/components/Button/Button.mdx | 322 - docs/5.x/docs/components/Button/_meta.json | 3 - docs/5.x/docs/components/Card/Card.mdx | 183 - docs/5.x/docs/components/Card/CardActions.mdx | 65 - docs/5.x/docs/components/Card/CardContent.mdx | 57 - docs/5.x/docs/components/Card/CardCover.mdx | 59 - docs/5.x/docs/components/Card/CardTitle.mdx | 171 - docs/5.x/docs/components/Card/_meta.json | 7 - .../5.x/docs/components/Checkbox/Checkbox.mdx | 108 - .../components/Checkbox/CheckboxAndroid.mdx | 91 - .../docs/components/Checkbox/CheckboxIOS.mdx | 83 - .../docs/components/Checkbox/CheckboxItem.mdx | 191 - docs/5.x/docs/components/Checkbox/_meta.json | 6 - docs/5.x/docs/components/Chip/Chip.mdx | 292 - docs/5.x/docs/components/Chip/_meta.json | 3 - .../docs/components/DataTable/DataTable.mdx | 120 - .../components/DataTable/DataTableCell.mdx | 105 - .../components/DataTable/DataTableHeader.mdx | 66 - .../DataTable/DataTablePagination.mdx | 206 - .../components/DataTable/DataTableRow.mdx | 86 - .../components/DataTable/DataTableTitle.mdx | 114 - docs/5.x/docs/components/DataTable/_meta.json | 8 - docs/5.x/docs/components/Dialog/Dialog.mdx | 138 - .../docs/components/Dialog/DialogActions.mdx | 73 - .../docs/components/Dialog/DialogContent.mdx | 64 - .../5.x/docs/components/Dialog/DialogIcon.mdx | 95 - .../components/Dialog/DialogScrollArea.mdx | 81 - .../docs/components/Dialog/DialogTitle.mdx | 73 - docs/5.x/docs/components/Dialog/_meta.json | 8 - docs/5.x/docs/components/Divider.mdx | 87 - .../components/Drawer/DrawerCollapsedItem.mdx | 153 - .../5.x/docs/components/Drawer/DrawerItem.mdx | 155 - .../docs/components/Drawer/DrawerSection.mdx | 104 - docs/5.x/docs/components/Drawer/_meta.json | 5 - docs/5.x/docs/components/FAB/AnimatedFAB.mdx | 299 - docs/5.x/docs/components/FAB/FAB.mdx | 279 - docs/5.x/docs/components/FAB/FABGroup.mdx | 237 - docs/5.x/docs/components/FAB/_meta.json | 5 - .../docs/components/HelperText/HelperText.mdx | 125 - .../5.x/docs/components/HelperText/_meta.json | 3 - docs/5.x/docs/components/Icon.mdx | 84 - .../docs/components/IconButton/IconButton.mdx | 241 - .../5.x/docs/components/IconButton/_meta.json | 3 - .../docs/components/List/ListAccordion.mdx | 274 - .../components/List/ListAccordionGroup.mdx | 78 - docs/5.x/docs/components/List/ListIcon.mdx | 72 - docs/5.x/docs/components/List/ListItem.mdx | 195 - docs/5.x/docs/components/List/ListSection.mdx | 83 - .../docs/components/List/ListSubheader.mdx | 58 - docs/5.x/docs/components/List/_meta.json | 8 - docs/5.x/docs/components/Menu/Menu.mdx | 194 - docs/5.x/docs/components/Menu/MenuItem.mdx | 211 - docs/5.x/docs/components/Menu/_meta.json | 4 - docs/5.x/docs/components/Modal.mdx | 144 - docs/5.x/docs/components/Portal/Portal.mdx | 68 - .../5.x/docs/components/Portal/PortalHost.mdx | 51 - docs/5.x/docs/components/Portal/_meta.json | 4 - docs/5.x/docs/components/ProgressBar.mdx | 118 - .../components/RadioButton/RadioButton.mdx | 123 - .../RadioButton/RadioButtonAndroid.mdx | 99 - .../RadioButton/RadioButtonGroup.mdx | 82 - .../components/RadioButton/RadioButtonIOS.mdx | 91 - .../RadioButton/RadioButtonItem.mdx | 203 - .../docs/components/RadioButton/_meta.json | 7 - docs/5.x/docs/components/Searchbar.mdx | 264 - .../SegmentedButtons/SegmentedButtons.mdx | 126 - .../components/SegmentedButtons/_meta.json | 3 - docs/5.x/docs/components/Snackbar.mdx | 220 - docs/5.x/docs/components/Surface.mdx | 121 - docs/5.x/docs/components/Switch/Switch.mdx | 95 - docs/5.x/docs/components/Switch/_meta.json | 3 - docs/5.x/docs/components/Text/Text.mdx | 95 - docs/5.x/docs/components/Text/_meta.json | 3 - .../docs/components/TextInput/TextInput.mdx | 304 - .../components/TextInput/TextInputAffix.mdx | 93 - .../components/TextInput/TextInputIcon.mdx | 100 - docs/5.x/docs/components/TextInput/_meta.json | 5 - .../components/ToggleButton/ToggleButton.mdx | 154 - .../ToggleButton/ToggleButtonGroup.mdx | 71 - .../ToggleButton/ToggleButtonRow.mdx | 75 - .../docs/components/ToggleButton/_meta.json | 5 - docs/5.x/docs/components/Tooltip/Tooltip.mdx | 98 - docs/5.x/docs/components/Tooltip/_meta.json | 3 - .../TouchableRipple/TouchableRipple.mdx | 153 - .../components/TouchableRipple/_meta.json | 3 - docs/5.x/docs/components/_meta.json | 180 - .../5.x/docs/guides/migration-guide-to-5.0.md | 8 +- .../guides/theming-with-react-navigation.md | 2 +- docs/5.x/docs/guides/theming.mdx | 2 +- .../6.x/docs/components/ActivityIndicator.mdx | 96 - docs/6.x/docs/components/Appbar/Appbar.mdx | 174 - .../docs/components/Appbar/AppbarAction.mdx | 134 - .../components/Appbar/AppbarBackAction.mdx | 101 - .../docs/components/Appbar/AppbarContent.mdx | 129 - .../docs/components/Appbar/AppbarHeader.mdx | 125 - docs/6.x/docs/components/Appbar/_meta.json | 7 - .../6.x/docs/components/Avatar/AvatarIcon.mdx | 81 - .../docs/components/Avatar/AvatarImage.mdx | 122 - .../6.x/docs/components/Avatar/AvatarText.mdx | 97 - docs/6.x/docs/components/Avatar/_meta.json | 5 - docs/6.x/docs/components/Badge.mdx | 84 - docs/6.x/docs/components/Banner.mdx | 172 - .../BottomNavigation/BottomNavigation.mdx | 308 - .../BottomNavigation/BottomNavigationBar.mdx | 285 - .../components/BottomNavigation/_meta.json | 4 - docs/6.x/docs/components/Button/Button.mdx | 273 - docs/6.x/docs/components/Button/_meta.json | 3 - docs/6.x/docs/components/Card/Card.mdx | 180 - docs/6.x/docs/components/Card/CardActions.mdx | 72 - docs/6.x/docs/components/Card/CardContent.mdx | 64 - docs/6.x/docs/components/Card/CardCover.mdx | 63 - docs/6.x/docs/components/Card/CardTitle.mdx | 176 - docs/6.x/docs/components/Card/_meta.json | 7 - .../6.x/docs/components/Checkbox/Checkbox.mdx | 124 - .../docs/components/Checkbox/CheckboxItem.mdx | 180 - docs/6.x/docs/components/Checkbox/_meta.json | 4 - docs/6.x/docs/components/Chip/Chip.mdx | 291 - docs/6.x/docs/components/Chip/_meta.json | 3 - .../docs/components/DataTable/DataTable.mdx | 127 - .../components/DataTable/DataTableCell.mdx | 109 - .../components/DataTable/DataTableHeader.mdx | 77 - .../DataTable/DataTablePagination.mdx | 187 - .../components/DataTable/DataTableRow.mdx | 90 - .../components/DataTable/DataTableTitle.mdx | 125 - docs/6.x/docs/components/DataTable/_meta.json | 8 - docs/6.x/docs/components/Dialog/Dialog.mdx | 136 - .../docs/components/Dialog/DialogActions.mdx | 80 - .../docs/components/Dialog/DialogContent.mdx | 71 - .../6.x/docs/components/Dialog/DialogIcon.mdx | 101 - .../components/Dialog/DialogScrollArea.mdx | 87 - .../docs/components/Dialog/DialogTitle.mdx | 80 - docs/6.x/docs/components/Dialog/_meta.json | 8 - docs/6.x/docs/components/Divider.mdx | 93 - .../components/Drawer/DrawerCollapsedItem.mdx | 149 - .../6.x/docs/components/Drawer/DrawerItem.mdx | 147 - .../docs/components/Drawer/DrawerSection.mdx | 110 - docs/6.x/docs/components/Drawer/_meta.json | 5 - docs/6.x/docs/components/FAB/FAB.mdx | 197 - docs/6.x/docs/components/FAB/FABExtended.mdx | 223 - docs/6.x/docs/components/FAB/FABMenu.mdx | 122 - docs/6.x/docs/components/FAB/_meta.json | 5 - docs/6.x/docs/components/Icon.mdx | 95 - .../docs/components/IconButton/IconButton.mdx | 182 - .../6.x/docs/components/IconButton/_meta.json | 3 - .../docs/components/List/ListAccordion.mdx | 265 - .../components/List/ListAccordionGroup.mdx | 85 - docs/6.x/docs/components/List/ListIcon.mdx | 79 - docs/6.x/docs/components/List/ListItem.mdx | 201 - docs/6.x/docs/components/List/ListSection.mdx | 90 - .../docs/components/List/ListSubheader.mdx | 63 - docs/6.x/docs/components/List/_meta.json | 8 - docs/6.x/docs/components/Menu/Menu.mdx | 190 - docs/6.x/docs/components/Menu/MenuItem.mdx | 214 - docs/6.x/docs/components/Menu/_meta.json | 4 - docs/6.x/docs/components/Modal.mdx | 176 - docs/6.x/docs/components/Portal/Portal.mdx | 74 - .../6.x/docs/components/Portal/PortalHost.mdx | 56 - docs/6.x/docs/components/Portal/_meta.json | 4 - docs/6.x/docs/components/ProgressBar.mdx | 119 - .../components/RadioButton/RadioButton.mdx | 123 - .../RadioButton/RadioButtonAndroid.mdx | 99 - .../RadioButton/RadioButtonGroup.mdx | 81 - .../components/RadioButton/RadioButtonIOS.mdx | 91 - .../RadioButton/RadioButtonItem.mdx | 202 - .../docs/components/RadioButton/_meta.json | 7 - docs/6.x/docs/components/Searchbar.mdx | 248 - .../SegmentedButtons/SegmentedButtons.mdx | 107 - .../components/SegmentedButtons/_meta.json | 3 - docs/6.x/docs/components/Snackbar.mdx | 218 - docs/6.x/docs/components/Surface.mdx | 247 - docs/6.x/docs/components/Switch/Switch.mdx | 125 - docs/6.x/docs/components/Switch/_meta.json | 3 - docs/6.x/docs/components/Text/Text.mdx | 109 - docs/6.x/docs/components/Text/_meta.json | 3 - .../docs/components/TextInput/TextInput.mdx | 177 - .../components/TextInput/TextInputIcon.mdx | 95 - docs/6.x/docs/components/TextInput/_meta.json | 4 - .../components/ToggleButton/ToggleButton.mdx | 154 - .../ToggleButton/ToggleButtonGroup.mdx | 78 - .../ToggleButton/ToggleButtonRow.mdx | 83 - .../docs/components/ToggleButton/_meta.json | 5 - docs/6.x/docs/components/Tooltip/Tooltip.mdx | 99 - docs/6.x/docs/components/Tooltip/_meta.json | 3 - .../TouchableRipple/TouchableRipple.mdx | 165 - .../components/TouchableRipple/_meta.json | 3 - docs/6.x/docs/components/_meta.json | 173 - .../guides/theming-with-react-navigation.md | 2 +- docs/6.x/docs/guides/theming.mdx | 2 +- docs/README.md | 25 - docs/component-docs-plugin/generatePageMDX.ts | 266 - docs/component-docs-plugin/useDocs.tsx | 46 - docs/component-docs.config.ts | 43 +- docs/component-docs.d.ts | 21 - docs/package.json | 9 +- docs/plugins/component-docs/index.ts | 160 + docs/plugins/component-docs/parser.ts | 500 + docs/plugins/component-docs/render.ts | 191 + docs/plugins/component-docs/types.ts | 26 + docs/rspress.config.ts | 11 +- docs/scripts/generate-5x-docs.ts | 526 + .../generate-current-component-docs.ts | 173 - docs/scripts/generate-rspress-content.ts | 274 - docs/src/components/PropTable.tsx | 184 +- docs/src/data/componentDocs5x.json | 12185 --------------- docs/src/data/componentDocs6x.json | 12420 ---------------- docs/src/pages/index.mdx | 38 - eslint.config.mjs | 10 +- scripts/generate-component-docs.ts | 276 - .../SegmentedButtons/SegmentedButtons.tsx | 4 +- src/components/TextInput/TextInput.tsx | 57 +- yarn.lock | 32 +- 233 files changed, 1646 insertions(+), 47161 deletions(-) delete mode 100644 docs/5.x/docs/components/ActivityIndicator.mdx delete mode 100644 docs/5.x/docs/components/Appbar/Appbar.mdx delete mode 100644 docs/5.x/docs/components/Appbar/AppbarAction.mdx delete mode 100644 docs/5.x/docs/components/Appbar/AppbarBackAction.mdx delete mode 100644 docs/5.x/docs/components/Appbar/AppbarContent.mdx delete mode 100644 docs/5.x/docs/components/Appbar/AppbarHeader.mdx delete mode 100644 docs/5.x/docs/components/Appbar/_meta.json delete mode 100644 docs/5.x/docs/components/Avatar/AvatarIcon.mdx delete mode 100644 docs/5.x/docs/components/Avatar/AvatarImage.mdx delete mode 100644 docs/5.x/docs/components/Avatar/AvatarText.mdx delete mode 100644 docs/5.x/docs/components/Avatar/_meta.json delete mode 100644 docs/5.x/docs/components/Badge.mdx delete mode 100644 docs/5.x/docs/components/Banner.mdx delete mode 100644 docs/5.x/docs/components/BottomNavigation/BottomNavigation.mdx delete mode 100644 docs/5.x/docs/components/BottomNavigation/BottomNavigationBar.mdx delete mode 100644 docs/5.x/docs/components/BottomNavigation/_meta.json delete mode 100644 docs/5.x/docs/components/Button/Button.mdx delete mode 100644 docs/5.x/docs/components/Button/_meta.json delete mode 100644 docs/5.x/docs/components/Card/Card.mdx delete mode 100644 docs/5.x/docs/components/Card/CardActions.mdx delete mode 100644 docs/5.x/docs/components/Card/CardContent.mdx delete mode 100644 docs/5.x/docs/components/Card/CardCover.mdx delete mode 100644 docs/5.x/docs/components/Card/CardTitle.mdx delete mode 100644 docs/5.x/docs/components/Card/_meta.json delete mode 100644 docs/5.x/docs/components/Checkbox/Checkbox.mdx delete mode 100644 docs/5.x/docs/components/Checkbox/CheckboxAndroid.mdx delete mode 100644 docs/5.x/docs/components/Checkbox/CheckboxIOS.mdx delete mode 100644 docs/5.x/docs/components/Checkbox/CheckboxItem.mdx delete mode 100644 docs/5.x/docs/components/Checkbox/_meta.json delete mode 100644 docs/5.x/docs/components/Chip/Chip.mdx delete mode 100644 docs/5.x/docs/components/Chip/_meta.json delete mode 100644 docs/5.x/docs/components/DataTable/DataTable.mdx delete mode 100644 docs/5.x/docs/components/DataTable/DataTableCell.mdx delete mode 100644 docs/5.x/docs/components/DataTable/DataTableHeader.mdx delete mode 100644 docs/5.x/docs/components/DataTable/DataTablePagination.mdx delete mode 100644 docs/5.x/docs/components/DataTable/DataTableRow.mdx delete mode 100644 docs/5.x/docs/components/DataTable/DataTableTitle.mdx delete mode 100644 docs/5.x/docs/components/DataTable/_meta.json delete mode 100644 docs/5.x/docs/components/Dialog/Dialog.mdx delete mode 100644 docs/5.x/docs/components/Dialog/DialogActions.mdx delete mode 100644 docs/5.x/docs/components/Dialog/DialogContent.mdx delete mode 100644 docs/5.x/docs/components/Dialog/DialogIcon.mdx delete mode 100644 docs/5.x/docs/components/Dialog/DialogScrollArea.mdx delete mode 100644 docs/5.x/docs/components/Dialog/DialogTitle.mdx delete mode 100644 docs/5.x/docs/components/Dialog/_meta.json delete mode 100644 docs/5.x/docs/components/Divider.mdx delete mode 100644 docs/5.x/docs/components/Drawer/DrawerCollapsedItem.mdx delete mode 100644 docs/5.x/docs/components/Drawer/DrawerItem.mdx delete mode 100644 docs/5.x/docs/components/Drawer/DrawerSection.mdx delete mode 100644 docs/5.x/docs/components/Drawer/_meta.json delete mode 100644 docs/5.x/docs/components/FAB/AnimatedFAB.mdx delete mode 100644 docs/5.x/docs/components/FAB/FAB.mdx delete mode 100644 docs/5.x/docs/components/FAB/FABGroup.mdx delete mode 100644 docs/5.x/docs/components/FAB/_meta.json delete mode 100644 docs/5.x/docs/components/HelperText/HelperText.mdx delete mode 100644 docs/5.x/docs/components/HelperText/_meta.json delete mode 100644 docs/5.x/docs/components/Icon.mdx delete mode 100644 docs/5.x/docs/components/IconButton/IconButton.mdx delete mode 100644 docs/5.x/docs/components/IconButton/_meta.json delete mode 100644 docs/5.x/docs/components/List/ListAccordion.mdx delete mode 100644 docs/5.x/docs/components/List/ListAccordionGroup.mdx delete mode 100644 docs/5.x/docs/components/List/ListIcon.mdx delete mode 100644 docs/5.x/docs/components/List/ListItem.mdx delete mode 100644 docs/5.x/docs/components/List/ListSection.mdx delete mode 100644 docs/5.x/docs/components/List/ListSubheader.mdx delete mode 100644 docs/5.x/docs/components/List/_meta.json delete mode 100644 docs/5.x/docs/components/Menu/Menu.mdx delete mode 100644 docs/5.x/docs/components/Menu/MenuItem.mdx delete mode 100644 docs/5.x/docs/components/Menu/_meta.json delete mode 100644 docs/5.x/docs/components/Modal.mdx delete mode 100644 docs/5.x/docs/components/Portal/Portal.mdx delete mode 100644 docs/5.x/docs/components/Portal/PortalHost.mdx delete mode 100644 docs/5.x/docs/components/Portal/_meta.json delete mode 100644 docs/5.x/docs/components/ProgressBar.mdx delete mode 100644 docs/5.x/docs/components/RadioButton/RadioButton.mdx delete mode 100644 docs/5.x/docs/components/RadioButton/RadioButtonAndroid.mdx delete mode 100644 docs/5.x/docs/components/RadioButton/RadioButtonGroup.mdx delete mode 100644 docs/5.x/docs/components/RadioButton/RadioButtonIOS.mdx delete mode 100644 docs/5.x/docs/components/RadioButton/RadioButtonItem.mdx delete mode 100644 docs/5.x/docs/components/RadioButton/_meta.json delete mode 100644 docs/5.x/docs/components/Searchbar.mdx delete mode 100644 docs/5.x/docs/components/SegmentedButtons/SegmentedButtons.mdx delete mode 100644 docs/5.x/docs/components/SegmentedButtons/_meta.json delete mode 100644 docs/5.x/docs/components/Snackbar.mdx delete mode 100644 docs/5.x/docs/components/Surface.mdx delete mode 100644 docs/5.x/docs/components/Switch/Switch.mdx delete mode 100644 docs/5.x/docs/components/Switch/_meta.json delete mode 100644 docs/5.x/docs/components/Text/Text.mdx delete mode 100644 docs/5.x/docs/components/Text/_meta.json delete mode 100644 docs/5.x/docs/components/TextInput/TextInput.mdx delete mode 100644 docs/5.x/docs/components/TextInput/TextInputAffix.mdx delete mode 100644 docs/5.x/docs/components/TextInput/TextInputIcon.mdx delete mode 100644 docs/5.x/docs/components/TextInput/_meta.json delete mode 100644 docs/5.x/docs/components/ToggleButton/ToggleButton.mdx delete mode 100644 docs/5.x/docs/components/ToggleButton/ToggleButtonGroup.mdx delete mode 100644 docs/5.x/docs/components/ToggleButton/ToggleButtonRow.mdx delete mode 100644 docs/5.x/docs/components/ToggleButton/_meta.json delete mode 100644 docs/5.x/docs/components/Tooltip/Tooltip.mdx delete mode 100644 docs/5.x/docs/components/Tooltip/_meta.json delete mode 100644 docs/5.x/docs/components/TouchableRipple/TouchableRipple.mdx delete mode 100644 docs/5.x/docs/components/TouchableRipple/_meta.json delete mode 100644 docs/5.x/docs/components/_meta.json delete mode 100644 docs/6.x/docs/components/ActivityIndicator.mdx delete mode 100644 docs/6.x/docs/components/Appbar/Appbar.mdx delete mode 100644 docs/6.x/docs/components/Appbar/AppbarAction.mdx delete mode 100644 docs/6.x/docs/components/Appbar/AppbarBackAction.mdx delete mode 100644 docs/6.x/docs/components/Appbar/AppbarContent.mdx delete mode 100644 docs/6.x/docs/components/Appbar/AppbarHeader.mdx delete mode 100644 docs/6.x/docs/components/Appbar/_meta.json delete mode 100644 docs/6.x/docs/components/Avatar/AvatarIcon.mdx delete mode 100644 docs/6.x/docs/components/Avatar/AvatarImage.mdx delete mode 100644 docs/6.x/docs/components/Avatar/AvatarText.mdx delete mode 100644 docs/6.x/docs/components/Avatar/_meta.json delete mode 100644 docs/6.x/docs/components/Badge.mdx delete mode 100644 docs/6.x/docs/components/Banner.mdx delete mode 100644 docs/6.x/docs/components/BottomNavigation/BottomNavigation.mdx delete mode 100644 docs/6.x/docs/components/BottomNavigation/BottomNavigationBar.mdx delete mode 100644 docs/6.x/docs/components/BottomNavigation/_meta.json delete mode 100644 docs/6.x/docs/components/Button/Button.mdx delete mode 100644 docs/6.x/docs/components/Button/_meta.json delete mode 100644 docs/6.x/docs/components/Card/Card.mdx delete mode 100644 docs/6.x/docs/components/Card/CardActions.mdx delete mode 100644 docs/6.x/docs/components/Card/CardContent.mdx delete mode 100644 docs/6.x/docs/components/Card/CardCover.mdx delete mode 100644 docs/6.x/docs/components/Card/CardTitle.mdx delete mode 100644 docs/6.x/docs/components/Card/_meta.json delete mode 100644 docs/6.x/docs/components/Checkbox/Checkbox.mdx delete mode 100644 docs/6.x/docs/components/Checkbox/CheckboxItem.mdx delete mode 100644 docs/6.x/docs/components/Checkbox/_meta.json delete mode 100644 docs/6.x/docs/components/Chip/Chip.mdx delete mode 100644 docs/6.x/docs/components/Chip/_meta.json delete mode 100644 docs/6.x/docs/components/DataTable/DataTable.mdx delete mode 100644 docs/6.x/docs/components/DataTable/DataTableCell.mdx delete mode 100644 docs/6.x/docs/components/DataTable/DataTableHeader.mdx delete mode 100644 docs/6.x/docs/components/DataTable/DataTablePagination.mdx delete mode 100644 docs/6.x/docs/components/DataTable/DataTableRow.mdx delete mode 100644 docs/6.x/docs/components/DataTable/DataTableTitle.mdx delete mode 100644 docs/6.x/docs/components/DataTable/_meta.json delete mode 100644 docs/6.x/docs/components/Dialog/Dialog.mdx delete mode 100644 docs/6.x/docs/components/Dialog/DialogActions.mdx delete mode 100644 docs/6.x/docs/components/Dialog/DialogContent.mdx delete mode 100644 docs/6.x/docs/components/Dialog/DialogIcon.mdx delete mode 100644 docs/6.x/docs/components/Dialog/DialogScrollArea.mdx delete mode 100644 docs/6.x/docs/components/Dialog/DialogTitle.mdx delete mode 100644 docs/6.x/docs/components/Dialog/_meta.json delete mode 100644 docs/6.x/docs/components/Divider.mdx delete mode 100644 docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx delete mode 100644 docs/6.x/docs/components/Drawer/DrawerItem.mdx delete mode 100644 docs/6.x/docs/components/Drawer/DrawerSection.mdx delete mode 100644 docs/6.x/docs/components/Drawer/_meta.json delete mode 100644 docs/6.x/docs/components/FAB/FAB.mdx delete mode 100644 docs/6.x/docs/components/FAB/FABExtended.mdx delete mode 100644 docs/6.x/docs/components/FAB/FABMenu.mdx delete mode 100644 docs/6.x/docs/components/FAB/_meta.json delete mode 100644 docs/6.x/docs/components/Icon.mdx delete mode 100644 docs/6.x/docs/components/IconButton/IconButton.mdx delete mode 100644 docs/6.x/docs/components/IconButton/_meta.json delete mode 100644 docs/6.x/docs/components/List/ListAccordion.mdx delete mode 100644 docs/6.x/docs/components/List/ListAccordionGroup.mdx delete mode 100644 docs/6.x/docs/components/List/ListIcon.mdx delete mode 100644 docs/6.x/docs/components/List/ListItem.mdx delete mode 100644 docs/6.x/docs/components/List/ListSection.mdx delete mode 100644 docs/6.x/docs/components/List/ListSubheader.mdx delete mode 100644 docs/6.x/docs/components/List/_meta.json delete mode 100644 docs/6.x/docs/components/Menu/Menu.mdx delete mode 100644 docs/6.x/docs/components/Menu/MenuItem.mdx delete mode 100644 docs/6.x/docs/components/Menu/_meta.json delete mode 100644 docs/6.x/docs/components/Modal.mdx delete mode 100644 docs/6.x/docs/components/Portal/Portal.mdx delete mode 100644 docs/6.x/docs/components/Portal/PortalHost.mdx delete mode 100644 docs/6.x/docs/components/Portal/_meta.json delete mode 100644 docs/6.x/docs/components/ProgressBar.mdx delete mode 100644 docs/6.x/docs/components/RadioButton/RadioButton.mdx delete mode 100644 docs/6.x/docs/components/RadioButton/RadioButtonAndroid.mdx delete mode 100644 docs/6.x/docs/components/RadioButton/RadioButtonGroup.mdx delete mode 100644 docs/6.x/docs/components/RadioButton/RadioButtonIOS.mdx delete mode 100644 docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx delete mode 100644 docs/6.x/docs/components/RadioButton/_meta.json delete mode 100644 docs/6.x/docs/components/Searchbar.mdx delete mode 100644 docs/6.x/docs/components/SegmentedButtons/SegmentedButtons.mdx delete mode 100644 docs/6.x/docs/components/SegmentedButtons/_meta.json delete mode 100644 docs/6.x/docs/components/Snackbar.mdx delete mode 100644 docs/6.x/docs/components/Surface.mdx delete mode 100644 docs/6.x/docs/components/Switch/Switch.mdx delete mode 100644 docs/6.x/docs/components/Switch/_meta.json delete mode 100644 docs/6.x/docs/components/Text/Text.mdx delete mode 100644 docs/6.x/docs/components/Text/_meta.json delete mode 100644 docs/6.x/docs/components/TextInput/TextInput.mdx delete mode 100644 docs/6.x/docs/components/TextInput/TextInputIcon.mdx delete mode 100644 docs/6.x/docs/components/TextInput/_meta.json delete mode 100644 docs/6.x/docs/components/ToggleButton/ToggleButton.mdx delete mode 100644 docs/6.x/docs/components/ToggleButton/ToggleButtonGroup.mdx delete mode 100644 docs/6.x/docs/components/ToggleButton/ToggleButtonRow.mdx delete mode 100644 docs/6.x/docs/components/ToggleButton/_meta.json delete mode 100644 docs/6.x/docs/components/Tooltip/Tooltip.mdx delete mode 100644 docs/6.x/docs/components/Tooltip/_meta.json delete mode 100644 docs/6.x/docs/components/TouchableRipple/TouchableRipple.mdx delete mode 100644 docs/6.x/docs/components/TouchableRipple/_meta.json delete mode 100644 docs/6.x/docs/components/_meta.json delete mode 100644 docs/README.md delete mode 100644 docs/component-docs-plugin/generatePageMDX.ts delete mode 100644 docs/component-docs-plugin/useDocs.tsx delete mode 100644 docs/component-docs.d.ts create mode 100644 docs/plugins/component-docs/index.ts create mode 100644 docs/plugins/component-docs/parser.ts create mode 100644 docs/plugins/component-docs/render.ts create mode 100644 docs/plugins/component-docs/types.ts create mode 100644 docs/scripts/generate-5x-docs.ts delete mode 100644 docs/scripts/generate-current-component-docs.ts delete mode 100644 docs/scripts/generate-rspress-content.ts delete mode 100644 docs/src/data/componentDocs5x.json delete mode 100644 docs/src/data/componentDocs6x.json delete mode 100644 docs/src/pages/index.mdx delete mode 100644 scripts/generate-component-docs.ts diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 4aa9b15e2d..a8e11ea034 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -30,8 +30,8 @@ jobs: - name: Setup uses: ./.github/actions/setup - - name: Generate 5.x component docs - run: node scripts/generate-component-docs.ts origin/5.0 docs/src/data/componentDocs5x.json + - name: Generate 5.x docs + run: yarn --cwd docs generate:5x - name: Build docs run: yarn --cwd docs build diff --git a/.gitignore b/.gitignore index f9e97800eb..4c86dcc669 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,9 @@ android/keystores/debug.keystore # dist/ web-build/ +docs/5.x/component-docs.json +docs/5.x/docs/components/ +docs/6.x/docs/components/ # Release # diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78b2b45937..62c3b9c59b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,8 +13,9 @@ The core team works directly on GitHub and all work is public. > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github). 1. Fork the repo and create your branch from `main` (a guide on [how to fork a repository](https://help.github.com/articles/fork-a-repo/)). -2. Run `yarn` on the root level, to setup the development environment. -3. Do the changes you want and test them out in the example app before sending a pull request. +2. Use the Node.js version specified in `.nvmrc`. +3. Run `yarn` on the root level, to setup the development environment. +4. Do the changes you want and test them out in the example app before sending a pull request. ### Commit message convention @@ -25,7 +26,7 @@ We follow the [conventional commits specification](https://www.conventionalcommi - `refactor`: code refactor, e.g. new folder structure for components. - `docs`: changes into documentation, e.g. add usage example for Button. - `test`: adding or updating tests, eg unit, snapshot testing. -- `chore`: tooling changes, e.g. change circleci config. +- `chore`: tooling changes, e.g. change the CI configuration. - `BREAKING CHANGE`: for changes that break existing usage, e.g. change API of a component. Our pre-commit hooks verify that your commit message matches this format when committing. @@ -49,21 +50,34 @@ When you're sending a pull request: When you're working on a component: -- Follow the guidelines described in the [official material design docs](https://material.io/guidelines/). -- Write a brief description of every prop when defining `type Props` to aid with documentation. -- Provide an example usage for the component (check other components to get a idea). -- Update the type definitions for Flow and TypeScript if you changed an API or added a component. +- Follow the guidelines described in the [official material design docs](https://m3.material.io/). +- Write a brief description of every prop when defining the props type to aid with documentation. +- Provide an example usage for the component (check other components to get an idea). +- Update the type definitions for TypeScript if you changed an API or added a component. ### Running the example -The example app uses [Expo](https://expo.dev/) for the React Native example. You will need to install the Expo app for [Android](https://play.google.com/store/apps/details?id=host.exp.exponent) and [iOS](https://itunes.apple.com/app/apple-store/id982107779) to start developing. +The example app uses [Expo](https://expo.dev/) development build for the React Native example. -> [!IMPORTANT] -> The example app is built with `react-native@0.77.x` and Expo SDK 52, which isn’t compatible with Expo Go. To run the app, you have to create a [development build](https://docs.expo.dev/develop/development-builds/create-a-build/). +First, generate the native projects: -After you're done, you can run `yarn example start` in the project root (or `npx expo start` in the `example/` folder) and scan the QR code to launch it on your device. +```sh +yarn example expo prebuild +``` + +Then build and install the development build: + +```sh +yarn example ios +``` + +or: + +```sh +yarn example android +``` -To run the example on web, run `yarn example web` in the project root. +After you're done, you can run `yarn example start` in the project root and scan the QR code to launch it on your device, or press `i`, `a` or `w` to launch it on the iOS simulator, Android emulator, or web browser, respectively. ### Testing a specific pull request/commit @@ -83,7 +97,7 @@ Alternatively, you may clone the `react-native-paper` repo and use the [yalc](ht ### Working on documentation -The documentation is automatically generated from the [TypeScript](https://www.typescriptlang.org/) annotations in the components. You can add comments above the type annotations to add descriptions. To preview the generated documentation, run `yarn docs start` in the project root. +To preview the documentation, run `yarn docs dev` in the project root. ### Publishing a release diff --git a/docs/.gitignore b/docs/.gitignore index 5cb36e5dac..e59dab5e8d 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -9,7 +9,6 @@ # Generated files .cache-loader /public/llms.txt -/public/react-native-paper # Misc .DS_Store @@ -22,9 +21,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -# component-docs-plugin -docs/components - # legacy dist/ .linaria-cache/ diff --git a/docs/5.x/_nav.json b/docs/5.x/_nav.json index 3634f9864d..0099eb059f 100644 --- a/docs/5.x/_nav.json +++ b/docs/5.x/_nav.json @@ -4,11 +4,6 @@ "link": "/docs/guides/getting-started", "activeMatch": "^/docs/guides/" }, - { - "text": "Components", - "link": "/docs/components/ActivityIndicator", - "activeMatch": "^/docs/components/" - }, { "text": "Showcase", "link": "/docs/showcase", diff --git a/docs/5.x/docs/_meta.json b/docs/5.x/docs/_meta.json index 9e4ac38659..1a81e3fb19 100644 --- a/docs/5.x/docs/_meta.json +++ b/docs/5.x/docs/_meta.json @@ -4,11 +4,6 @@ "name": "guides", "label": "Guides" }, - { - "type": "dir-section-header", - "name": "components", - "label": "Components" - }, { "type": "file", "name": "showcase", diff --git a/docs/5.x/docs/components/ActivityIndicator.mdx b/docs/5.x/docs/components/ActivityIndicator.mdx deleted file mode 100644 index 3595f9e66b..0000000000 --- a/docs/5.x/docs/components/ActivityIndicator.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: ActivityIndicator ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Activity indicator is used to present progress of some activity in the app. -It can be used as a drop-in replacement for the ActivityIndicator shipped with React Native. - - - -## Usage - -```js -import * as React from 'react'; -import { ActivityIndicator, MD2Colors } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - -## Props - - - -
- -### animating - -
- - - -
- -### color - -
- - - -
- -### size - -
- - - -
- -### hidesWhenStopped - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Appbar/Appbar.mdx b/docs/5.x/docs/components/Appbar/Appbar.mdx deleted file mode 100644 index c02ab5dd57..0000000000 --- a/docs/5.x/docs/components/Appbar/Appbar.mdx +++ /dev/null @@ -1,173 +0,0 @@ ---- -title: Appbar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to display action items in a bar. It can be placed at the top or bottom. -The top bar usually contains the screen title, controls such as navigation buttons, menu button etc. -The bottom bar usually provides access to a drawer and up to four actions. - -By default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. -See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations - - - -## Usage - -### Top bar - -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} /> - - {}} /> - {}} /> - -); - -export default MyComponent; -``` - -### Bottom bar - -```js -import * as React from 'react'; -import { StyleSheet } from 'react-native'; -import { Appbar, FAB, useTheme } from 'react-native-paper'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; - -const BOTTOM_APPBAR_HEIGHT = 80; -const MEDIUM_FAB_HEIGHT = 56; - -const MyComponent = () => { - const { bottom } = useSafeAreaInsets(); - const theme = useTheme(); - - return ( - - {}} /> - {}} /> - {}} /> - {}} /> - {}} - style={[ - styles.fab, - { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 }, - ]} - /> - - ); -}; - -const styles = StyleSheet.create({ - bottom: { - backgroundColor: 'aquamarine', - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - }, - fab: { - position: 'absolute', - right: 16, - }, -}); - -export default MyComponent; -``` - -## Props - - - -
- -### dark - -
- - - -
- -### children (required) - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### elevated Available in v5.x with theme version 3 - -
- - - -
- -### safeAreaInsets - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Appbar/AppbarAction.mdx b/docs/5.x/docs/components/Appbar/AppbarAction.mdx deleted file mode 100644 index 89de6a56cf..0000000000 --- a/docs/5.x/docs/components/Appbar/AppbarAction.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Appbar.Action ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display an action item in the appbar. - - - -## Usage - -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; -import { Platform } from 'react-native'; - -const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; - -const MyComponent = () => ( - - - {}} /> - {}} /> - -); - -export default MyComponent; -``` - -## Props - - - -
- -### color - -
- - - -
- -### rippleColor - -
- - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### disabled - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### onPress - -
- - - -
- -### isLeading Available in v5.x with theme version 3 - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Appbar/AppbarBackAction.mdx b/docs/5.x/docs/components/Appbar/AppbarBackAction.mdx deleted file mode 100644 index 2c4c62fb8c..0000000000 --- a/docs/5.x/docs/components/Appbar/AppbarBackAction.mdx +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: Appbar.BackAction ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display a back button in the appbar. - - - -## Usage - -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} /> - -); - -export default MyComponent; -``` - -## Props - - - -
- -### color - -
- - - -
- -### size - -
- - - -
- -### disabled - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Appbar/AppbarContent.mdx b/docs/5.x/docs/components/Appbar/AppbarContent.mdx deleted file mode 100644 index da9975e3e2..0000000000 --- a/docs/5.x/docs/components/Appbar/AppbarContent.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Appbar.Content ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display a title and optional subtitle in an appbar. - - - -## Usage - -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => ( - - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### title (required) - -
- - - -
- -### titleStyle - -
- - - -
- -### titleRef - -
- - - -
- -### subtitle Deprecated in v5.x - -
- - - -
- -### subtitleStyle Deprecated in v5.x - -
- - - -
- -### onPress - -
- - - -
- -### disabled - -
- - - -
- -### color - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Appbar/AppbarHeader.mdx b/docs/5.x/docs/components/Appbar/AppbarHeader.mdx deleted file mode 100644 index b05957f6a3..0000000000 --- a/docs/5.x/docs/components/Appbar/AppbarHeader.mdx +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: Appbar.Header ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to use as a header at the top of the screen. -It can contain the screen title, controls such as navigation buttons, menu button etc. - - - -## Usage - -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => { - const _goBack = () => console.log('Went back'); - - const _handleSearch = () => console.log('Searching'); - - const _handleMore = () => console.log('Shown more'); - - return ( - - - - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### dark - -
- - - -
- -### statusBarHeight - -
- - - -
- -### children (required) - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### elevated Available in v5.x with theme version 3 - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Appbar/_meta.json b/docs/5.x/docs/components/Appbar/_meta.json deleted file mode 100644 index d4243fea02..0000000000 --- a/docs/5.x/docs/components/Appbar/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - "Appbar", - "AppbarAction", - "AppbarBackAction", - "AppbarContent", - "AppbarHeader" -] diff --git a/docs/5.x/docs/components/Avatar/AvatarIcon.mdx b/docs/5.x/docs/components/Avatar/AvatarIcon.mdx deleted file mode 100644 index cead6b3256..0000000000 --- a/docs/5.x/docs/components/Avatar/AvatarIcon.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Avatar.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Avatars can be used to represent people in a graphical way. - - - -## Usage - -```js -import * as React from 'react'; -import { Avatar } from 'react-native-paper'; - -const MyComponent = () => ; -``` - -## Props - - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### color - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Avatar/AvatarImage.mdx b/docs/5.x/docs/components/Avatar/AvatarImage.mdx deleted file mode 100644 index 31c3d960e1..0000000000 --- a/docs/5.x/docs/components/Avatar/AvatarImage.mdx +++ /dev/null @@ -1,115 +0,0 @@ ---- -title: Avatar.Image ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Avatars can be used to represent people in a graphical way. - - - -## Usage - -```js -import * as React from 'react'; -import { Avatar } from 'react-native-paper'; - -const MyComponent = () => ( - -); -export default MyComponent; -``` - -## Props - - - -
- -### source (required) - -
- - - -
- -### size - -
- - - -
- -### style - -
- - - -
- -### onError - -
- - - -
- -### onLayout - -
- - - -
- -### onLoad - -
- - - -
- -### onLoadEnd - -
- - - -
- -### onLoadStart - -
- - - -
- -### onProgress - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Avatar/AvatarText.mdx b/docs/5.x/docs/components/Avatar/AvatarText.mdx deleted file mode 100644 index 212f688548..0000000000 --- a/docs/5.x/docs/components/Avatar/AvatarText.mdx +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: Avatar.Text ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Avatars can be used to represent people in a graphical way. - - - -## Usage - -```js -import * as React from 'react'; -import { Avatar } from 'react-native-paper'; - -const MyComponent = () => ; -``` - -## Props - - - -
- -### label (required) - -
- - - -
- -### size - -
- - - -
- -### color - -
- - - -
- -### style - -
- - - -
- -### labelStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Avatar/_meta.json b/docs/5.x/docs/components/Avatar/_meta.json deleted file mode 100644 index b3967b8318..0000000000 --- a/docs/5.x/docs/components/Avatar/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "AvatarIcon", - "AvatarImage", - "AvatarText" -] diff --git a/docs/5.x/docs/components/Badge.mdx b/docs/5.x/docs/components/Badge.mdx deleted file mode 100644 index d071c3d8eb..0000000000 --- a/docs/5.x/docs/components/Badge.mdx +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: Badge ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Badges are small status descriptors for UI elements. -A badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object. - - - -## Usage - -```js -import * as React from 'react'; -import { Badge } from 'react-native-paper'; - -const MyComponent = () => 3; - -export default MyComponent; -``` - -## Props - - - -
- -### visible - -
- - - -
- -### children - -
- - - -
- -### size - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Banner.mdx b/docs/5.x/docs/components/Banner.mdx deleted file mode 100644 index fc54ae6ba4..0000000000 --- a/docs/5.x/docs/components/Banner.mdx +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Banner ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Banner displays a prominent message and related actions. - - - -## Usage - -```js -import * as React from 'react'; -import { Image } from 'react-native'; -import { Banner } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(true); - - return ( - setVisible(false), - }, - { - label: 'Learn more', - onPress: () => setVisible(false), - }, - ]} - icon={({ size }) => ( - - )} - > - There was a problem processing a transaction on your credit card. - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### visible (required) - -
- - - -
- -### children (required) - -
- - - -
- -### icon - -
- - - -
- -### actions - -
- - - -
- -### contentStyle - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - -
- -### onShowAnimationFinished - -
- - - -
- -### onHideAnimationFinished - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/BottomNavigation/BottomNavigation.mdx b/docs/5.x/docs/components/BottomNavigation/BottomNavigation.mdx deleted file mode 100644 index 3196cf34f1..0000000000 --- a/docs/5.x/docs/components/BottomNavigation/BottomNavigation.mdx +++ /dev/null @@ -1,319 +0,0 @@ ---- -title: BottomNavigation ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar. -It is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar). - -By default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. -See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. - - - -## Usage - -```js -import * as React from 'react'; -import { BottomNavigation, Text } from 'react-native-paper'; - -const MusicRoute = () => Music; - -const AlbumsRoute = () => Albums; - -const RecentsRoute = () => Recents; - -const NotificationsRoute = () => Notifications; - -const MyComponent = () => { - const [index, setIndex] = React.useState(0); - const [routes] = React.useState([ - { - key: 'music', - title: 'Favorites', - focusedIcon: 'heart', - unfocusedIcon: 'heart-outline', - }, - { key: 'albums', title: 'Albums', focusedIcon: 'album' }, - { key: 'recents', title: 'Recents', focusedIcon: 'history' }, - { - key: 'notifications', - title: 'Notifications', - focusedIcon: 'bell', - unfocusedIcon: 'bell-outline', - }, - ]); - - const renderScene = BottomNavigation.SceneMap({ - music: MusicRoute, - albums: AlbumsRoute, - recents: RecentsRoute, - notifications: NotificationsRoute, - }); - - return ( - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### shifting - -
- - - -
- -### labeled - -
- - - -
- -### compact - -
- - - -
- -### navigationState (required) - -
- - - -
- -### onIndexChange (required) - -
- - - -
- -### renderScene (required) - -
- - - -
- -### renderIcon - -
- - - -
- -### renderLabel - -
- - - -
- -### renderTouchable - -
- - - -
- -### getAccessibilityLabel - -
- - - -
- -### getBadge - -
- - - -
- -### getColor - -
- - - -
- -### getLabelText - -
- - - -
- -### getLazy - -
- - - -
- -### getTestID - -
- - - -
- -### onTabPress - -
- - - -
- -### onTabLongPress - -
- - - -
- -### activeColor - -
- - - -
- -### inactiveColor - -
- - - -
- -### sceneAnimationEnabled - -
- - - -
- -### sceneAnimationType - -
- - - -
- -### sceneAnimationEasing - -
- - - -
- -### keyboardHidesNavigationBar - -
- - - -
- -### safeAreaInsets - -
- - - -
- -### barStyle - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### activeIndicatorStyle - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/BottomNavigation/BottomNavigationBar.mdx b/docs/5.x/docs/components/BottomNavigation/BottomNavigationBar.mdx deleted file mode 100644 index 5bbbb1ea4f..0000000000 --- a/docs/5.x/docs/components/BottomNavigation/BottomNavigationBar.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: BottomNavigation.Bar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/). - - - -## Usage - -### without React Navigation - -```js -import React from 'react'; -import { useState } from 'react'; -import { View } from 'react-native'; -import { BottomNavigation, Text, Provider } from 'react-native-paper'; -import MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons'; - -function HomeScreen() { - return ( - - Home! - - ); -} - -function SettingsScreen() { - return ( - - Settings! - - ); -} - -export default function MyComponent() { - const [index, setIndex] = useState(0); - - const routes = [ - { key: 'home', title: 'Home', icon: 'home' }, - { key: 'settings', title: 'Settings', icon: 'cog' }, - ]; - - const renderScene = ({ route }) => { - switch (route.key) { - case 'home': - return ; - case 'settings': - return ; - default: - return null; - } - }; - - return ( - - {renderScene({ route: routes[index] })} - { - const newIndex = routes.findIndex((r) => r.key === route.key); - if (newIndex !== -1) { - setIndex(newIndex); - } - }} - renderIcon={({ route, color }) => ( - - )} - getLabelText={({ route }) => route.title} - /> - - ); -} -``` - -### with React Navigation - -\n Home!\n
\n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst MyTabs = createBottomTabNavigator({\n screenOptions: {\n animation: 'shift',\n },\n tabBar: ({ navigation, state, descriptors, insets }) => (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n ),\n screens: {\n Home: {\n screen: HomeScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n Settings: {\n screen: SettingsScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n },\n});\n\nconst Navigation = createStaticNavigation(MyTabs);\n\nexport default function App() {\n return (\n \n \n \n \n \n );\n}", - dynamic: - "import { Text, View } from 'react-native';\nimport { NavigationContainer, CommonActions } from '@react-navigation/native';\nimport { createBottomTabNavigator } from '@react-navigation/bottom-tabs';\nimport { Provider, BottomNavigation } from 'react-native-paper';\nimport MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst Tab = createBottomTabNavigator();\n\nexport default function App() {\n return (\n \n \n (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n )}>\n (\n \n ),\n }}\n />\n (\n \n ),\n }}\n />\n \n \n \n );\n}\n", - }, - }} -/> - -## Props - - - -
- -### shifting - -
- - - -
- -### labeled - -
- - - -
- -### compact - -
- - - -
- -### navigationState (required) - -
- - - -
- -### renderIcon - -
- - - -
- -### renderLabel - -
- - - -
- -### renderTouchable - -
- - - -
- -### getAccessibilityLabel - -
- - - -
- -### getBadge - -
- - - -
- -### getColor - -
- - - -
- -### getLabelText - -
- - - -
- -### getTestID - -
- - - -
- -### onTabPress (required) - -
- - - -
- -### onTabLongPress - -
- - - -
- -### activeColor - -
- - - -
- -### inactiveColor - -
- - - -
- -### animationEasing - -
- - - -
- -### keyboardHidesNavigationBar - -
- - - -
- -### safeAreaInsets - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### activeIndicatorStyle - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/BottomNavigation/_meta.json b/docs/5.x/docs/components/BottomNavigation/_meta.json deleted file mode 100644 index 94a0b010f3..0000000000 --- a/docs/5.x/docs/components/BottomNavigation/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "BottomNavigation", - "BottomNavigationBar" -] diff --git a/docs/5.x/docs/components/Button/Button.mdx b/docs/5.x/docs/components/Button/Button.mdx deleted file mode 100644 index 7ffde3a330..0000000000 --- a/docs/5.x/docs/components/Button/Button.mdx +++ /dev/null @@ -1,322 +0,0 @@ ---- -title: Button ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A button is component that the user can press to trigger an action. - - - -## Usage - -```js -import * as React from 'react'; -import { Button } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - -## Props - - - -
- -### mode - -
- - - -
- -### dark - -
- - - -
- -### compact - -
- - - -
- -### color Deprecated in v5.x - use `buttonColor` or `textColor` instead. - -
- - - -
- -### buttonColor - -
- - - -
- -### textColor - -
- - - -
- -### rippleColor - -
- - - -
- -### loading - -
- - - -
- -### icon - -
- - - -
- -### disabled - -
- - - -
- -### children (required) - -
- - - -
- -### uppercase - -
- - - -
- -### background - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### accessibilityHint - -
- - - -
- -### accessibilityRole - -
- - - -
- -### onPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### onLongPress - -
- - - -
- -### delayLongPress - -
- - - -
- -### contentStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### hitSlop - -
- - - -
- -### style - -
- - - -
- -### labelStyle - -
- - - -
- -### theme - -
- - - -
- -### touchableRef - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Button/_meta.json b/docs/5.x/docs/components/Button/_meta.json deleted file mode 100644 index a6d17d5990..0000000000 --- a/docs/5.x/docs/components/Button/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Button" -] diff --git a/docs/5.x/docs/components/Card/Card.mdx b/docs/5.x/docs/components/Card/Card.mdx deleted file mode 100644 index c5eedd570b..0000000000 --- a/docs/5.x/docs/components/Card/Card.mdx +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: Card ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A card is a sheet of material that serves as an entry point to more detailed information. - - - -## Usage - -```js -import * as React from 'react'; -import { Avatar, Button, Card, Text } from 'react-native-paper'; - -const LeftContent = (props) => ; - -const MyComponent = () => ( - - - - Card title - Card content - - - - - - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### mode - -
- - - -
- -### children (required) - -
- - - -
- -### onLongPress - -
- - - -
- -### onPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### delayLongPress - -
- - - -
- -### disabled - -
- - - -
- -### elevation - -
- - - -
- -### contentStyle - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### accessible - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Card/CardActions.mdx b/docs/5.x/docs/components/Card/CardActions.mdx deleted file mode 100644 index 0e0e2cb88a..0000000000 --- a/docs/5.x/docs/components/Card/CardActions.mdx +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Card.Actions ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a list of actions inside a Card. - - - -## Usage - -```js -import * as React from 'react'; -import { Card, Button } from 'react-native-paper'; - -const MyComponent = () => ( - - - - - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Card/CardContent.mdx b/docs/5.x/docs/components/Card/CardContent.mdx deleted file mode 100644 index 8013d61a8d..0000000000 --- a/docs/5.x/docs/components/Card/CardContent.mdx +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Card.Content ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show content inside a Card. - - - -## Usage - -```js -import * as React from 'react'; -import { Card, Text } from 'react-native-paper'; - -const MyComponent = () => ( - - - Card title - Card content - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Card/CardCover.mdx b/docs/5.x/docs/components/Card/CardCover.mdx deleted file mode 100644 index 258924e9a4..0000000000 --- a/docs/5.x/docs/components/Card/CardCover.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Card.Cover ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a cover image inside a Card. - - - -## Usage - -```js -import * as React from 'react'; -import { Card } from 'react-native-paper'; - -const MyComponent = () => ( - - - -); - -export default MyComponent; -``` - -## Props - -### Image props - - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Card/CardTitle.mdx b/docs/5.x/docs/components/Card/CardTitle.mdx deleted file mode 100644 index d29acd8f3f..0000000000 --- a/docs/5.x/docs/components/Card/CardTitle.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Card.Title ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a title, subtitle and an avatar inside a Card. - - - -## Usage - -```js -import * as React from 'react'; -import { Avatar, Card, IconButton } from 'react-native-paper'; - -const MyComponent = () => ( - } - right={(props) => ( - {}} /> - )} - /> -); - -export default MyComponent; -``` - -## Props - - - -
- -### title (required) - -
- - - -
- -### titleStyle - -
- - - -
- -### titleNumberOfLines - -
- - - -
- -### titleVariant Available in v5.x with theme version 3 - -
- - - -
- -### subtitle - -
- - - -
- -### subtitleStyle - -
- - - -
- -### subtitleNumberOfLines - -
- - - -
- -### subtitleVariant Available in v5.x with theme version 3 - -
- - - -
- -### left - -
- - - -
- -### leftStyle - -
- - - -
- -### right - -
- - - -
- -### rightStyle - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### subtitleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Card/_meta.json b/docs/5.x/docs/components/Card/_meta.json deleted file mode 100644 index 3b7edb1615..0000000000 --- a/docs/5.x/docs/components/Card/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - "Card", - "CardActions", - "CardContent", - "CardCover", - "CardTitle" -] diff --git a/docs/5.x/docs/components/Checkbox/Checkbox.mdx b/docs/5.x/docs/components/Checkbox/Checkbox.mdx deleted file mode 100644 index 469a1a4388..0000000000 --- a/docs/5.x/docs/components/Checkbox/Checkbox.mdx +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: Checkbox ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Checkboxes allow the selection of multiple options from a set. - - - -## Usage - -```js -import * as React from 'react'; -import { Checkbox } from 'react-native-paper'; - -const MyComponent = () => { - const [checked, setChecked] = React.useState(false); - - return ( - { - setChecked(!checked); - }} - /> - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### status (required) - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Checkbox/CheckboxAndroid.mdx b/docs/5.x/docs/components/Checkbox/CheckboxAndroid.mdx deleted file mode 100644 index 08f8502d73..0000000000 --- a/docs/5.x/docs/components/Checkbox/CheckboxAndroid.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Checkbox.Android ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - - -Checkboxes allow the selection of multiple options from a set. -This component follows platform guidelines for Android, but can be used -on any platform. - -## Props - -### TouchableRipple props - - - -
- -### status (required) - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Checkbox/CheckboxIOS.mdx b/docs/5.x/docs/components/Checkbox/CheckboxIOS.mdx deleted file mode 100644 index a669c3b08d..0000000000 --- a/docs/5.x/docs/components/Checkbox/CheckboxIOS.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Checkbox.IOS ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - - -Checkboxes allow the selection of multiple options from a set. -This component follows platform guidelines for iOS, but can be used -on any platform. - -## Props - -### TouchableRipple props - - - -
- -### status (required) - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Checkbox/CheckboxItem.mdx b/docs/5.x/docs/components/Checkbox/CheckboxItem.mdx deleted file mode 100644 index e89ac53743..0000000000 --- a/docs/5.x/docs/components/Checkbox/CheckboxItem.mdx +++ /dev/null @@ -1,191 +0,0 @@ ---- -title: Checkbox.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Checkbox } from 'react-native-paper'; - -const MyComponent = () => ( - - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### status (required) - -
- - - -
- -### disabled - -
- - - -
- -### label (required) - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### background - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### rippleColor - -
- - - -
- -### style - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### labelStyle - -
- - - -
- -### labelVariant Available in v5.x with theme version 3 - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### position - -
- - - -
- -### mode - -
- - - -
- -### hitSlop - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Checkbox/_meta.json b/docs/5.x/docs/components/Checkbox/_meta.json deleted file mode 100644 index f25fa6d271..0000000000 --- a/docs/5.x/docs/components/Checkbox/_meta.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - "Checkbox", - "CheckboxAndroid", - "CheckboxIOS", - "CheckboxItem" -] diff --git a/docs/5.x/docs/components/Chip/Chip.mdx b/docs/5.x/docs/components/Chip/Chip.mdx deleted file mode 100644 index cb0f4c80f8..0000000000 --- a/docs/5.x/docs/components/Chip/Chip.mdx +++ /dev/null @@ -1,292 +0,0 @@ ---- -title: Chip ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Chips are compact elements that can represent inputs, attributes, or actions. -They can have an icon or avatar on the left, and a close button icon on the right. -They are typically used to: - -
    -
  • Present multiple options
  • -
  • Represent attributes active or chosen
  • -
  • Present filter options
  • -
  • Trigger actions related to primary content
  • -
- - - -## Usage - -```js -import * as React from 'react'; -import { Chip } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')}> - Example Chip - -); - -export default MyComponent; -``` - -## Props - - - -
- -### mode - -
- - - -
- -### children (required) - -
- - - -
- -### icon - -
- - - -
- -### avatar - -
- - - -
- -### closeIcon - -
- - - -
- -### selected - -
- - - -
- -### selectedColor - -
- - - -
- -### showSelectedOverlay Available in v5.x with theme version 3 - -
- - - -
- -### showSelectedCheck - -
- - - -
- -### rippleColor - -
- - - -
- -### disabled - -
- - - -
- -### background - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### closeIconAccessibilityLabel - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### onClose - -
- - - -
- -### delayLongPress - -
- - - -
- -### compact Available in v5.x with theme version 3 - -
- - - -
- -### elevated Available in v5.x with theme version 3 - -
- - - -
- -### textStyle - -
- - - -
- -### style - -
- - - -
- -### hitSlop - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### ellipsizeMode - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### accessibilityRole - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Chip/_meta.json b/docs/5.x/docs/components/Chip/_meta.json deleted file mode 100644 index 02eb8c63e5..0000000000 --- a/docs/5.x/docs/components/Chip/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Chip" -] diff --git a/docs/5.x/docs/components/DataTable/DataTable.mdx b/docs/5.x/docs/components/DataTable/DataTable.mdx deleted file mode 100644 index c5a7790525..0000000000 --- a/docs/5.x/docs/components/DataTable/DataTable.mdx +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: DataTable ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Data tables allow displaying sets of data. - - - -## Usage - -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => { - const [page, setPage] = React.useState < number > 0; - const [numberOfItemsPerPageList] = React.useState([2, 3, 4]); - const [itemsPerPage, onItemsPerPageChange] = React.useState( - numberOfItemsPerPageList[0] - ); - - const [items] = React.useState([ - { - key: 1, - name: 'Cupcake', - calories: 356, - fat: 16, - }, - { - key: 2, - name: 'Eclair', - calories: 262, - fat: 16, - }, - { - key: 3, - name: 'Frozen yogurt', - calories: 159, - fat: 6, - }, - { - key: 4, - name: 'Gingerbread', - calories: 305, - fat: 3.7, - }, - ]); - - const from = page * itemsPerPage; - const to = Math.min((page + 1) * itemsPerPage, items.length); - - React.useEffect(() => { - setPage(0); - }, [itemsPerPage]); - - return ( - - - Dessert - Calories - Fat - - - {items.slice(from, to).map((item) => ( - - {item.name} - {item.calories} - {item.fat} - - ))} - - setPage(page)} - label={`${from + 1}-${to} of ${items.length}`} - numberOfItemsPerPageList={numberOfItemsPerPageList} - numberOfItemsPerPage={itemsPerPage} - onItemsPerPageChange={onItemsPerPageChange} - showFastPaginationControls - selectPageDropdownLabel={'Rows per page'} - /> - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - diff --git a/docs/5.x/docs/components/DataTable/DataTableCell.mdx b/docs/5.x/docs/components/DataTable/DataTableCell.mdx deleted file mode 100644 index d01389d5eb..0000000000 --- a/docs/5.x/docs/components/DataTable/DataTableCell.mdx +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: DataTable.Cell ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a single cell inside of a table. - - - -## Usage - -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - 1 - 2 - 3 - 4 - -); - -export default MyComponent; -``` - -If you want to support multiline text, please use View instead, as multiline text doesn't comply with -MD Guidelines (https://github.com/callstack/react-native-paper/issues/2381). - -## Props - -### TouchableRipple props - - - -
- -### children (required) - -
- - - -
- -### numeric - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### textStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/DataTable/DataTableHeader.mdx b/docs/5.x/docs/components/DataTable/DataTableHeader.mdx deleted file mode 100644 index 7ba602e8e2..0000000000 --- a/docs/5.x/docs/components/DataTable/DataTableHeader.mdx +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: DataTable.Header ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to display title in table header. - - - -## Usage - -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - - Dessert - Calories - Fat (g) - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/DataTable/DataTablePagination.mdx b/docs/5.x/docs/components/DataTable/DataTablePagination.mdx deleted file mode 100644 index 75cb47c01c..0000000000 --- a/docs/5.x/docs/components/DataTable/DataTablePagination.mdx +++ /dev/null @@ -1,206 +0,0 @@ ---- -title: DataTable.Pagination ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show pagination for data table. - - - -## Usage - -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const numberOfItemsPerPageList = [2, 3, 4]; - -const items = [ - { - key: 1, - name: 'Page 1', - }, - { - key: 2, - name: 'Page 2', - }, - { - key: 3, - name: 'Page 3', - }, -]; - -const MyComponent = () => { - const [page, setPage] = React.useState(0); - const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState( - numberOfItemsPerPageList[0] - ); - const from = page * numberOfItemsPerPage; - const to = Math.min((page + 1) * numberOfItemsPerPage, items.length); - - React.useEffect(() => { - setPage(0); - }, [numberOfItemsPerPage]); - - return ( - - setPage(page)} - label={`${from + 1}-${to} of ${items.length}`} - showFastPaginationControls - numberOfItemsPerPageList={numberOfItemsPerPageList} - numberOfItemsPerPage={numberOfItemsPerPage} - onItemsPerPageChange={onItemsPerPageChange} - selectPageDropdownLabel={'Rows per page'} - /> - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### page (required) - -
- - - -
- -### numberOfPages (required) - -
- - - -
- -### onPageChange (required) - -
- - - -
- -### showFastPaginationControls - -
- - - -
- -### paginationControlRippleColor - -
- - - -
- -### theme - -
- - - -
- -### numberOfItemsPerPage - -
- - - -
- -### numberOfItemsPerPageList - -
- - - -
- -### onItemsPerPageChange - -
- - - -
- -### dropdownItemRippleColor - -
- - - -
- -### selectPageDropdownRippleColor - -
- - - -
- -### selectPageDropdownLabel - -
- - - -
- -### selectPageDropdownAccessibilityLabel - -
- - - -
- -### label - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### style - -
- - - - - - - - diff --git a/docs/5.x/docs/components/DataTable/DataTableRow.mdx b/docs/5.x/docs/components/DataTable/DataTableRow.mdx deleted file mode 100644 index add98252f9..0000000000 --- a/docs/5.x/docs/components/DataTable/DataTableRow.mdx +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: DataTable.Row ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a single row inside of a table. - - - -## Usage - -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - 1 - 2 - 3 - 4 - -); - -export default MyComponent; -``` - -## Props - -### TouchableRipple props - - - -
- -### children (required) - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### pointerEvents - -
- - - - - - - - diff --git a/docs/5.x/docs/components/DataTable/DataTableTitle.mdx b/docs/5.x/docs/components/DataTable/DataTableTitle.mdx deleted file mode 100644 index beb8b7f84b..0000000000 --- a/docs/5.x/docs/components/DataTable/DataTableTitle.mdx +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: DataTable.Title ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to display title in table header. - - - -## Usage - -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - - Dessert - Calories - Fat (g) - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### numeric - -
- - - -
- -### sortDirection - -
- - - -
- -### numberOfLines - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### textStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/DataTable/_meta.json b/docs/5.x/docs/components/DataTable/_meta.json deleted file mode 100644 index aaed448e4d..0000000000 --- a/docs/5.x/docs/components/DataTable/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "DataTable", - "DataTableCell", - "DataTableHeader", - "DataTablePagination", - "DataTableRow", - "DataTableTitle" -] diff --git a/docs/5.x/docs/components/Dialog/Dialog.mdx b/docs/5.x/docs/components/Dialog/Dialog.mdx deleted file mode 100644 index 2daa359bdd..0000000000 --- a/docs/5.x/docs/components/Dialog/Dialog.mdx +++ /dev/null @@ -1,138 +0,0 @@ ---- -title: Dialog ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. -To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal/Portal) component. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { - Button, - Dialog, - Portal, - PaperProvider, - Text, -} from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const showDialog = () => setVisible(true); - - const hideDialog = () => setVisible(false); - - return ( - - - - - - Alert - - This is simple dialog - - - - - - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### dismissable - -
- - - -
- -### dismissableBackButton - -
- - - -
- -### onDismiss - -
- - - -
- -### visible - -
- - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Dialog/DialogActions.mdx b/docs/5.x/docs/components/Dialog/DialogActions.mdx deleted file mode 100644 index 79154eb08a..0000000000 --- a/docs/5.x/docs/components/Dialog/DialogActions.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Dialog.Actions ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a list of actions in a Dialog. - - - -## Usage - -```js -import * as React from 'react'; -import { Button, Dialog, Portal } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - - - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Dialog/DialogContent.mdx b/docs/5.x/docs/components/Dialog/DialogContent.mdx deleted file mode 100644 index df01fc5eba..0000000000 --- a/docs/5.x/docs/components/Dialog/DialogContent.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Dialog.Content ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show content in a Dialog. - - - -## Usage - -```js -import * as React from 'react'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - This is simple dialog - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Dialog/DialogIcon.mdx b/docs/5.x/docs/components/Dialog/DialogIcon.mdx deleted file mode 100644 index 8320997b6b..0000000000 --- a/docs/5.x/docs/components/Dialog/DialogIcon.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Dialog.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -@supported Available in v5.x with theme version 3 -A component to show an icon in a Dialog. - - - -## Usage - -```js -import * as React from 'react'; -import { StyleSheet } from 'react-native'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - This is a title - - This is simple dialog - - - - ); -}; - -const styles = StyleSheet.create({ - title: { - textAlign: 'center', - }, -}); - -export default MyComponent; -``` - -## Props - - - -
- -### color - -
- - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Dialog/DialogScrollArea.mdx b/docs/5.x/docs/components/Dialog/DialogScrollArea.mdx deleted file mode 100644 index 4bfd363ebe..0000000000 --- a/docs/5.x/docs/components/Dialog/DialogScrollArea.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Dialog.ScrollArea ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a scrollable content in a Dialog. The component only provides appropriate styling. -For the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement. - - - -## Usage - -```js -import * as React from 'react'; -import { ScrollView } from 'react-native'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - - This is a scrollable area - - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Dialog/DialogTitle.mdx b/docs/5.x/docs/components/Dialog/DialogTitle.mdx deleted file mode 100644 index 9be26eb11e..0000000000 --- a/docs/5.x/docs/components/Dialog/DialogTitle.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Dialog.Title ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a title in a Dialog. - - - -## Usage - -```js -import * as React from 'react'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - This is a title - - This is simple dialog - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Dialog/_meta.json b/docs/5.x/docs/components/Dialog/_meta.json deleted file mode 100644 index 81373811ab..0000000000 --- a/docs/5.x/docs/components/Dialog/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "Dialog", - "DialogActions", - "DialogContent", - "DialogIcon", - "DialogScrollArea", - "DialogTitle" -] diff --git a/docs/5.x/docs/components/Divider.mdx b/docs/5.x/docs/components/Divider.mdx deleted file mode 100644 index b713e2c1ae..0000000000 --- a/docs/5.x/docs/components/Divider.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Divider ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A divider is a thin, lightweight separator that groups content in lists and page layouts. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Divider, Text } from 'react-native-paper'; - -const MyComponent = () => ( - - Lemon - - Mango - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### leftInset Renamed from 'inset' to 'leftInset` in v5.x - -
- - - -
- -### horizontalInset Available in v5.x with theme version 3 - -
- - - -
- -### bold Available in v5.x with theme version 3 - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Drawer/DrawerCollapsedItem.mdx b/docs/5.x/docs/components/Drawer/DrawerCollapsedItem.mdx deleted file mode 100644 index 810fe2f811..0000000000 --- a/docs/5.x/docs/components/Drawer/DrawerCollapsedItem.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: Drawer.CollapsedItem ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Note: Available in v5.x with theme version 3 - -Collapsed component used to show an action item with an icon and optionally label in a navigation drawer. - - - -## Usage - -```js -import * as React from 'react'; -import { Drawer } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - -## Props - - - -
- -### label - -
- - - -
- -### badge - -
- - - -
- -### disabled - -
- - - -
- -### focusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x - -
- - - -
- -### unfocusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x - -
- - - -
- -### active - -
- - - -
- -### onPress - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Drawer/DrawerItem.mdx b/docs/5.x/docs/components/Drawer/DrawerItem.mdx deleted file mode 100644 index d54d7bc5e0..0000000000 --- a/docs/5.x/docs/components/Drawer/DrawerItem.mdx +++ /dev/null @@ -1,155 +0,0 @@ ---- -title: Drawer.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to show an action item with an icon and a label in a navigation drawer. - - - -## Usage - -```js -import * as React from 'react'; -import { Drawer } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - -## Props - - - -
- -### label (required) - -
- - - -
- -### icon - -
- - - -
- -### active - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### background - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### right - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### rippleColor - -
- - - -
- -### hitSlop - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Drawer/DrawerSection.mdx b/docs/5.x/docs/components/Drawer/DrawerSection.mdx deleted file mode 100644 index c7d7510f77..0000000000 --- a/docs/5.x/docs/components/Drawer/DrawerSection.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: Drawer.Section ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to group content inside a navigation drawer. - - - -## Usage - -```js -import * as React from 'react'; -import { Drawer } from 'react-native-paper'; - -const MyComponent = () => { - const [active, setActive] = React.useState(''); - - return ( - - setActive('first')} - /> - setActive('second')} - /> - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### title - -
- - - -
- -### children (required) - -
- - - -
- -### showDivider - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Drawer/_meta.json b/docs/5.x/docs/components/Drawer/_meta.json deleted file mode 100644 index 0099a35f77..0000000000 --- a/docs/5.x/docs/components/Drawer/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "DrawerCollapsedItem", - "DrawerItem", - "DrawerSection" -] diff --git a/docs/5.x/docs/components/FAB/AnimatedFAB.mdx b/docs/5.x/docs/components/FAB/AnimatedFAB.mdx deleted file mode 100644 index 363ec41f2c..0000000000 --- a/docs/5.x/docs/components/FAB/AnimatedFAB.mdx +++ /dev/null @@ -1,299 +0,0 @@ ---- -title: AnimatedFAB ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -An animated, extending horizontally floating action button represents the primary action in an application. - - - -## Usage - -```js -import React from 'react'; -import { - StyleProp, - ViewStyle, - Animated, - StyleSheet, - Platform, - ScrollView, - Text, - SafeAreaView, - I18nManager, -} from 'react-native'; -import { AnimatedFAB } from 'react-native-paper'; - -const MyComponent = ({ - animatedValue, - visible, - extended, - label, - animateFrom, - style, - iconMode, -}) => { - const [isExtended, setIsExtended] = React.useState(true); - - const isIOS = Platform.OS === 'ios'; - - const onScroll = ({ nativeEvent }) => { - const currentScrollPosition = - Math.floor(nativeEvent?.contentOffset?.y) ?? 0; - - setIsExtended(currentScrollPosition <= 0); - }; - - const fabStyle = { [animateFrom]: 16 }; - - return ( - - - {[...new Array(100).keys()].map((_, i) => ( - {i} - ))} - - console.log('Pressed')} - visible={visible} - animateFrom={'right'} - iconMode={'static'} - style={[styles.fabStyle, style, fabStyle]} - /> - - ); -}; - -export default MyComponent; - -const styles = StyleSheet.create({ - container: { - flexGrow: 1, - }, - fabStyle: { - bottom: 16, - right: 16, - position: 'absolute', - }, -}); -``` - -## Props - - - -
- -### icon (required) - -
- - - -
- -### label (required) - -
- - - -
- -### uppercase - -
- - - -
- -### background - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### accessibilityState - -
- - - -
- -### color - -
- - - -
- -### rippleColor - -
- - - -
- -### disabled - -
- - - -
- -### visible - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### delayLongPress - -
- - - -
- -### iconMode - -
- - - -
- -### animateFrom - -
- - - -
- -### extended - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### variant Available in v5.x with theme version 3 - -
- - - -
- -### style - -
- - - -
- -### hitSlop - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/FAB/FAB.mdx b/docs/5.x/docs/components/FAB/FAB.mdx deleted file mode 100644 index 71e55c0553..0000000000 --- a/docs/5.x/docs/components/FAB/FAB.mdx +++ /dev/null @@ -1,279 +0,0 @@ ---- -title: FAB ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A floating action button represents the primary action on a screen. It appears in front of all screen content. - - - -## Usage - -```js -import * as React from 'react'; -import { StyleSheet } from 'react-native'; -import { FAB } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')} /> -); - -const styles = StyleSheet.create({ - fab: { - position: 'absolute', - margin: 16, - right: 0, - bottom: 0, - }, -}); - -export default MyComponent; -``` - -## Props - - - -
- -### icon - -
- - - -
- -### label - -
- - - -
- -### uppercase - -
- - - -
- -### background - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### accessibilityState - -
- - - -
- -### animated - -
- - - -
- -### small Deprecated in v.5x - use prop size="small". - -
- - - -
- -### color - -
- - - -
- -### rippleColor - -
- - - -
- -### disabled - -
- - - -
- -### visible - -
- - - -
- -### loading - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### delayLongPress - -
- - - -
- -### size Available in v5.x with theme version 3 - -
- - - -
- -### customSize - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### variant Available in v5.x with theme version 3 - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### ref - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/FAB/FABGroup.mdx b/docs/5.x/docs/components/FAB/FABGroup.mdx deleted file mode 100644 index bf10f759b3..0000000000 --- a/docs/5.x/docs/components/FAB/FABGroup.mdx +++ /dev/null @@ -1,237 +0,0 @@ ---- -title: FAB.Group ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to display a stack of FABs with related actions in a speed dial. -To render the group above other components, you'll need to wrap it with the [`Portal`](../Portal/Portal) component. - - - -## Usage - -```js -import * as React from 'react'; -import { FAB, Portal, PaperProvider } from 'react-native-paper'; - -const MyComponent = () => { - const [state, setState] = React.useState({ open: false }); - - const onStateChange = ({ open }) => setState({ open }); - - const { open } = state; - - return ( - - - console.log('Pressed add') }, - { - icon: 'star', - label: 'Star', - onPress: () => console.log('Pressed star'), - }, - { - icon: 'email', - label: 'Email', - onPress: () => console.log('Pressed email'), - }, - { - icon: 'bell', - label: 'Remind', - onPress: () => console.log('Pressed notifications'), - }, - ]} - onStateChange={onStateChange} - onPress={() => { - if (open) { - // do something if the speed dial is open - } - }} - /> - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### actions (required) - -
- - - -
- -### icon (required) - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### color - -
- - - -
- -### backdropColor - -
- - - -
- -### rippleColor - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### toggleStackOnLongPress - -
- - - -
- -### delayLongPress - -
- - - -
- -### enableLongPressWhenStackOpened - -
- - - -
- -### open (required) - -
- - - -
- -### onStateChange (required) - -
- - - -
- -### visible (required) - -
- - - -
- -### style - -
- - - -
- -### fabStyle - -
- - - -
- -### variant Available in v5.x with theme version 3 - -
- - - -
- -### theme - -
- - - -
- -### label - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/FAB/_meta.json b/docs/5.x/docs/components/FAB/_meta.json deleted file mode 100644 index 57c6fd81f3..0000000000 --- a/docs/5.x/docs/components/FAB/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "AnimatedFAB", - "FAB", - "FABGroup" -] diff --git a/docs/5.x/docs/components/HelperText/HelperText.mdx b/docs/5.x/docs/components/HelperText/HelperText.mdx deleted file mode 100644 index 5de4479130..0000000000 --- a/docs/5.x/docs/components/HelperText/HelperText.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: HelperText ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Helper text is used in conjuction with input elements to provide additional hints for the user. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { HelperText, TextInput } from 'react-native-paper'; - -const MyComponent = () => { - const [text, setText] = React.useState(''); - - const onChangeText = (text) => setText(text); - - const hasErrors = () => { - return !text.includes('@'); - }; - - return ( - - - - Email address is invalid! - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### type - -
- - - -
- -### children (required) - -
- - - -
- -### visible - -
- - - -
- -### padding - -
- - - -
- -### disabled - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/HelperText/_meta.json b/docs/5.x/docs/components/HelperText/_meta.json deleted file mode 100644 index 869755ba04..0000000000 --- a/docs/5.x/docs/components/HelperText/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "HelperText" -] diff --git a/docs/5.x/docs/components/Icon.mdx b/docs/5.x/docs/components/Icon.mdx deleted file mode 100644 index 2cc563998c..0000000000 --- a/docs/5.x/docs/components/Icon.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -An icon component which renders icon from vector library. - - - -## Usage - -```js -import * as React from 'react'; -import { Icon, MD3Colors } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - -## Props - - - -
- -### size (required) - -
- - - -
- -### allowFontScaling - -
- - - -
- -### source (required) - -
- - - -
- -### color - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/IconButton/IconButton.mdx b/docs/5.x/docs/components/IconButton/IconButton.mdx deleted file mode 100644 index 68fee19aeb..0000000000 --- a/docs/5.x/docs/components/IconButton/IconButton.mdx +++ /dev/null @@ -1,241 +0,0 @@ ---- -title: IconButton ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -An icon button is a button which displays only an icon without a label. - - - -## Usage - -```js -import * as React from 'react'; -import { IconButton, MD3Colors } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')} - /> -); - -export default MyComponent; -``` - -## Props - -### TouchableRipple props - - - -
- -### icon (required) - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### iconColor Renamed from 'color' to 'iconColor' in v5.x - -
- - - -
- -### containerColor - -
- - - -
- -### rippleColor - -
- - - -
- -### selected Available in v5.x with theme version 3 - -
- - - -
- -### size - -
- - - -
- -### disabled - -
- - - -
- -### animated - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### contentStyle - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - -
- -### loading - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/IconButton/_meta.json b/docs/5.x/docs/components/IconButton/_meta.json deleted file mode 100644 index 00d733ae01..0000000000 --- a/docs/5.x/docs/components/IconButton/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "IconButton" -] diff --git a/docs/5.x/docs/components/List/ListAccordion.mdx b/docs/5.x/docs/components/List/ListAccordion.mdx deleted file mode 100644 index 6258e15ec9..0000000000 --- a/docs/5.x/docs/components/List/ListAccordion.mdx +++ /dev/null @@ -1,274 +0,0 @@ ---- -title: List.Accordion ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display an expandable list item. - - - -## Usage - -```js -import * as React from 'react'; -import { List } from 'react-native-paper'; - -const MyComponent = () => { - const [expanded, setExpanded] = React.useState(true); - - const handlePress = () => setExpanded(!expanded); - - return ( - - } - > - - - - - } - expanded={expanded} - onPress={handlePress} - > - - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### title (required) - -
- - - -
- -### description - -
- - - -
- -### left - -
- - - -
- -### right - -
- - - -
- -### expanded - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### delayLongPress - -
- - - -
- -### children (required) - -
- - - -
- -### theme - -
- - - -
- -### background - -
- - - -
- -### style - -
- - - -
- -### containerStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### titleStyle - -
- - - -
- -### descriptionStyle - -
- - - -
- -### rippleColor - -
- - - -
- -### titleNumberOfLines - -
- - - -
- -### descriptionNumberOfLines - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### descriptionMaxFontSizeMultiplier - -
- - - -
- -### id - -
- - - -
- -### testID - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### pointerEvents - -
- - - -
- -### hitSlop - -
- - - - - - - - diff --git a/docs/5.x/docs/components/List/ListAccordionGroup.mdx b/docs/5.x/docs/components/List/ListAccordionGroup.mdx deleted file mode 100644 index 426430129c..0000000000 --- a/docs/5.x/docs/components/List/ListAccordionGroup.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: List.AccordionGroup ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work. -List.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version. -At most one Accordion can be expanded at a given time. - - - -## Usage - -```js -import * as React from 'react'; -import { View, Text } from 'react-native'; -import { List } from 'react-native-paper'; - -const MyComponent = () => ( - - - - - - - - - - List.Accordion can be wrapped because implementation uses React.Context. - - - - - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### onAccordionPress - -
- - - -
- -### expandedId - -
- - - -
- -### children (required) - -
- - - - - - - - diff --git a/docs/5.x/docs/components/List/ListIcon.mdx b/docs/5.x/docs/components/List/ListIcon.mdx deleted file mode 100644 index 78ae13ec48..0000000000 --- a/docs/5.x/docs/components/List/ListIcon.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: List.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show an icon in a list item. - - - -## Usage - -```js -import * as React from 'react'; -import { List, MD3Colors } from 'react-native-paper'; - -const MyComponent = () => ( - <> - - - - -); - -export default MyComponent; -``` - -## Props - - - -
- -### icon (required) - -
- - - -
- -### color - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/List/ListItem.mdx b/docs/5.x/docs/components/List/ListItem.mdx deleted file mode 100644 index 7720440943..0000000000 --- a/docs/5.x/docs/components/List/ListItem.mdx +++ /dev/null @@ -1,195 +0,0 @@ ---- -title: List.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show tiles inside a List. - - - -## Usage - -```js -import * as React from 'react'; -import { List } from 'react-native-paper'; - -const MyComponent = () => ( - } - /> -); - -export default MyComponent; -``` - -## Props - -### TouchableRipple props - - - -
- -### title (required) - -
- - - -
- -### description - -
- - - -
- -### left - -
- - - -
- -### right - -
- - - -
- -### onPress - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - -
- -### containerStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### titleStyle - -
- - - -
- -### descriptionStyle - -
- - - -
- -### titleNumberOfLines - -
- - - -
- -### descriptionNumberOfLines - -
- - - -
- -### titleEllipsizeMode - -
- - - -
- -### descriptionEllipsizeMode - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### descriptionMaxFontSizeMultiplier - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/List/ListSection.mdx b/docs/5.x/docs/components/List/ListSection.mdx deleted file mode 100644 index 918091af2e..0000000000 --- a/docs/5.x/docs/components/List/ListSection.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: List.Section ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to group list items. - - - -## Usage - -```js -import * as React from 'react'; -import { List, MD3Colors } from 'react-native-paper'; - -const MyComponent = () => ( - - Some title - } /> - } - /> - -); - -export default MyComponent; -``` - -## Props - - - -
- -### title - -
- - - -
- -### children (required) - -
- - - -
- -### theme - -
- - - -
- -### titleStyle - -
- - - -
- -### style - -
- - - - - - - - diff --git a/docs/5.x/docs/components/List/ListSubheader.mdx b/docs/5.x/docs/components/List/ListSubheader.mdx deleted file mode 100644 index ad4a0ec57f..0000000000 --- a/docs/5.x/docs/components/List/ListSubheader.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: List.Subheader ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display a header in lists. - - - -## Usage - -```js -import * as React from 'react'; -import { List } from 'react-native-paper'; - -const MyComponent = () => My List Title; - -export default MyComponent; -``` - -## Props - - - -
- -### theme - -
- - - -
- -### style - -
- - - -
- -### maxFontSizeMultiplier - -
- - - - - - - - diff --git a/docs/5.x/docs/components/List/_meta.json b/docs/5.x/docs/components/List/_meta.json deleted file mode 100644 index 4ba864114c..0000000000 --- a/docs/5.x/docs/components/List/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "ListAccordion", - "ListAccordionGroup", - "ListIcon", - "ListItem", - "ListSection", - "ListSubheader" -] diff --git a/docs/5.x/docs/components/Menu/Menu.mdx b/docs/5.x/docs/components/Menu/Menu.mdx deleted file mode 100644 index 0793840260..0000000000 --- a/docs/5.x/docs/components/Menu/Menu.mdx +++ /dev/null @@ -1,194 +0,0 @@ ---- -title: Menu ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Button, Menu, Divider, PaperProvider } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const openMenu = () => setVisible(true); - - const closeMenu = () => setVisible(false); - - return ( - - - Show menu} - > - {}} title="Item 1" /> - {}} title="Item 2" /> - - {}} title="Item 3" /> - - - - ); -}; - -export default MyComponent; -``` - -### Note - -When using `Menu` within a React Native's `Modal` component, you need to wrap all -`Modal` contents within a `PaperProvider` in order for the menu to show. This -wrapping is not necessary if you use Paper's `Modal` instead. - -## Props - - - -
- -### visible (required) - -
- - - -
- -### anchor (required) - -
- - - -
- -### anchorPosition - -
- - - -
- -### statusBarHeight - -
- - - -
- -### onDismiss - -
- - - -
- -### overlayAccessibilityLabel - -
- - - -
- -### children (required) - -
- - - -
- -### contentStyle - -
- - - -
- -### style - -
- - - -
- -### elevation - -
- - - -
- -### mode - -
- - - -
- -### theme - -
- - - -
- -### keyboardShouldPersistTaps - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Menu/MenuItem.mdx b/docs/5.x/docs/components/Menu/MenuItem.mdx deleted file mode 100644 index aa663294b0..0000000000 --- a/docs/5.x/docs/components/Menu/MenuItem.mdx +++ /dev/null @@ -1,211 +0,0 @@ ---- -title: Menu.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a single list item inside a Menu. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Menu } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} title="Redo" /> - {}} title="Undo" /> - {}} - title="Cut" - disabled - /> - {}} - title="Copy" - disabled - /> - {}} title="Paste" /> - -); - -export default MyComponent; -``` - -## Props - - - -
- -### title (required) - -
- - - -
- -### leadingIcon Renamed from 'icon' to 'leadingIcon' in v5.x - -
- - - -
- -### trailingIcon Available in v5.x with theme version 3 - -
- - - -
- -### disabled - -
- - - -
- -### dense Available in v5.x with theme version 3 - -
- - - -
- -### background - -
- - - -
- -### onPress - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### containerStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### titleStyle - -
- - - -
- -### rippleColor - -
- - - -
- -### theme - -
- - - -
- -### hitSlop - -
- - - -
- -### testID - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### accessibilityState - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Menu/_meta.json b/docs/5.x/docs/components/Menu/_meta.json deleted file mode 100644 index 50192eb25e..0000000000 --- a/docs/5.x/docs/components/Menu/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "Menu", - "MenuItem" -] diff --git a/docs/5.x/docs/components/Modal.mdx b/docs/5.x/docs/components/Modal.mdx deleted file mode 100644 index 7a1f5c8a18..0000000000 --- a/docs/5.x/docs/components/Modal.mdx +++ /dev/null @@ -1,144 +0,0 @@ ---- -title: Modal ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -The Modal component is a simple way to present content above an enclosing view. -To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal/Portal) component. -Note that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal. - - - -## Usage - -```js -import * as React from 'react'; -import { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const showModal = () => setVisible(true); - const hideModal = () => setVisible(false); - const containerStyle = { backgroundColor: 'white', padding: 20 }; - - return ( - - - - Example Modal. Click outside this area to dismiss. - - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### dismissable - -
- - - -
- -### dismissableBackButton - -
- - - -
- -### onDismiss - -
- - - -
- -### overlayAccessibilityLabel - -
- - - -
- -### visible - -
- - - -
- -### children (required) - -
- - - -
- -### contentContainerStyle - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Portal/Portal.mdx b/docs/5.x/docs/components/Portal/Portal.mdx deleted file mode 100644 index ebc00e8c26..0000000000 --- a/docs/5.x/docs/components/Portal/Portal.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Portal ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Portal allows rendering a component at a different place in the parent tree. -You can use it to render content which should appear above other elements, similar to `Modal`. -It requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree. -Note that if you're using the `Provider` component, this already includes a `Portal.Host`. - - - -## Usage - -```js -import * as React from 'react'; -import { Portal, Text } from 'react-native-paper'; - -const MyComponent = () => ( - - This is rendered at a different place - -); - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### theme (required) - -
- - - -## More Examples - -
- Toggle to grab more examples - -
- - - - diff --git a/docs/5.x/docs/components/Portal/PortalHost.mdx b/docs/5.x/docs/components/Portal/PortalHost.mdx deleted file mode 100644 index 2418f6efe9..0000000000 --- a/docs/5.x/docs/components/Portal/PortalHost.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Portal.Host ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Portal host renders all of its children `Portal` elements. -For example, you can wrap a screen in `Portal.Host` to render items above the screen. -If you're using the `Provider` component, it already includes `Portal.Host`. - - - -## Usage - -```js -import * as React from 'react'; -import { Text } from 'react-native'; -import { Portal } from 'react-native-paper'; - -const MyComponent = () => ( - - Content of the app - -); - -export default MyComponent; -``` - -Here any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`. - -## Props - - - -
- -### children (required) - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Portal/_meta.json b/docs/5.x/docs/components/Portal/_meta.json deleted file mode 100644 index 6e5b7b374b..0000000000 --- a/docs/5.x/docs/components/Portal/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "Portal", - "PortalHost" -] diff --git a/docs/5.x/docs/components/ProgressBar.mdx b/docs/5.x/docs/components/ProgressBar.mdx deleted file mode 100644 index e539839345..0000000000 --- a/docs/5.x/docs/components/ProgressBar.mdx +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: ProgressBar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Progress bar is an indicator used to present progress of some activity in the app. - - - -## Usage - -```js -import * as React from 'react'; -import { ProgressBar, MD3Colors } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - -## Props - - - -
- -### animatedValue - -
- - - -
- -### progress - -
- - - -
- -### color - -
- - - -
- -### indeterminate - -
- - - -
- -### visible - -
- - - -
- -### fillStyle - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/RadioButton/RadioButton.mdx b/docs/5.x/docs/components/RadioButton/RadioButton.mdx deleted file mode 100644 index d5d9d7b1a8..0000000000 --- a/docs/5.x/docs/components/RadioButton/RadioButton.mdx +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: RadioButton ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Radio buttons allow the selection a single option from a set. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { RadioButton } from 'react-native-paper'; - -const MyComponent = () => { - const [checked, setChecked] = React.useState('first'); - - return ( - - setChecked('first')} - /> - setChecked('second')} - /> - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### value (required) - -
- - - -
- -### status - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/RadioButton/RadioButtonAndroid.mdx b/docs/5.x/docs/components/RadioButton/RadioButtonAndroid.mdx deleted file mode 100644 index 075bfd8f4b..0000000000 --- a/docs/5.x/docs/components/RadioButton/RadioButtonAndroid.mdx +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: RadioButton.Android ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - - -Radio buttons allow the selection a single option from a set. -This component follows platform guidelines for Android, but can be used -on any platform. - -## Props - -### TouchableRipple props - - - -
- -### value (required) - -
- - - -
- -### status - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/RadioButton/RadioButtonGroup.mdx b/docs/5.x/docs/components/RadioButton/RadioButtonGroup.mdx deleted file mode 100644 index f6e562f04a..0000000000 --- a/docs/5.x/docs/components/RadioButton/RadioButtonGroup.mdx +++ /dev/null @@ -1,82 +0,0 @@ ---- -title: RadioButton.Group ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Radio button group allows to control a group of radio buttons. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { RadioButton, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('first'); - - return ( - setValue(newValue)} - value={value} - > - - First - - - - Second - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### onValueChange (required) - -
- - - -
- -### value (required) - -
- - - -
- -### children (required) - -
- - - - - - - - diff --git a/docs/5.x/docs/components/RadioButton/RadioButtonIOS.mdx b/docs/5.x/docs/components/RadioButton/RadioButtonIOS.mdx deleted file mode 100644 index 75898f44ed..0000000000 --- a/docs/5.x/docs/components/RadioButton/RadioButtonIOS.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: RadioButton.IOS ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - - -Radio buttons allow the selection a single option from a set. -This component follows platform guidelines for iOS, but can be used -on any platform. - -## Props - -### TouchableRipple props - - - -
- -### value (required) - -
- - - -
- -### status - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/RadioButton/RadioButtonItem.mdx b/docs/5.x/docs/components/RadioButton/RadioButtonItem.mdx deleted file mode 100644 index 26b5d45c14..0000000000 --- a/docs/5.x/docs/components/RadioButton/RadioButtonItem.mdx +++ /dev/null @@ -1,203 +0,0 @@ ---- -title: RadioButton.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton. - - - -## Usage - -```js -import * as React from 'react'; -import { RadioButton } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('first'); - - return ( - setValue(value)} value={value}> - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### value (required) - -
- - - -
- -### label (required) - -
- - - -
- -### disabled - -
- - - -
- -### background - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### rippleColor - -
- - - -
- -### status - -
- - - -
- -### style - -
- - - -
- -### labelStyle - -
- - - -
- -### labelVariant Available in v5.x with theme version 3 - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### mode - -
- - - -
- -### position - -
- - - -
- -### hitSlop - -
- - - - - - - - diff --git a/docs/5.x/docs/components/RadioButton/_meta.json b/docs/5.x/docs/components/RadioButton/_meta.json deleted file mode 100644 index f9c20c781a..0000000000 --- a/docs/5.x/docs/components/RadioButton/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - "RadioButton", - "RadioButtonAndroid", - "RadioButtonGroup", - "RadioButtonIOS", - "RadioButtonItem" -] diff --git a/docs/5.x/docs/components/Searchbar.mdx b/docs/5.x/docs/components/Searchbar.mdx deleted file mode 100644 index 8cac720b1c..0000000000 --- a/docs/5.x/docs/components/Searchbar.mdx +++ /dev/null @@ -1,264 +0,0 @@ ---- -title: Searchbar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Searchbar is a simple input box where users can type search queries. - - - -## Usage - -```js -import * as React from 'react'; -import { Searchbar } from 'react-native-paper'; - -const MyComponent = () => { - const [searchQuery, setSearchQuery] = React.useState(''); - - return ( - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### placeholder - -
- - - -
- -### value (required) - -
- - - -
- -### onChangeText - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### icon - -
- - - -
- -### iconColor - -
- - - -
- -### rippleColor - -
- - - -
- -### onIconPress - -
- - - -
- -### onClearIconPress - -
- - - -
- -### searchAccessibilityLabel - -
- - - -
- -### clearIcon - -
- - - -
- -### clearAccessibilityLabel - -
- - - -
- -### traileringIcon Available in v5.x with theme version 3 - -
- - - -
- -### traileringIconColor Available in v5.x with theme version 3 - -
- - - -
- -### traileringRippleColor Available in v5.x with theme version 3 - -
- - - -
- -### onTraileringIconPress Available in v5.x with theme version 3 - -
- - - -
- -### traileringIconAccessibilityLabel - -
- - - -
- -### right Available in v5.x with theme version 3 - -
- - - -
- -### showDivider Available in v5.x with theme version 3 - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### inputStyle - -
- - - -
- -### style - -
- - - -
- -### loading - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/SegmentedButtons/SegmentedButtons.mdx b/docs/5.x/docs/components/SegmentedButtons/SegmentedButtons.mdx deleted file mode 100644 index 0f813befb2..0000000000 --- a/docs/5.x/docs/components/SegmentedButtons/SegmentedButtons.mdx +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: SegmentedButtons ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Segmented buttons can be used to select options, switch views or sort elements. - - - -## Usage - -```js -import * as React from 'react'; -import { SafeAreaView, StyleSheet } from 'react-native'; -import { SegmentedButtons } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState(''); - - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - }, -}); - -export default MyComponent; -``` - -## Props - - - -
- -### buttons (required) - -
- - - -
- -### density - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/SegmentedButtons/_meta.json b/docs/5.x/docs/components/SegmentedButtons/_meta.json deleted file mode 100644 index 85b4317790..0000000000 --- a/docs/5.x/docs/components/SegmentedButtons/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "SegmentedButtons" -] diff --git a/docs/5.x/docs/components/Snackbar.mdx b/docs/5.x/docs/components/Snackbar.mdx deleted file mode 100644 index 2e21df5dfc..0000000000 --- a/docs/5.x/docs/components/Snackbar.mdx +++ /dev/null @@ -1,220 +0,0 @@ ---- -title: Snackbar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped. - -Note: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the "More Examples` section. - - - -## Usage - -```js -import * as React from 'react'; -import { View, StyleSheet } from 'react-native'; -import { Button, Snackbar } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const onToggleSnackBar = () => setVisible(!visible); - - const onDismissSnackBar = () => setVisible(false); - - return ( - - - { - // Do something - }, - }} - > - Hey there! I'm a Snackbar. - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'space-between', - }, -}); - -export default MyComponent; -``` - -## Props - - - -
- -### visible (required) - -
- - - -
- -### action - -
- - - -
- -### icon Available in v5.x with theme version 3 - -
- - - -
- -### rippleColor Available in v5.x with theme version 3 - -
- - - -
- -### onIconPress Available in v5.x with theme version 3 - -
- - - -
- -### iconAccessibilityLabel Available in v5.x with theme version 3 - -
- - - -
- -### duration - -
- - - -
- -### onDismiss (required) - -
- - - -
- -### children (required) - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### wrapperStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -## More Examples - -
- Toggle to grab more examples - -
- -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Surface.mdx b/docs/5.x/docs/components/Surface.mdx deleted file mode 100644 index e25bbfded1..0000000000 --- a/docs/5.x/docs/components/Surface.mdx +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Surface ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Surface is a basic container that can give depth to an element with elevation shadow. -On dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface. -See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. -Overlay and shadow can be applied by specifying the `elevation` property both on Android and iOS. - - - -## Usage - -```js -import * as React from 'react'; -import { Surface, Text } from 'react-native-paper'; -import { StyleSheet } from 'react-native'; - -const MyComponent = () => ( - - Surface - -); - -export default MyComponent; - -const styles = StyleSheet.create({ - surface: { - padding: 8, - height: 80, - width: 80, - alignItems: 'center', - justifyContent: 'center', - }, -}); -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### ref - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Switch/Switch.mdx b/docs/5.x/docs/components/Switch/Switch.mdx deleted file mode 100644 index 7a5dac1ddb..0000000000 --- a/docs/5.x/docs/components/Switch/Switch.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Switch ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Switch is a visual toggle between two mutually exclusive states — on and off. - - - -## Usage - -```js -import * as React from 'react'; -import { Switch } from 'react-native-paper'; - -const MyComponent = () => { - const [isSwitchOn, setIsSwitchOn] = React.useState(false); - - const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn); - - return ; -}; - -export default MyComponent; -``` - -## Props - - - -
- -### disabled - -
- - - -
- -### value - -
- - - -
- -### color - -
- - - -
- -### onValueChange - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/Switch/_meta.json b/docs/5.x/docs/components/Switch/_meta.json deleted file mode 100644 index 3bc6b9e49c..0000000000 --- a/docs/5.x/docs/components/Switch/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Switch" -] diff --git a/docs/5.x/docs/components/Text/Text.mdx b/docs/5.x/docs/components/Text/Text.mdx deleted file mode 100644 index 22dd4f4315..0000000000 --- a/docs/5.x/docs/components/Text/Text.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Text ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Typography component showing styles complied with passed `variant` prop and supported by the type system. - - - -## Usage - -```js -import * as React from 'react'; -import { Text } from 'react-native-paper'; - -const MyComponent = () => ( - <> - Display Large - Display Medium - Display small - - Headline Large - Headline Medium - Headline Small - - Title Large - Title Medium - Title Small - - Body Large - Body Medium - Body Small - - Label Large - Label Medium - Label Small - -); - -export default MyComponent; -``` - -## Props - -### Text props - - - -
- -### variant Available in v5.x with theme version 3 - -
- - - -
- -### children (required) - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Text/_meta.json b/docs/5.x/docs/components/Text/_meta.json deleted file mode 100644 index 2837ff31af..0000000000 --- a/docs/5.x/docs/components/Text/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Text" -] diff --git a/docs/5.x/docs/components/TextInput/TextInput.mdx b/docs/5.x/docs/components/TextInput/TextInput.mdx deleted file mode 100644 index 7d070f463c..0000000000 --- a/docs/5.x/docs/components/TextInput/TextInput.mdx +++ /dev/null @@ -1,304 +0,0 @@ ---- -title: TextInput ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to allow users to input text. - - - -## Usage - -```js -import * as React from 'react'; -import { TextInput } from 'react-native-paper'; - -const MyComponent = () => { - const [text, setText] = React.useState(''); - - return ( - setText(text)} - /> - ); -}; - -export default MyComponent; -``` - -## Props - -### TextInput props - - - -
- -### mode - -
- - - -
- -### left - -
- - - -
- -### right - -
- - - -
- -### disabled - -
- - - -
- -### label - -
- - - -
- -### placeholder - -
- - - -
- -### error - -
- - - -
- -### onChangeText - -
- - - -
- -### selectionColor - -
- - - -
- -### cursorColor Android only - -
- - - -
- -### underlineColor - -
- - - -
- -### activeUnderlineColor - -
- - - -
- -### outlineColor - -
- - - -
- -### activeOutlineColor - -
- - - -
- -### textColor - -
- - - -
- -### dense - -
- - - -
- -### multiline - -
- - - -
- -### numberOfLines Android only - -
- - - -
- -### onFocus - -
- - - -
- -### onBlur - -
- - - -
- -### render - -
- - - -
- -### value - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### contentStyle - -
- - - -
- -### outlineStyle - -
- - - -
- -### underlineStyle - -
- - - -
- -### editable - -
- - - - - - - -## Known Issues - -
- -
diff --git a/docs/5.x/docs/components/TextInput/TextInputAffix.mdx b/docs/5.x/docs/components/TextInput/TextInputAffix.mdx deleted file mode 100644 index 9ae689e1ae..0000000000 --- a/docs/5.x/docs/components/TextInput/TextInputAffix.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: TextInput.Affix ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to render a leading / trailing text in the TextInput - - - -## Usage - -```js -import * as React from 'react'; -import { TextInput } from 'react-native-paper'; - -const MyComponent = () => { - const [text, setText] = React.useState(''); - - return ( - } - /> - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### text (required) - -
- - - -
- -### onLayout - -
- - - -
- -### onPress - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### textStyle - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/TextInput/TextInputIcon.mdx b/docs/5.x/docs/components/TextInput/TextInputIcon.mdx deleted file mode 100644 index 2fe4ee3037..0000000000 --- a/docs/5.x/docs/components/TextInput/TextInputIcon.mdx +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: TextInput.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to render a leading / trailing icon in the TextInput - - - -## Usage - -```js -import * as React from 'react'; -import { TextInput } from 'react-native-paper'; - -const MyComponent = () => { - const [text, setText] = React.useState(''); - - return ( - } - /> - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### icon (required) Renamed from 'name' to 'icon` in v5.x - -
- - - -
- -### onPress - -
- - - -
- -### forceTextInputFocus - -
- - - -
- -### color - -
- - - -
- -### rippleColor - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/TextInput/_meta.json b/docs/5.x/docs/components/TextInput/_meta.json deleted file mode 100644 index 412d6916f3..0000000000 --- a/docs/5.x/docs/components/TextInput/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "TextInput", - "TextInputAffix", - "TextInputIcon" -] diff --git a/docs/5.x/docs/components/ToggleButton/ToggleButton.mdx b/docs/5.x/docs/components/ToggleButton/ToggleButton.mdx deleted file mode 100644 index ff0e636eb5..0000000000 --- a/docs/5.x/docs/components/ToggleButton/ToggleButton.mdx +++ /dev/null @@ -1,154 +0,0 @@ ---- -title: ToggleButton ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons, -a group should share a common container. - - - -## Usage - -```js -import * as React from 'react'; -import { ToggleButton } from 'react-native-paper'; - -const ToggleButtonExample = () => { - const [status, setStatus] = React.useState('checked'); - - const onButtonToggle = (value) => { - setStatus(status === 'checked' ? 'unchecked' : 'checked'); - }; - - return ( - - ); -}; - -export default ToggleButtonExample; -``` - -## Props - - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### iconColor - -
- - - -
- -### rippleColor - -
- - - -
- -### disabled - -
- - - -
- -### accessibilityLabel - -
- - - -
- -### onPress - -
- - - -
- -### value - -
- - - -
- -### status - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### ref - -
- - - -
- -### testID - -
- - - - - - - - diff --git a/docs/5.x/docs/components/ToggleButton/ToggleButtonGroup.mdx b/docs/5.x/docs/components/ToggleButton/ToggleButtonGroup.mdx deleted file mode 100644 index 5bc93919a1..0000000000 --- a/docs/5.x/docs/components/ToggleButton/ToggleButtonGroup.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: ToggleButton.Group ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Toggle group allows to control a group of toggle buttons. -It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow). - - - -## Usage - -```js -import * as React from 'react'; -import { ToggleButton } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('left'); - - return ( - setValue(value)} - value={value} - > - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### onValueChange (required) - -
- - - -
- -### value (required) - -
- - - -
- -### children (required) - -
- - - - - - - - diff --git a/docs/5.x/docs/components/ToggleButton/ToggleButtonRow.mdx b/docs/5.x/docs/components/ToggleButton/ToggleButtonRow.mdx deleted file mode 100644 index f04bf5cba5..0000000000 --- a/docs/5.x/docs/components/ToggleButton/ToggleButtonRow.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: ToggleButton.Row ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Toggle button row renders a group of toggle buttons in a row. - - - -## Usage - -```js -import * as React from 'react'; -import { ToggleButton } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('left'); - - return ( - setValue(value)} value={value}> - - - - ); -}; - -export default MyComponent; -``` - -## Props - - - -
- -### onValueChange (required) - -
- - - -
- -### value (required) - -
- - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - diff --git a/docs/5.x/docs/components/ToggleButton/_meta.json b/docs/5.x/docs/components/ToggleButton/_meta.json deleted file mode 100644 index 935c2b9584..0000000000 --- a/docs/5.x/docs/components/ToggleButton/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "ToggleButton", - "ToggleButtonGroup", - "ToggleButtonRow" -] diff --git a/docs/5.x/docs/components/Tooltip/Tooltip.mdx b/docs/5.x/docs/components/Tooltip/Tooltip.mdx deleted file mode 100644 index 51ce74bdf3..0000000000 --- a/docs/5.x/docs/components/Tooltip/Tooltip.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Tooltip ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Tooltips display informative text when users hover over, focus on, or tap an element. - -Plain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text. - - - -## Usage - -```js -import * as React from 'react'; -import { IconButton, Tooltip } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} /> - -); - -export default MyComponent; -``` - -## Props - - - -
- -### children (required) - -
- - - -
- -### enterTouchDelay - -
- - - -
- -### leaveTouchDelay - -
- - - -
- -### title (required) - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - - - -## Theme colors - - - - diff --git a/docs/5.x/docs/components/Tooltip/_meta.json b/docs/5.x/docs/components/Tooltip/_meta.json deleted file mode 100644 index c39a54301f..0000000000 --- a/docs/5.x/docs/components/Tooltip/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Tooltip" -] diff --git a/docs/5.x/docs/components/TouchableRipple/TouchableRipple.mdx b/docs/5.x/docs/components/TouchableRipple/TouchableRipple.mdx deleted file mode 100644 index 00dee005e0..0000000000 --- a/docs/5.x/docs/components/TouchableRipple/TouchableRipple.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: TouchableRipple ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A wrapper for views that should respond to touches. -Provides a material "ink ripple" interaction effect for supported platforms (>= Android Lollipop). -On unsupported platforms, it falls back to a highlight effect. - - - -## Usage - -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Text, TouchableRipple } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')} - rippleColor="rgba(0, 0, 0, .32)" - > - Press anywhere - -); - -export default MyComponent; -``` - -## Props - -### Pressable props - - - -
- -### borderless - -
- - - -
- -### background - -
- - - -
- -### centered - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### rippleColor - -
- - - -
- -### underlayColor - -
- - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - diff --git a/docs/5.x/docs/components/TouchableRipple/_meta.json b/docs/5.x/docs/components/TouchableRipple/_meta.json deleted file mode 100644 index 91b70a001b..0000000000 --- a/docs/5.x/docs/components/TouchableRipple/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "TouchableRipple" -] diff --git a/docs/5.x/docs/components/_meta.json b/docs/5.x/docs/components/_meta.json deleted file mode 100644 index 548d39075a..0000000000 --- a/docs/5.x/docs/components/_meta.json +++ /dev/null @@ -1,180 +0,0 @@ -[ - "ActivityIndicator", - { - "type": "dir", - "name": "Appbar", - "label": "Appbar", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Avatar", - "label": "Avatar", - "collapsible": true, - "collapsed": false - }, - "Badge", - "Banner", - { - "type": "dir", - "name": "BottomNavigation", - "label": "BottomNavigation", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Button", - "label": "Button", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Card", - "label": "Card", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Checkbox", - "label": "Checkbox", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Chip", - "label": "Chip", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "DataTable", - "label": "DataTable", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Dialog", - "label": "Dialog", - "collapsible": true, - "collapsed": false - }, - "Divider", - { - "type": "dir", - "name": "Drawer", - "label": "Drawer", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "FAB", - "label": "FAB", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "IconButton", - "label": "IconButton", - "collapsible": true, - "collapsed": false - }, - "Icon", - { - "type": "dir", - "name": "List", - "label": "List", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Menu", - "label": "Menu", - "collapsible": true, - "collapsed": false - }, - "Modal", - { - "type": "dir", - "name": "Portal", - "label": "Portal", - "collapsible": true, - "collapsed": false - }, - "ProgressBar", - { - "type": "dir", - "name": "RadioButton", - "label": "RadioButton", - "collapsible": true, - "collapsed": false - }, - "Searchbar", - { - "type": "dir", - "name": "SegmentedButtons", - "label": "SegmentedButtons", - "collapsible": true, - "collapsed": false - }, - "Snackbar", - "Surface", - { - "type": "dir", - "name": "Switch", - "label": "Switch", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "TextInput", - "label": "TextInput", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "ToggleButton", - "label": "ToggleButton", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Tooltip", - "label": "Tooltip", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "TouchableRipple", - "label": "TouchableRipple", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Text", - "label": "Text", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "HelperText", - "label": "HelperText", - "collapsible": true, - "collapsed": false - } -] diff --git a/docs/5.x/docs/guides/migration-guide-to-5.0.md b/docs/5.x/docs/guides/migration-guide-to-5.0.md index 6abf85502c..2e47a8773a 100644 --- a/docs/5.x/docs/guides/migration-guide-to-5.0.md +++ b/docs/5.x/docs/guides/migration-guide-to-5.0.md @@ -60,7 +60,7 @@ Each accent and error colors has a group of related tones. The tones are mapped Dynamic colors are not supported yet ::: -![color-palette](/react-native-paper/migration/color-palette.png) +![color-palette](../../../public/migration/color-palette.png) Colors theme structure should follow the default palette and contain the following properties: @@ -290,7 +290,7 @@ Two additional props that control the scene animation were introduced that contr - `sceneAnimationType: "opacity" | "shifting" | undefined` - defines the animation type for the scene. `shifting` enables a new animation where navigating to a scene will shift it horizontally into view. Both `opacity` and `undefined` have the same effect, fading the scene into view. - `sceneAnimationEasing` allows specifying a custom easing function for the scene animation. -![shiftingAnimation](/react-native-paper/screenshots/bottom-navigation-shifting.gif) +![shiftingAnimation](../../../public/screenshots/bottom-navigation-shifting.gif) On a final note, please be aware that `BottomNavigation` with theme version 3 doesn't have a shadow. @@ -575,7 +575,7 @@ At the same time, by analogy to the second new prop, the `icon` prop was renamed `SegmentedButtons` is a completely new component introduced in the latest version. It allows people to select options, switch views, or sort elements. It supports single and multiselect select variant and provide a lot of customization options. -![segmentedButtons](/react-native-paper/screenshots/segmentedbuttons.gif) +![segmentedButtons](../../../public/screenshots/segmentedbuttons.gif) ```js const MyComponent = () => { @@ -640,7 +640,7 @@ The property `name` was renamed to `icon`, since the scope and type of that prop Component displayed upon tapping and holding a screen element or component used to present an informative text label identifying an element, such as a description of its function. -![tooltip](/react-native-paper/screenshots/tooltips.gif) +![tooltip](../../../public/screenshots/tooltips.gif) ```js diff --git a/docs/5.x/docs/guides/theming-with-react-navigation.md b/docs/5.x/docs/guides/theming-with-react-navigation.md index d240f5b43d..5ccd78701d 100644 --- a/docs/5.x/docs/guides/theming-with-react-navigation.md +++ b/docs/5.x/docs/guides/theming-with-react-navigation.md @@ -328,7 +328,7 @@ const Header = ({ scene }) => { And now you can switch between light and dark theme! -![paperGuide1](/react-native-paper/screenshots/themingWithReactNavigationDarkLightSwitch.gif) +![paperGuide1](../../../public/screenshots/themingWithReactNavigationDarkLightSwitch.gif) Thanks to the linking of themes that we did earlier, switching themes can be controlled with only one piece of state. diff --git a/docs/5.x/docs/guides/theming.mdx b/docs/5.x/docs/guides/theming.mdx index 89bd6e75af..d2d4392fe8 100644 --- a/docs/5.x/docs/guides/theming.mdx +++ b/docs/5.x/docs/guides/theming.mdx @@ -188,7 +188,7 @@ Created schemes are following the Material Design 3 color system and covering co Passed source color into the util is translated into tones to automatically provide the range of tones that map to color roles. -![customColors](/react-native-paper/screenshots/custom-colors.png) +![customColors](../../../public/screenshots/custom-colors.png) _Source: [Material You Color System](https://m3.material.io/styles/color/the-color-system/custom-colors)_ diff --git a/docs/6.x/docs/components/ActivityIndicator.mdx b/docs/6.x/docs/components/ActivityIndicator.mdx deleted file mode 100644 index eac03089ea..0000000000 --- a/docs/6.x/docs/components/ActivityIndicator.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: ActivityIndicator ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Activity indicator is used to present progress of some activity in the app. -It can be used as a drop-in replacement for the ActivityIndicator shipped with React Native. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { ActivityIndicator, Palette } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### animating - -
- - - -
- -### color - -
- - - -
- -### size - -
- - - -
- -### hidesWhenStopped - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Appbar/Appbar.mdx b/docs/6.x/docs/components/Appbar/Appbar.mdx deleted file mode 100644 index 798b6dd136..0000000000 --- a/docs/6.x/docs/components/Appbar/Appbar.mdx +++ /dev/null @@ -1,174 +0,0 @@ ---- -title: Appbar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to display action items in a bar. It can be placed at the top or bottom. -The top bar usually contains the screen title, controls such as navigation buttons, menu button etc. -The bottom bar usually provides access to a drawer and up to four actions. - -By default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. -See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations - - - - - - - -## Usage -### Top bar -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} /> - - {}} /> - {}} /> - -); - -export default MyComponent; -``` - -### Bottom bar -```js -import * as React from 'react'; -import { StyleSheet } from 'react-native'; -import { Appbar, FAB, useTheme } from 'react-native-paper'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; - -const BOTTOM_APPBAR_HEIGHT = 80; -const MEDIUM_FAB_HEIGHT = 56; - -const MyComponent = () => { - const { bottom } = useSafeAreaInsets(); - const theme = useTheme(); - - return ( - - {}} /> - {}} /> - {}} /> - {}} /> - {}} - style={[ - styles.fab, - { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 }, - ]} - /> - - ); -}; - -const styles = StyleSheet.create({ - bottom: { - backgroundColor: 'aquamarine', - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - }, - fab: { - position: 'absolute', - right: 16, - }, -}); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### dark - -
- - - -
- -### children (required) - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### elevated Available in v5.x with theme version 3 - -
- - - -
- -### safeAreaInsets - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Appbar/AppbarAction.mdx b/docs/6.x/docs/components/Appbar/AppbarAction.mdx deleted file mode 100644 index 5db23d4ba9..0000000000 --- a/docs/6.x/docs/components/Appbar/AppbarAction.mdx +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: Appbar.Action ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display an action item in the appbar. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; -import { Platform } from 'react-native'; - -const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; - -const MyComponent = () => ( - - - {}} /> - {}} /> - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### color - -
- - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### disabled - -
- - - -
- -### aria-label - -
- - - -
- -### onPress - -
- - - -
- -### isLeading Available in v5.x with theme version 3 - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx b/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx deleted file mode 100644 index 4faa4ec18d..0000000000 --- a/docs/6.x/docs/components/Appbar/AppbarBackAction.mdx +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Appbar.BackAction ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display a back button in the appbar. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} /> - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### color - -
- - - -
- -### size - -
- - - -
- -### disabled - -
- - - -
- -### aria-label - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Appbar/AppbarContent.mdx b/docs/6.x/docs/components/Appbar/AppbarContent.mdx deleted file mode 100644 index a3c4953c66..0000000000 --- a/docs/6.x/docs/components/Appbar/AppbarContent.mdx +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: Appbar.Content ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display a title in an appbar. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => ( - - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### title (required) - -
- - - -
- -### titleStyle - -
- - - -
- -### titleRef - -
- - - -
- -### onPress - -
- - - -
- -### disabled - -
- - - -
- -### color - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Appbar/AppbarHeader.mdx b/docs/6.x/docs/components/Appbar/AppbarHeader.mdx deleted file mode 100644 index f541042a00..0000000000 --- a/docs/6.x/docs/components/Appbar/AppbarHeader.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: Appbar.Header ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to use as a header at the top of the screen. -It can contain the screen title, controls such as navigation buttons, menu button etc. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Appbar } from 'react-native-paper'; - -const MyComponent = () => { - const _goBack = () => console.log('Went back'); - - const _handleSearch = () => console.log('Searching'); - - const _handleMore = () => console.log('Shown more'); - - return ( - - - - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### dark - -
- - - -
- -### statusBarHeight - -
- - - -
- -### children (required) - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### elevated Available in v5.x with theme version 3 - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Appbar/_meta.json b/docs/6.x/docs/components/Appbar/_meta.json deleted file mode 100644 index d4243fea02..0000000000 --- a/docs/6.x/docs/components/Appbar/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - "Appbar", - "AppbarAction", - "AppbarBackAction", - "AppbarContent", - "AppbarHeader" -] diff --git a/docs/6.x/docs/components/Avatar/AvatarIcon.mdx b/docs/6.x/docs/components/Avatar/AvatarIcon.mdx deleted file mode 100644 index 045a00c260..0000000000 --- a/docs/6.x/docs/components/Avatar/AvatarIcon.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Avatar.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Avatars can be used to represent people in a graphical way. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Avatar } from 'react-native-paper'; - -const MyComponent = () => ( - -); -``` - - - ## Props - - - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### color - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Avatar/AvatarImage.mdx b/docs/6.x/docs/components/Avatar/AvatarImage.mdx deleted file mode 100644 index ecd4bcc751..0000000000 --- a/docs/6.x/docs/components/Avatar/AvatarImage.mdx +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Avatar.Image ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Avatars can be used to represent people in a graphical way. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Avatar } from 'react-native-paper'; - -const MyComponent = () => ( - -); -export default MyComponent -``` - - - ## Props - - - - -
- -### source (required) - -
- - - -
- -### size - -
- - - -
- -### style - -
- - - -
- -### onError - -
- - - -
- -### onLayout - -
- - - -
- -### onLoad - -
- - - -
- -### onLoadEnd - -
- - - -
- -### onLoadStart - -
- - - -
- -### onProgress - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Avatar/AvatarText.mdx b/docs/6.x/docs/components/Avatar/AvatarText.mdx deleted file mode 100644 index 600c026e9f..0000000000 --- a/docs/6.x/docs/components/Avatar/AvatarText.mdx +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Avatar.Text ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Avatars can be used to represent people in a graphical way. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Avatar } from 'react-native-paper'; - -const MyComponent = () => ( - -); -``` - - - ## Props - - - - -
- -### label (required) - -
- - - -
- -### size - -
- - - -
- -### color - -
- - - -
- -### style - -
- - - -
- -### labelStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Avatar/_meta.json b/docs/6.x/docs/components/Avatar/_meta.json deleted file mode 100644 index b3967b8318..0000000000 --- a/docs/6.x/docs/components/Avatar/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "AvatarIcon", - "AvatarImage", - "AvatarText" -] diff --git a/docs/6.x/docs/components/Badge.mdx b/docs/6.x/docs/components/Badge.mdx deleted file mode 100644 index fbb1589d84..0000000000 --- a/docs/6.x/docs/components/Badge.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: Badge ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Badges are small status descriptors for UI elements. -A badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object. - -The badge is styled differently based on whether `children` is passed: -- Small dot when it doesn't have `children` -- Larger pill when it has `children` - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Badge } from 'react-native-paper'; - -const MyComponent = () => ( - 3 -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### visible - -
- - - -
- -### children - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Banner.mdx b/docs/6.x/docs/components/Banner.mdx deleted file mode 100644 index 344d9103e9..0000000000 --- a/docs/6.x/docs/components/Banner.mdx +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Banner ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Banner displays a prominent message and related actions. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Image } from 'react-native'; -import { Banner } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(true); - - return ( - setVisible(false), - }, - { - label: 'Learn more', - onPress: () => setVisible(false), - }, - ]} - icon={({size}) => ( - - )}> - There was a problem processing a transaction on your credit card. - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### visible (required) - -
- - - -
- -### children (required) - -
- - - -
- -### icon - -
- - - -
- -### actions - -
- - - -
- -### contentStyle - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - -
- -### onShowAnimationFinished - -
- - - -
- -### onHideAnimationFinished - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/BottomNavigation/BottomNavigation.mdx b/docs/6.x/docs/components/BottomNavigation/BottomNavigation.mdx deleted file mode 100644 index c4fe55af84..0000000000 --- a/docs/6.x/docs/components/BottomNavigation/BottomNavigation.mdx +++ /dev/null @@ -1,308 +0,0 @@ ---- -title: BottomNavigation ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar. -It is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar). - -By default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. -See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { BottomNavigation, Text } from 'react-native-paper'; - -const MusicRoute = () => Music; - -const AlbumsRoute = () => Albums; - -const RecentsRoute = () => Recents; - -const NotificationsRoute = () => Notifications; - -const MyComponent = () => { - const [index, setIndex] = React.useState(0); - const [routes] = React.useState([ - { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'}, - { key: 'albums', title: 'Albums', focusedIcon: 'album' }, - { key: 'recents', title: 'Recents', focusedIcon: 'history' }, - { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' }, - ]); - - const renderScene = BottomNavigation.SceneMap({ - music: MusicRoute, - albums: AlbumsRoute, - recents: RecentsRoute, - notifications: NotificationsRoute, - }); - - return ( - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### shifting - -
- - - -
- -### labeled - -
- - - -
- -### compact - -
- - - -
- -### navigationState (required) - -
- - - -
- -### onIndexChange (required) - -
- - - -
- -### renderScene (required) - -
- - - -
- -### renderIcon - -
- - - -
- -### renderLabel - -
- - - -
- -### renderTouchable - -
- - - -
- -### getAccessibilityLabel - -
- - - -
- -### getBadge - -
- - - -
- -### getLabelText - -
- - - -
- -### getLazy - -
- - - -
- -### getTestID - -
- - - -
- -### onTabPress - -
- - - -
- -### onTabLongPress - -
- - - -
- -### activeColor - -
- - - -
- -### inactiveColor - -
- - - -
- -### sceneAnimationEnabled - -
- - - -
- -### sceneAnimationType - -
- - - -
- -### sceneAnimationEasing - -
- - - -
- -### keyboardHidesNavigationBar - -
- - - -
- -### safeAreaInsets - -
- - - -
- -### barStyle - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### activeIndicatorStyle - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/BottomNavigation/BottomNavigationBar.mdx b/docs/6.x/docs/components/BottomNavigation/BottomNavigationBar.mdx deleted file mode 100644 index a93d41ee74..0000000000 --- a/docs/6.x/docs/components/BottomNavigation/BottomNavigationBar.mdx +++ /dev/null @@ -1,285 +0,0 @@ ---- -title: BottomNavigation.Bar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/). - - - - - - - - - ## Usage -### without React Navigation -```js -import React from 'react'; -import { useState } from 'react'; -import { View } from 'react-native'; -import { BottomNavigation, Text, Provider } from 'react-native-paper'; -import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons'; - -function HomeScreen() { - return ( - - Home! - - ); -} - -function SettingsScreen() { - return ( - - Settings! - - ); -} - -export default function MyComponent() { - const [index, setIndex] = useState(0); - - const routes = [ - { key: 'home', title: 'Home', icon: 'home' }, - { key: 'settings', title: 'Settings', icon: 'cog' }, - ]; - - const renderScene = ({ route }) => { - switch (route.key) { - case 'home': - return ; - case 'settings': - return ; - default: - return null; - } - }; - - return ( - - {renderScene({ route: routes[index] })} - { - const newIndex = routes.findIndex((r) => r.key === route.key); - if (newIndex !== -1) { - setIndex(newIndex); - } - }} - renderIcon={({ route, color }) => ( - - )} - getLabelText={({ route }) => route.title} - /> - - ); -} -``` - - ### with React Navigation - \n Home!\n
\n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst MyTabs = createBottomTabNavigator({\n screenOptions: {\n animation: 'shift',\n },\n tabBar: ({ navigation, state, descriptors, insets }) => (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n ),\n screens: {\n Home: {\n screen: HomeScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n Settings: {\n screen: SettingsScreen,\n options: {\n tabBarIcon: ({ color }) => (\n \n ),\n },\n },\n },\n});\n\nconst Navigation = createStaticNavigation(MyTabs);\n\nexport default function App() {\n return (\n \n \n \n \n \n );\n}","dynamic":"import { Text, View } from 'react-native';\nimport { NavigationContainer, CommonActions } from '@react-navigation/native';\nimport { createBottomTabNavigator } from '@react-navigation/bottom-tabs';\nimport { Provider, BottomNavigation } from 'react-native-paper';\nimport MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nconst Tab = createBottomTabNavigator();\n\nexport default function App() {\n return (\n \n \n (\n {\n const event = navigation.emit({\n type: 'tabPress',\n target: route.key,\n canPreventDefault: true,\n });\n\n if (event.defaultPrevented) {\n preventDefault();\n } else {\n navigation.dispatch({\n ...CommonActions.navigate(route.name, route.params),\n target: state.key,\n });\n }\n }}\n renderIcon={({ route, focused, color }) =>\n descriptors[route.key].options.tabBarIcon?.({\n focused,\n color,\n size: 24,\n }) || null\n }\n getLabelText={({ route }) => {\n const { options } = descriptors[route.key];\n const label =\n typeof options.tabBarLabel === 'string'\n ? options.tabBarLabel\n : typeof options.title === 'string'\n ? options.title\n : route.name;\n\n return label;\n }}\n />\n )}>\n (\n \n ),\n }}\n />\n (\n \n ),\n }}\n />\n \n \n \n );\n}\n"}}} /> - - - - ## Props - - - - -
- -### shifting - -
- - - -
- -### labeled - -
- - - -
- -### compact - -
- - - -
- -### navigationState (required) - -
- - - -
- -### renderIcon - -
- - - -
- -### renderLabel - -
- - - -
- -### renderTouchable - -
- - - -
- -### getAccessibilityLabel - -
- - - -
- -### getBadge - -
- - - -
- -### getLabelText - -
- - - -
- -### getTestID - -
- - - -
- -### onTabPress (required) - -
- - - -
- -### onTabLongPress - -
- - - -
- -### activeColor - -
- - - -
- -### inactiveColor - -
- - - -
- -### animationEasing - -
- - - -
- -### keyboardHidesNavigationBar - -
- - - -
- -### safeAreaInsets - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### activeIndicatorStyle - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/BottomNavigation/_meta.json b/docs/6.x/docs/components/BottomNavigation/_meta.json deleted file mode 100644 index 94a0b010f3..0000000000 --- a/docs/6.x/docs/components/BottomNavigation/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "BottomNavigation", - "BottomNavigationBar" -] diff --git a/docs/6.x/docs/components/Button/Button.mdx b/docs/6.x/docs/components/Button/Button.mdx deleted file mode 100644 index 80e87fbf84..0000000000 --- a/docs/6.x/docs/components/Button/Button.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: Button ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A button is component that the user can press to trigger an action. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Button } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### mode - -
- - - -
- -### dark - -
- - - -
- -### compact - -
- - - -
- -### buttonColor - -
- - - -
- -### textColor - -
- - - -
- -### loading - -
- - - -
- -### icon - -
- - - -
- -### disabled - -
- - - -
- -### children (required) - -
- - - -
- -### uppercase - -
- - - -
- -### background - -
- - - -
- -### aria-label - -
- - - -
- -### accessibilityHint - -
- - - -
- -### role - -
- - - -
- -### onPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### onLongPress - -
- - - -
- -### delayLongPress - -
- - - -
- -### contentStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### hitSlop - -
- - - -
- -### style - -
- - - -
- -### labelStyle - -
- - - -
- -### theme - -
- - - -
- -### touchableRef - -
- - - -
- -### ref - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Button/_meta.json b/docs/6.x/docs/components/Button/_meta.json deleted file mode 100644 index a6d17d5990..0000000000 --- a/docs/6.x/docs/components/Button/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Button" -] diff --git a/docs/6.x/docs/components/Card/Card.mdx b/docs/6.x/docs/components/Card/Card.mdx deleted file mode 100644 index de2e1b19cb..0000000000 --- a/docs/6.x/docs/components/Card/Card.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: Card ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A card is a sheet of material that serves as an entry point to more detailed information. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Avatar, Button, Card, Text } from 'react-native-paper'; - -const LeftContent = props => - -const MyComponent = () => ( - - - - Card title - Card content - - - - - - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### mode - -
- - - -
- -### children (required) - -
- - - -
- -### onLongPress - -
- - - -
- -### onPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### delayLongPress - -
- - - -
- -### disabled - -
- - - -
- -### elevation - -
- - - -
- -### contentStyle - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### accessible - -
- - - -
- -### ref - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Card/CardActions.mdx b/docs/6.x/docs/components/Card/CardActions.mdx deleted file mode 100644 index fb2af05f91..0000000000 --- a/docs/6.x/docs/components/Card/CardActions.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Card.Actions ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a list of actions inside a Card. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Card, Button } from 'react-native-paper'; - -const MyComponent = () => ( - - - - - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Card/CardContent.mdx b/docs/6.x/docs/components/Card/CardContent.mdx deleted file mode 100644 index 54bf3c61be..0000000000 --- a/docs/6.x/docs/components/Card/CardContent.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Card.Content ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show content inside a Card. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Card, Text } from 'react-native-paper'; - -const MyComponent = () => ( - - - Card title - Card content - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Card/CardCover.mdx b/docs/6.x/docs/components/Card/CardCover.mdx deleted file mode 100644 index 9808e9bfd8..0000000000 --- a/docs/6.x/docs/components/Card/CardCover.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Card.Cover ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a cover image inside a Card. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Card } from 'react-native-paper'; - -const MyComponent = () => ( - - - -); - -export default MyComponent; -``` - - - - - ## Props - ### Image props - - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Card/CardTitle.mdx b/docs/6.x/docs/components/Card/CardTitle.mdx deleted file mode 100644 index 90d114e478..0000000000 --- a/docs/6.x/docs/components/Card/CardTitle.mdx +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Card.Title ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a title, subtitle and an avatar inside a Card. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Avatar, Card, IconButton } from 'react-native-paper'; - -const MyComponent = () => ( - } - right={(props) => {}} />} - /> -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### title (required) - -
- - - -
- -### titleStyle - -
- - - -
- -### titleNumberOfLines - -
- - - -
- -### titleVariant Available in v5.x with theme version 3 - -
- - - -
- -### subtitle - -
- - - -
- -### subtitleStyle - -
- - - -
- -### subtitleNumberOfLines - -
- - - -
- -### subtitleVariant Available in v5.x with theme version 3 - -
- - - -
- -### left - -
- - - -
- -### leftStyle - -
- - - -
- -### right - -
- - - -
- -### rightStyle - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### subtitleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Card/_meta.json b/docs/6.x/docs/components/Card/_meta.json deleted file mode 100644 index 3b7edb1615..0000000000 --- a/docs/6.x/docs/components/Card/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - "Card", - "CardActions", - "CardContent", - "CardCover", - "CardTitle" -] diff --git a/docs/6.x/docs/components/Checkbox/Checkbox.mdx b/docs/6.x/docs/components/Checkbox/Checkbox.mdx deleted file mode 100644 index b12b7e56b9..0000000000 --- a/docs/6.x/docs/components/Checkbox/Checkbox.mdx +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: Checkbox ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Checkboxes allow the selection of multiple options from a set. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Checkbox } from 'react-native-paper'; - -const MyComponent = () => { - const [checked, setChecked] = React.useState(false); - - return ( - { - setChecked(!checked); - }} - /> - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### status (required) - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### error - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### style - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx b/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx deleted file mode 100644 index c81380f62b..0000000000 --- a/docs/6.x/docs/components/Checkbox/CheckboxItem.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: Checkbox.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox. - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Checkbox } from 'react-native-paper'; - -const MyComponent = () => ( - - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### status (required) - -
- - - -
- -### disabled - -
- - - -
- -### label (required) - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### background - -
- - - -
- -### aria-label - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### style - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### labelStyle - -
- - - -
- -### labelVariant Available in v5.x with theme version 3 - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### position - -
- - - -
- -### hitSlop - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Checkbox/_meta.json b/docs/6.x/docs/components/Checkbox/_meta.json deleted file mode 100644 index 69a54c00fd..0000000000 --- a/docs/6.x/docs/components/Checkbox/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "Checkbox", - "CheckboxItem" -] diff --git a/docs/6.x/docs/components/Chip/Chip.mdx b/docs/6.x/docs/components/Chip/Chip.mdx deleted file mode 100644 index f1a72dc03d..0000000000 --- a/docs/6.x/docs/components/Chip/Chip.mdx +++ /dev/null @@ -1,291 +0,0 @@ ---- -title: Chip ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Chips are compact elements that can represent inputs, attributes, or actions. -They can have an icon or avatar on the left, and a close button icon on the right. -They are typically used to: -
    -
  • Present multiple options
  • -
  • Represent attributes active or chosen
  • -
  • Present filter options
  • -
  • Trigger actions related to primary content
  • -
- - - - - - - -## Usage -```js -import * as React from 'react'; -import { Chip } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')}>Example Chip -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### mode - -
- - - -
- -### children (required) - -
- - - -
- -### icon - -
- - - -
- -### avatar - -
- - - -
- -### closeIcon - -
- - - -
- -### selected - -
- - - -
- -### selectedColor - -
- - - -
- -### showSelectedOverlay Available in v5.x with theme version 3 - -
- - - -
- -### showSelectedCheck - -
- - - -
- -### disabled - -
- - - -
- -### background - -
- - - -
- -### aria-label - -
- - - -
- -### closeIconAccessibilityLabel - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### onClose - -
- - - -
- -### delayLongPress - -
- - - -
- -### compact Available in v5.x with theme version 3 - -
- - - -
- -### elevated Available in v5.x with theme version 3 - -
- - - -
- -### textStyle - -
- - - -
- -### style - -
- - - -
- -### hitSlop - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### ellipsizeMode - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### ref - -
- - - -
- -### role - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Chip/_meta.json b/docs/6.x/docs/components/Chip/_meta.json deleted file mode 100644 index 02eb8c63e5..0000000000 --- a/docs/6.x/docs/components/Chip/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Chip" -] diff --git a/docs/6.x/docs/components/DataTable/DataTable.mdx b/docs/6.x/docs/components/DataTable/DataTable.mdx deleted file mode 100644 index 2a9e4ddae7..0000000000 --- a/docs/6.x/docs/components/DataTable/DataTable.mdx +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: DataTable ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Data tables allow displaying sets of data. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => { - const [page, setPage] = React.useState(0); - const [numberOfItemsPerPageList] = React.useState([2, 3, 4]); - const [itemsPerPage, onItemsPerPageChange] = React.useState( - numberOfItemsPerPageList[0] - ); - - const [items] = React.useState([ - { - key: 1, - name: 'Cupcake', - calories: 356, - fat: 16, - }, - { - key: 2, - name: 'Eclair', - calories: 262, - fat: 16, - }, - { - key: 3, - name: 'Frozen yogurt', - calories: 159, - fat: 6, - }, - { - key: 4, - name: 'Gingerbread', - calories: 305, - fat: 3.7, - }, - ]); - - const from = page * itemsPerPage; - const to = Math.min((page + 1) * itemsPerPage, items.length); - - React.useEffect(() => { - setPage(0); - }, [itemsPerPage]); - - return ( - - - Dessert - Calories - Fat - - - {items.slice(from, to).map((item) => ( - - {item.name} - {item.calories} - {item.fat} - - ))} - - setPage(page)} - label={`${from + 1}-${to} of ${items.length}`} - numberOfItemsPerPageList={numberOfItemsPerPageList} - numberOfItemsPerPage={itemsPerPage} - onItemsPerPageChange={onItemsPerPageChange} - showFastPaginationControls - selectPageDropdownLabel={'Rows per page'} - /> - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/DataTable/DataTableCell.mdx b/docs/6.x/docs/components/DataTable/DataTableCell.mdx deleted file mode 100644 index 89d4809627..0000000000 --- a/docs/6.x/docs/components/DataTable/DataTableCell.mdx +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: DataTable.Cell ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a single cell inside of a table. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - 1 - 2 - 3 - 4 - -); - -export default MyComponent; -``` - -If you want to support multiline text, please use View instead, as multiline text doesn't comply with -MD Guidelines (https://github.com/callstack/react-native-paper/issues/2381). - - - - - ## Props - ### TouchableRipple props - - - -
- -### children (required) - -
- - - -
- -### numeric - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### textStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/DataTable/DataTableHeader.mdx b/docs/6.x/docs/components/DataTable/DataTableHeader.mdx deleted file mode 100644 index 8b1ee0dd8f..0000000000 --- a/docs/6.x/docs/components/DataTable/DataTableHeader.mdx +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: DataTable.Header ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to display title in table header. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - - - Dessert - - Calories - Fat (g) - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/DataTable/DataTablePagination.mdx b/docs/6.x/docs/components/DataTable/DataTablePagination.mdx deleted file mode 100644 index 9ededfa603..0000000000 --- a/docs/6.x/docs/components/DataTable/DataTablePagination.mdx +++ /dev/null @@ -1,187 +0,0 @@ ---- -title: DataTable.Pagination ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show pagination for data table. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const numberOfItemsPerPageList = [2, 3, 4]; - -const items = [ - { - key: 1, - name: 'Page 1', - }, - { - key: 2, - name: 'Page 2', - }, - { - key: 3, - name: 'Page 3', - }, -]; - -const MyComponent = () => { - const [page, setPage] = React.useState(0); - const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]); - const from = page * numberOfItemsPerPage; - const to = Math.min((page + 1) * numberOfItemsPerPage, items.length); - - React.useEffect(() => { - setPage(0); - }, [numberOfItemsPerPage]); - - return ( - - setPage(page)} - label={`${from + 1}-${to} of ${items.length}`} - showFastPaginationControls - numberOfItemsPerPageList={numberOfItemsPerPageList} - numberOfItemsPerPage={numberOfItemsPerPage} - onItemsPerPageChange={onItemsPerPageChange} - selectPageDropdownLabel={'Rows per page'} - /> - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### page (required) - -
- - - -
- -### numberOfPages (required) - -
- - - -
- -### onPageChange (required) - -
- - - -
- -### showFastPaginationControls - -
- - - -
- -### theme - -
- - - -
- -### numberOfItemsPerPage - -
- - - -
- -### numberOfItemsPerPageList - -
- - - -
- -### onItemsPerPageChange - -
- - - -
- -### selectPageDropdownLabel - -
- - - -
- -### selectPageDropdownAccessibilityLabel - -
- - - -
- -### label - -
- - - -
- -### aria-label - -
- - - -
- -### style - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/DataTable/DataTableRow.mdx b/docs/6.x/docs/components/DataTable/DataTableRow.mdx deleted file mode 100644 index 59e6a4f5be..0000000000 --- a/docs/6.x/docs/components/DataTable/DataTableRow.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: DataTable.Row ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a single row inside of a table. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - 1 - 2 - 3 - 4 - -); - -export default MyComponent; -``` - - - - - ## Props - ### TouchableRipple props - - - -
- -### children (required) - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### pointerEvents - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/DataTable/DataTableTitle.mdx b/docs/6.x/docs/components/DataTable/DataTableTitle.mdx deleted file mode 100644 index f8087199f7..0000000000 --- a/docs/6.x/docs/components/DataTable/DataTableTitle.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: DataTable.Title ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to display title in table header. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { DataTable } from 'react-native-paper'; - -const MyComponent = () => ( - - - - Dessert - - Calories - Fat (g) - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### numeric - -
- - - -
- -### sortDirection - -
- - - -
- -### numberOfLines - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### textStyle - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/DataTable/_meta.json b/docs/6.x/docs/components/DataTable/_meta.json deleted file mode 100644 index aaed448e4d..0000000000 --- a/docs/6.x/docs/components/DataTable/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "DataTable", - "DataTableCell", - "DataTableHeader", - "DataTablePagination", - "DataTableRow", - "DataTableTitle" -] diff --git a/docs/6.x/docs/components/Dialog/Dialog.mdx b/docs/6.x/docs/components/Dialog/Dialog.mdx deleted file mode 100644 index e871f7f750..0000000000 --- a/docs/6.x/docs/components/Dialog/Dialog.mdx +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: Dialog ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. -To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal/Portal) component. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const showDialog = () => setVisible(true); - - const hideDialog = () => setVisible(false); - - return ( - - - - - - Alert - - This is simple dialog - - - - - - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### dismissable - -
- - - -
- -### dismissableBackButton - -
- - - -
- -### onDismiss - -
- - - -
- -### visible - -
- - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Dialog/DialogActions.mdx b/docs/6.x/docs/components/Dialog/DialogActions.mdx deleted file mode 100644 index b1d6bda29f..0000000000 --- a/docs/6.x/docs/components/Dialog/DialogActions.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Dialog.Actions ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a list of actions in a Dialog. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Button, Dialog, Portal } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - - - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Dialog/DialogContent.mdx b/docs/6.x/docs/components/Dialog/DialogContent.mdx deleted file mode 100644 index 38771647f2..0000000000 --- a/docs/6.x/docs/components/Dialog/DialogContent.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Dialog.Content ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show content in a Dialog. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - This is simple dialog - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Dialog/DialogIcon.mdx b/docs/6.x/docs/components/Dialog/DialogIcon.mdx deleted file mode 100644 index 34777210ca..0000000000 --- a/docs/6.x/docs/components/Dialog/DialogIcon.mdx +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: Dialog.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -@supported Available in v5.x with theme version 3 -A component to show an icon in a Dialog. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { StyleSheet } from 'react-native'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - This is a title - - This is simple dialog - - - - ); -}; - -const styles = StyleSheet.create({ - title: { - textAlign: 'center', - }, -}) - -export default MyComponent; -``` - - - ## Props - - - - -
- -### color - -
- - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Dialog/DialogScrollArea.mdx b/docs/6.x/docs/components/Dialog/DialogScrollArea.mdx deleted file mode 100644 index f5fc1904d2..0000000000 --- a/docs/6.x/docs/components/Dialog/DialogScrollArea.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Dialog.ScrollArea ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a scrollable content in a Dialog. The component only provides appropriate styling. -For the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { ScrollView } from 'react-native'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - - - This is a scrollable area - - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Dialog/DialogTitle.mdx b/docs/6.x/docs/components/Dialog/DialogTitle.mdx deleted file mode 100644 index d548cffb85..0000000000 --- a/docs/6.x/docs/components/Dialog/DialogTitle.mdx +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Dialog.Title ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a title in a Dialog. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Dialog, Portal, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const hideDialog = () => setVisible(false); - - return ( - - - This is a title - - This is simple dialog - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Dialog/_meta.json b/docs/6.x/docs/components/Dialog/_meta.json deleted file mode 100644 index 81373811ab..0000000000 --- a/docs/6.x/docs/components/Dialog/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "Dialog", - "DialogActions", - "DialogContent", - "DialogIcon", - "DialogScrollArea", - "DialogTitle" -] diff --git a/docs/6.x/docs/components/Divider.mdx b/docs/6.x/docs/components/Divider.mdx deleted file mode 100644 index 02ff41392f..0000000000 --- a/docs/6.x/docs/components/Divider.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Divider ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A divider is a thin, lightweight separator that groups content in lists and page layouts. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Divider, Text } from 'react-native-paper'; - -const MyComponent = () => ( - - Lemon - - Mango - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### leftInset Renamed from 'inset' to 'leftInset` in v5.x - -
- - - -
- -### horizontalInset Available in v5.x with theme version 3 - -
- - - -
- -### bold Available in v5.x with theme version 3 - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx b/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx deleted file mode 100644 index 2305db396d..0000000000 --- a/docs/6.x/docs/components/Drawer/DrawerCollapsedItem.mdx +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: Drawer.CollapsedItem ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Note: Available in v5.x with theme version 3 - -Collapsed component used to show an action item with an icon and optionally label in a navigation drawer. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Drawer } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### label - -
- - - -
- -### badge - -
- - - -
- -### disabled - -
- - - -
- -### focusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x - -
- - - -
- -### unfocusedIcon Renamed from 'icon' to 'focusedIcon' in v5.x - -
- - - -
- -### active - -
- - - -
- -### onPress - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### aria-label - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Drawer/DrawerItem.mdx b/docs/6.x/docs/components/Drawer/DrawerItem.mdx deleted file mode 100644 index 4be921a561..0000000000 --- a/docs/6.x/docs/components/Drawer/DrawerItem.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Drawer.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to show an action item with an icon and a label in a navigation drawer. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Drawer } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### label (required) - -
- - - -
- -### icon - -
- - - -
- -### active - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### background - -
- - - -
- -### aria-label - -
- - - -
- -### right - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### hitSlop - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Drawer/DrawerSection.mdx b/docs/6.x/docs/components/Drawer/DrawerSection.mdx deleted file mode 100644 index 5c34c9aef3..0000000000 --- a/docs/6.x/docs/components/Drawer/DrawerSection.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Drawer.Section ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to group content inside a navigation drawer. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Drawer } from 'react-native-paper'; - -const MyComponent = () => { - const [active, setActive] = React.useState(''); - - return ( - - setActive('first')} - /> - setActive('second')} - /> - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### title - -
- - - -
- -### children (required) - -
- - - -
- -### showDivider - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Drawer/_meta.json b/docs/6.x/docs/components/Drawer/_meta.json deleted file mode 100644 index 0099a35f77..0000000000 --- a/docs/6.x/docs/components/Drawer/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "DrawerCollapsedItem", - "DrawerItem", - "DrawerSection" -] diff --git a/docs/6.x/docs/components/FAB/FAB.mdx b/docs/6.x/docs/components/FAB/FAB.mdx deleted file mode 100644 index d672df9781..0000000000 --- a/docs/6.x/docs/components/FAB/FAB.mdx +++ /dev/null @@ -1,197 +0,0 @@ ---- -title: FAB ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A floating action button represents the primary action on a screen. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { StyleSheet } from 'react-native'; -import { FAB } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')} - /> -); - -const styles = StyleSheet.create({ - fab: { - position: 'absolute', - margin: 16, - right: 0, - bottom: 0, - }, -}); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### icon (required) - -
- - - -
- -### variant - -
- - - -
- -### containerColor - -
- - - -
- -### contentColor - -
- - - -
- -### size - -
- - - -
- -### visible - -
- - - -
- -### onPress - -
- - - -
- -### aria-label - -
- - - -
- -### aria-checked - -
- - - -
- -### aria-selected - -
- - - -
- -### aria-busy - -
- - - -
- -### aria-expanded - -
- - - -
- -### background - -
- - - -
- -### style - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - -
- -### ref - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/FAB/FABExtended.mdx b/docs/6.x/docs/components/FAB/FABExtended.mdx deleted file mode 100644 index 98969dc964..0000000000 --- a/docs/6.x/docs/components/FAB/FABExtended.mdx +++ /dev/null @@ -1,223 +0,0 @@ ---- -title: Extended ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -An extended floating action button represents the primary action on a screen -and shows a label next to the icon. Animates between expanded (icon + label) -and collapsed (icon only) states. - - - - - -## Usage -```js -import * as React from 'react'; -import { StyleSheet } from 'react-native'; -import { FAB } from 'react-native-paper'; - -const MyComponent = () => { - const [expanded, setExpanded] = React.useState(true); - - return ( - setExpanded((v) => !v)} - style={styles.fab} - /> - ); -}; - -const styles = StyleSheet.create({ - fab: { - position: 'absolute', - margin: 16, - left: 0, - bottom: 0, - }, -}); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### icon (required) - -
- - - -
- -### label (required) - -
- - - -
- -### variant - -
- - - -
- -### containerColor - -
- - - -
- -### contentColor - -
- - - -
- -### size - -
- - - -
- -### expanded (required) - -
- - - -
- -### visible - -
- - - -
- -### onPress - -
- - - -
- -### aria-label - -
- - - -
- -### aria-checked - -
- - - -
- -### aria-selected - -
- - - -
- -### aria-busy - -
- - - -
- -### aria-expanded - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### background - -
- - - -
- -### style - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - -
- -### ref - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/FAB/FABMenu.mdx b/docs/6.x/docs/components/FAB/FABMenu.mdx deleted file mode 100644 index 4d460f0fc4..0000000000 --- a/docs/6.x/docs/components/FAB/FABMenu.mdx +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Menu ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Floating action button menu. Wraps a trigger FAB; when `expanded` is true, -items appear stacked above and the trigger morphs into the spec'd close -button (`shape: 'full'`, 56 dp, saturated role color). - -No visual backdrop and no outside-tap dismiss — that matches the MD3 spec -and lets the user keep interacting with the content underneath. Dismiss -via the close button or by tapping an item. - - - - - - - -## Usage -```tsx -const [open, setOpen] = React.useState(false); - - - setOpen(false)} - trigger={{ icon: 'plus', variant: 'primary', onPress: () => setOpen(true) }} - items={[ - { icon: 'email', label: 'Send', onPress: () => {} }, - { icon: 'bell', label: 'Remind', onPress: () => {} }, - ]} - /> - -``` - - - ## Props - - - - -
- -### expanded (required) - -
- - - -
- -### onDismiss (required) - -
- - - -
- -### trigger (required) - -
- - - -
- -### alignment - -
- - - -
- -### closeIcon - -
- - - -
- -### items (required) - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/FAB/_meta.json b/docs/6.x/docs/components/FAB/_meta.json deleted file mode 100644 index 5de9f066d9..0000000000 --- a/docs/6.x/docs/components/FAB/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "FAB", - "FABExtended", - "FABMenu" -] diff --git a/docs/6.x/docs/components/Icon.mdx b/docs/6.x/docs/components/Icon.mdx deleted file mode 100644 index 0250150ef0..0000000000 --- a/docs/6.x/docs/components/Icon.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -An icon component which renders icon from vector library. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Icon, Palette } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### size (required) - -
- - - -
- -### allowFontScaling - -
- - - -
- -### source (required) - -
- - - -
- -### color - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/IconButton/IconButton.mdx b/docs/6.x/docs/components/IconButton/IconButton.mdx deleted file mode 100644 index 849f8ce78e..0000000000 --- a/docs/6.x/docs/components/IconButton/IconButton.mdx +++ /dev/null @@ -1,182 +0,0 @@ ---- -title: IconButton ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -An icon button is a button which displays only an icon without a label. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { IconButton, Palette } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')} - /> -); - -export default MyComponent; -``` - - - - - ## Props - ### TouchableRipple props - - - -
- -### icon (required) - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### iconColor Renamed from 'color' to 'iconColor' in v5.x - -
- - - -
- -### containerColor - -
- - - -
- -### selected - -
- - - -
- -### size - -
- - - -
- -### disabled - -
- - - -
- -### animated - -
- - - -
- -### aria-label - -
- - - -
- -### contentStyle - -
- - - -
- -### onPress - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - -
- -### loading - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/IconButton/_meta.json b/docs/6.x/docs/components/IconButton/_meta.json deleted file mode 100644 index 00d733ae01..0000000000 --- a/docs/6.x/docs/components/IconButton/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "IconButton" -] diff --git a/docs/6.x/docs/components/List/ListAccordion.mdx b/docs/6.x/docs/components/List/ListAccordion.mdx deleted file mode 100644 index acc851352f..0000000000 --- a/docs/6.x/docs/components/List/ListAccordion.mdx +++ /dev/null @@ -1,265 +0,0 @@ ---- -title: List.Accordion ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display an expandable list item. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { List } from 'react-native-paper'; - -const MyComponent = () => { - const [expanded, setExpanded] = React.useState(true); - - const handlePress = () => setExpanded(!expanded); - - return ( - - }> - - - - - } - expanded={expanded} - onPress={handlePress}> - - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### title (required) - -
- - - -
- -### description - -
- - - -
- -### left - -
- - - -
- -### right - -
- - - -
- -### expanded - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### delayLongPress - -
- - - -
- -### children (required) - -
- - - -
- -### theme - -
- - - -
- -### background - -
- - - -
- -### style - -
- - - -
- -### containerStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### titleStyle - -
- - - -
- -### descriptionStyle - -
- - - -
- -### titleNumberOfLines - -
- - - -
- -### descriptionNumberOfLines - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### descriptionMaxFontSizeMultiplier - -
- - - -
- -### id - -
- - - -
- -### testID - -
- - - -
- -### aria-label - -
- - - -
- -### pointerEvents - -
- - - -
- -### hitSlop - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/List/ListAccordionGroup.mdx b/docs/6.x/docs/components/List/ListAccordionGroup.mdx deleted file mode 100644 index 8829f84049..0000000000 --- a/docs/6.x/docs/components/List/ListAccordionGroup.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: List.AccordionGroup ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work. -List.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version. -At most one Accordion can be expanded at a given time. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View, Text } from 'react-native'; -import { List } from 'react-native-paper'; - -const MyComponent = () => ( - - - - - - - - - - List.Accordion can be wrapped because implementation uses React.Context. - - - - - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### onAccordionPress - -
- - - -
- -### expandedId - -
- - - -
- -### children (required) - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/List/ListIcon.mdx b/docs/6.x/docs/components/List/ListIcon.mdx deleted file mode 100644 index d9eb09a250..0000000000 --- a/docs/6.x/docs/components/List/ListIcon.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: List.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show an icon in a list item. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { List, Palette } from 'react-native-paper'; - -const MyComponent = () => ( - <> - - - - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### icon (required) - -
- - - -
- -### color - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/List/ListItem.mdx b/docs/6.x/docs/components/List/ListItem.mdx deleted file mode 100644 index c962a55f9c..0000000000 --- a/docs/6.x/docs/components/List/ListItem.mdx +++ /dev/null @@ -1,201 +0,0 @@ ---- -title: List.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show tiles inside a List. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { List } from 'react-native-paper'; - -const MyComponent = () => ( - } - /> -); - -export default MyComponent; -``` - - - - - ## Props - ### TouchableRipple props - - - -
- -### title (required) - -
- - - -
- -### description - -
- - - -
- -### left - -
- - - -
- -### right - -
- - - -
- -### onPress - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### containerStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### titleStyle - -
- - - -
- -### descriptionStyle - -
- - - -
- -### titleNumberOfLines - -
- - - -
- -### descriptionNumberOfLines - -
- - - -
- -### titleEllipsizeMode - -
- - - -
- -### descriptionEllipsizeMode - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### descriptionMaxFontSizeMultiplier - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/List/ListSection.mdx b/docs/6.x/docs/components/List/ListSection.mdx deleted file mode 100644 index 8a145c20d0..0000000000 --- a/docs/6.x/docs/components/List/ListSection.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: List.Section ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to group list items. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { List, Palette } from 'react-native-paper'; - -const MyComponent = () => ( - - Some title - } /> - } - /> - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### title - -
- - - -
- -### children (required) - -
- - - -
- -### theme - -
- - - -
- -### titleStyle - -
- - - -
- -### style - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/List/ListSubheader.mdx b/docs/6.x/docs/components/List/ListSubheader.mdx deleted file mode 100644 index 78db549903..0000000000 --- a/docs/6.x/docs/components/List/ListSubheader.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: List.Subheader ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component used to display a header in lists. - - - - - -## Usage -```js -import * as React from 'react'; -import { List } from 'react-native-paper'; - -const MyComponent = () => My List Title; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### theme - -
- - - -
- -### style - -
- - - -
- -### maxFontSizeMultiplier - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/List/_meta.json b/docs/6.x/docs/components/List/_meta.json deleted file mode 100644 index 4ba864114c..0000000000 --- a/docs/6.x/docs/components/List/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - "ListAccordion", - "ListAccordionGroup", - "ListIcon", - "ListItem", - "ListSection", - "ListSubheader" -] diff --git a/docs/6.x/docs/components/Menu/Menu.mdx b/docs/6.x/docs/components/Menu/Menu.mdx deleted file mode 100644 index 1d1ee928b2..0000000000 --- a/docs/6.x/docs/components/Menu/Menu.mdx +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: Menu ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Button, Menu, Divider, PaperProvider } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const openMenu = () => setVisible(true); - - const closeMenu = () => setVisible(false); - - return ( - - - Show menu}> - {}} title="Item 1" /> - {}} title="Item 2" /> - - {}} title="Item 3" /> - - - - ); -}; - -export default MyComponent; -``` - -### Note -When using `Menu` within a React Native's `Modal` component, you need to wrap all -`Modal` contents within a `PaperProvider` in order for the menu to show. This -wrapping is not necessary if you use Paper's `Modal` instead. - - - ## Props - - - - -
- -### visible (required) - -
- - - -
- -### anchor (required) - -
- - - -
- -### anchorPosition - -
- - - -
- -### statusBarHeight - -
- - - -
- -### onDismiss - -
- - - -
- -### overlayAccessibilityLabel - -
- - - -
- -### children (required) - -
- - - -
- -### contentStyle - -
- - - -
- -### style - -
- - - -
- -### elevation - -
- - - -
- -### mode - -
- - - -
- -### theme - -
- - - -
- -### keyboardShouldPersistTaps - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Menu/MenuItem.mdx b/docs/6.x/docs/components/Menu/MenuItem.mdx deleted file mode 100644 index b3fb4e5110..0000000000 --- a/docs/6.x/docs/components/Menu/MenuItem.mdx +++ /dev/null @@ -1,214 +0,0 @@ ---- -title: Menu.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to show a single list item inside a Menu. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Menu } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} title="Redo" /> - {}} title="Undo" /> - {}} title="Cut" disabled /> - {}} title="Copy" disabled /> - {}} title="Paste" /> - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### title (required) - -
- - - -
- -### leadingIcon Renamed from 'icon' to 'leadingIcon' in v5.x - -
- - - -
- -### trailingIcon Available in v5.x with theme version 3 - -
- - - -
- -### disabled - -
- - - -
- -### dense Available in v5.x with theme version 3 - -
- - - -
- -### background - -
- - - -
- -### onPress - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### style - -
- - - -
- -### containerStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### titleStyle - -
- - - -
- -### theme - -
- - - -
- -### hitSlop - -
- - - -
- -### testID - -
- - - -
- -### aria-label - -
- - - -
- -### aria-checked - -
- - - -
- -### aria-selected - -
- - - -
- -### aria-busy - -
- - - -
- -### aria-expanded - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Menu/_meta.json b/docs/6.x/docs/components/Menu/_meta.json deleted file mode 100644 index 50192eb25e..0000000000 --- a/docs/6.x/docs/components/Menu/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "Menu", - "MenuItem" -] diff --git a/docs/6.x/docs/components/Modal.mdx b/docs/6.x/docs/components/Modal.mdx deleted file mode 100644 index f94e758cad..0000000000 --- a/docs/6.x/docs/components/Modal.mdx +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Modal ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -The Modal component is a simple way to present content above an enclosing view. -To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal/Portal) component. -Note that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const showModal = () => setVisible(true); - const hideModal = () => setVisible(false); - - const containerStyle = { padding: 20 }; - - return ( - - - - Example Modal. Click outside this area to dismiss. - - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### dismissable - -
- - - -
- -### dismissableBackButton - -
- - - -
- -### onDismiss - -
- - - -
- -### overlayAccessibilityLabel - -
- - - -
- -### visible - -
- - - -
- -### children (required) - -
- - - -
- -### contentContainerStyle - -
- - - -
- -### contentBackgroundColor - -
- - - -
- -### contentBorderRadius - -
- - - -
- -### contentElevation - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Portal/Portal.mdx b/docs/6.x/docs/components/Portal/Portal.mdx deleted file mode 100644 index 027dec2299..0000000000 --- a/docs/6.x/docs/components/Portal/Portal.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Portal ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Portal allows rendering a component at a different place in the parent tree. -You can use it to render content which should appear above other elements, similar to `Modal`. -It requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree. -Note that if you're using the `Provider` component, this already includes a `Portal.Host`. - - - - - -## Usage -```js -import * as React from 'react'; -import { Portal, Text } from 'react-native-paper'; - -const MyComponent = () => ( - - This is rendered at a different place - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### theme (required) - -
- - - - - - - ## More Examples -
- Toggle to grab more examples - -
- - - - - diff --git a/docs/6.x/docs/components/Portal/PortalHost.mdx b/docs/6.x/docs/components/Portal/PortalHost.mdx deleted file mode 100644 index 9962cea0a9..0000000000 --- a/docs/6.x/docs/components/Portal/PortalHost.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Portal.Host ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Portal host renders all of its children `Portal` elements. -For example, you can wrap a screen in `Portal.Host` to render items above the screen. -If you're using the `Provider` component, it already includes `Portal.Host`. - - - - - -## Usage -```js -import * as React from 'react'; -import { Text } from 'react-native'; -import { Portal } from 'react-native-paper'; - -const MyComponent = () => ( - - Content of the app - -); - -export default MyComponent; -``` - -Here any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`. - - - ## Props - - - - -
- -### children (required) - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Portal/_meta.json b/docs/6.x/docs/components/Portal/_meta.json deleted file mode 100644 index 6e5b7b374b..0000000000 --- a/docs/6.x/docs/components/Portal/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "Portal", - "PortalHost" -] diff --git a/docs/6.x/docs/components/ProgressBar.mdx b/docs/6.x/docs/components/ProgressBar.mdx deleted file mode 100644 index 5dd0044793..0000000000 --- a/docs/6.x/docs/components/ProgressBar.mdx +++ /dev/null @@ -1,119 +0,0 @@ ---- -title: ProgressBar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Progress bar is an indicator used to present progress of some activity in the app. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { ProgressBar, Palette } from 'react-native-paper'; - -const MyComponent = () => ( - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### animatedValue - -
- - - -
- -### progress - -
- - - -
- -### color - -
- - - -
- -### indeterminate - -
- - - -
- -### visible - -
- - - -
- -### fillStyle - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/RadioButton/RadioButton.mdx b/docs/6.x/docs/components/RadioButton/RadioButton.mdx deleted file mode 100644 index e4c0bec6e7..0000000000 --- a/docs/6.x/docs/components/RadioButton/RadioButton.mdx +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: RadioButton ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Radio buttons allow the selection a single option from a set. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { RadioButton } from 'react-native-paper'; - -const MyComponent = () => { - const [checked, setChecked] = React.useState('first'); - - return ( - - setChecked('first')} - /> - setChecked('second')} - /> - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### value (required) - -
- - - -
- -### status - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/RadioButton/RadioButtonAndroid.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonAndroid.mdx deleted file mode 100644 index a71620c84f..0000000000 --- a/docs/6.x/docs/components/RadioButton/RadioButtonAndroid.mdx +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: RadioButton.Android ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - - - - - - -Radio buttons allow the selection a single option from a set. -This component follows platform guidelines for Android, but can be used -on any platform. - - - - - ## Props - ### TouchableRipple props - - - -
- -### value (required) - -
- - - -
- -### status - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/RadioButton/RadioButtonGroup.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonGroup.mdx deleted file mode 100644 index 12493f5c32..0000000000 --- a/docs/6.x/docs/components/RadioButton/RadioButtonGroup.mdx +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: RadioButton.Group ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Radio button group allows to control a group of radio buttons. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { RadioButton, Text } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('first'); - - return ( - setValue(newValue)} value={value}> - - First - - - - Second - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### onValueChange (required) - -
- - - -
- -### value (required) - -
- - - -
- -### children (required) - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/RadioButton/RadioButtonIOS.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonIOS.mdx deleted file mode 100644 index ae561c9972..0000000000 --- a/docs/6.x/docs/components/RadioButton/RadioButtonIOS.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: RadioButton.IOS ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - - - - - - - -Radio buttons allow the selection a single option from a set. -This component follows platform guidelines for iOS, but can be used -on any platform. - - - - - ## Props - ### TouchableRipple props - - - -
- -### value (required) - -
- - - -
- -### status - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### color - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx b/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx deleted file mode 100644 index 4bfea5da3d..0000000000 --- a/docs/6.x/docs/components/RadioButton/RadioButtonItem.mdx +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: RadioButton.Item ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { RadioButton } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('first'); - - return ( - setValue(value)} value={value}> - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### value (required) - -
- - - -
- -### label (required) - -
- - - -
- -### disabled - -
- - - -
- -### background - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### aria-label - -
- - - -
- -### uncheckedColor - -
- - - -
- -### color - -
- - - -
- -### status - -
- - - -
- -### style - -
- - - -
- -### labelStyle - -
- - - -
- -### labelVariant Available in v5.x with theme version 3 - -
- - - -
- -### labelMaxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - -
- -### mode - -
- - - -
- -### position - -
- - - -
- -### hitSlop - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/RadioButton/_meta.json b/docs/6.x/docs/components/RadioButton/_meta.json deleted file mode 100644 index f9c20c781a..0000000000 --- a/docs/6.x/docs/components/RadioButton/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - "RadioButton", - "RadioButtonAndroid", - "RadioButtonGroup", - "RadioButtonIOS", - "RadioButtonItem" -] diff --git a/docs/6.x/docs/components/Searchbar.mdx b/docs/6.x/docs/components/Searchbar.mdx deleted file mode 100644 index 76e475256f..0000000000 --- a/docs/6.x/docs/components/Searchbar.mdx +++ /dev/null @@ -1,248 +0,0 @@ ---- -title: Searchbar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Searchbar is a simple input box where users can type search queries. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Searchbar } from 'react-native-paper'; - -const MyComponent = () => { - const [searchQuery, setSearchQuery] = React.useState(''); - - return ( - - ); -}; - -export default MyComponent; - -``` - - - ## Props - - - - -
- -### placeholder - -
- - - -
- -### value (required) - -
- - - -
- -### onChangeText - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### icon - -
- - - -
- -### iconColor - -
- - - -
- -### onIconPress - -
- - - -
- -### onClearIconPress - -
- - - -
- -### searchAccessibilityLabel - -
- - - -
- -### clearIcon - -
- - - -
- -### clearAccessibilityLabel - -
- - - -
- -### traileringIcon Available in v5.x with theme version 3 - -
- - - -
- -### traileringIconColor Available in v5.x with theme version 3 - -
- - - -
- -### onTraileringIconPress - -
- - - -
- -### traileringIconAccessibilityLabel - -
- - - -
- -### right Available in v5.x with theme version 3 - -
- - - -
- -### showDivider Available in v5.x with theme version 3 - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### inputStyle - -
- - - -
- -### style - -
- - - -
- -### loading - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - -
- -### ref - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/SegmentedButtons/SegmentedButtons.mdx b/docs/6.x/docs/components/SegmentedButtons/SegmentedButtons.mdx deleted file mode 100644 index fbd734c612..0000000000 --- a/docs/6.x/docs/components/SegmentedButtons/SegmentedButtons.mdx +++ /dev/null @@ -1,107 +0,0 @@ ---- -title: SegmentedButtons ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Segmented buttons can be used to select options, switch views or sort elements. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { SafeAreaView, StyleSheet } from 'react-native'; -import { SegmentedButtons } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState(''); - - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - }, -}); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### buttons (required) - -
- - - -
- -### density - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/SegmentedButtons/_meta.json b/docs/6.x/docs/components/SegmentedButtons/_meta.json deleted file mode 100644 index 85b4317790..0000000000 --- a/docs/6.x/docs/components/SegmentedButtons/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "SegmentedButtons" -] diff --git a/docs/6.x/docs/components/Snackbar.mdx b/docs/6.x/docs/components/Snackbar.mdx deleted file mode 100644 index f9ab14a337..0000000000 --- a/docs/6.x/docs/components/Snackbar.mdx +++ /dev/null @@ -1,218 +0,0 @@ ---- -title: Snackbar ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped. - -Note: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the "More Examples` section. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View, StyleSheet } from 'react-native'; -import { Button, Snackbar } from 'react-native-paper'; - -const MyComponent = () => { - const [visible, setVisible] = React.useState(false); - - const onToggleSnackBar = () => setVisible(!visible); - - const onDismissSnackBar = () => setVisible(false); - - return ( - - - { - // Do something - }, - }}> - Hey there! I'm a Snackbar. - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'space-between', - }, -}); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### visible (required) - -
- - - -
- -### action - -
- - - -
- -### icon Available in v5.x with theme version 3 - -
- - - -
- -### onIconPress - -
- - - -
- -### iconAccessibilityLabel Available in v5.x with theme version 3 - -
- - - -
- -### duration - -
- - - -
- -### onDismiss (required) - -
- - - -
- -### children (required) - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### maxFontSizeMultiplier - -
- - - -
- -### wrapperStyle - -
- - - -
- -### contentStyle - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - -
- -### testID - -
- - - - - - - ## More Examples -
- Toggle to grab more examples - -
- - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Surface.mdx b/docs/6.x/docs/components/Surface.mdx deleted file mode 100644 index 4add0da5c0..0000000000 --- a/docs/6.x/docs/components/Surface.mdx +++ /dev/null @@ -1,247 +0,0 @@ ---- -title: Surface ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Surface is a basic container that can give depth to an element with elevation shadow. - -On Android, Surface uses the native `elevation` style, -and falls back to shadows that approximate the elevation on other platforms. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Surface, Text } from 'react-native-paper'; -import { StyleSheet } from 'react-native'; - -const MyComponent = () => ( - - Surface - -); - -export default MyComponent; - -const styles = StyleSheet.create({ - surface: { - height: 80, - width: 80, - padding: 8, - alignItems: 'center', - justifyContent: 'center', - }, -}); -``` - - - ## Props - - - - -
- -### backgroundColor - -
- - - -
- -### borderRadius - -
- - - -
- -### borderBottomEndRadius - -
- - - -
- -### borderBottomLeftRadius - -
- - - -
- -### borderBottomRightRadius - -
- - - -
- -### borderBottomStartRadius - -
- - - -
- -### borderEndEndRadius - -
- - - -
- -### borderEndStartRadius - -
- - - -
- -### borderStartEndRadius - -
- - - -
- -### borderStartStartRadius - -
- - - -
- -### borderTopEndRadius - -
- - - -
- -### borderTopLeftRadius - -
- - - -
- -### borderTopRightRadius - -
- - - -
- -### borderTopStartRadius - -
- - - -
- -### borderCurve - -
- - - -
- -### transitionDuration - -
- - - -
- -### style - -
- - - -
- -### elevation Available in v5.x with theme version 3 - -
- - - -
- -### mode Available in v5.x with theme version 3 - -
- - - -
- -### theme - -
- - - -
- -### children (required) - -
- - - -
- -### testID - -
- - - -
- -### ref - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Switch/Switch.mdx b/docs/6.x/docs/components/Switch/Switch.mdx deleted file mode 100644 index 41df316693..0000000000 --- a/docs/6.x/docs/components/Switch/Switch.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: Switch ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Material 3 toggle between two mutually exclusive states (on / off). - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Switch } from 'react-native-paper'; - -const Example = () => { - const [on, setOn] = React.useState(false); - return ; -}; -``` - -## Theming -Customize by overriding these `theme.colors` roles: -- `primary` / `onPrimary`: selected track + icon / selected handle -- `primaryContainer`: selected handle on hover, press -- `surfaceContainerHighest`: unselected track + icon -- `outline`: unselected resting handle, unselected track outline -- `onSurfaceVariant`: unselected handle on hover, press -- `onSurface`: disabled track, handle, and icon fills -- `surface`: disabled selected handle -- `secondary`: focus indicator - - - ## Props - - - - -
- -### value - -
- - - -
- -### onValueChange - -
- - - -
- -### disabled - -
- - - -
- -### checkedIcon - -
- - - -
- -### uncheckedIcon - -
- - - -
- -### style - -
- - - -
- -### testID - -
- - - -
- -### theme - -
- - - -
- -### aria-label - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/Switch/_meta.json b/docs/6.x/docs/components/Switch/_meta.json deleted file mode 100644 index 3bc6b9e49c..0000000000 --- a/docs/6.x/docs/components/Switch/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Switch" -] diff --git a/docs/6.x/docs/components/Text/Text.mdx b/docs/6.x/docs/components/Text/Text.mdx deleted file mode 100644 index 2e38542594..0000000000 --- a/docs/6.x/docs/components/Text/Text.mdx +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: Text ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Typography component showing styles complied with passed `variant` prop and supported by the type system. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { Text } from 'react-native-paper'; - -const MyComponent = () => ( - <> - Display Large - Display Medium - Display small - - Headline Large - Headline Medium - Headline Small - - Title Large - Title Medium - Title Small - - Body Large - Body Medium - Body Small - - Label Large - Label Medium - Label Small - -); - -export default MyComponent; -``` - - - - - ## Props - ### Text props - - - -
- -### variant Available in v5.x with theme version 3 - -
- - - -
- -### children (required) - -
- - - -
- -### theme - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Text/_meta.json b/docs/6.x/docs/components/Text/_meta.json deleted file mode 100644 index 2837ff31af..0000000000 --- a/docs/6.x/docs/components/Text/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Text" -] diff --git a/docs/6.x/docs/components/TextInput/TextInput.mdx b/docs/6.x/docs/components/TextInput/TextInput.mdx deleted file mode 100644 index 5a3fb96042..0000000000 --- a/docs/6.x/docs/components/TextInput/TextInput.mdx +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: TextInput ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A text input lets users enter and edit text. It shows an optional floating label, -supports `filled` and `outlined` variants, optional supporting text (including -error state), and start/end accessories. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { TextInput } from 'react-native-paper'; - -const MyComponent = () => { - const [text, setText] = React.useState(''); - - const searchAccessory = (accessoryProps) => ( - - ); - - const clearAccessory = ({ style, disabled }) => ( - setText('')} - role="button" - aria-label="Clear text" - > - - - ); - - return ( - - ); -}; - -export default MyComponent; -``` - - - - - ## Props - ### TextInput props - - - -
- -### ref - -
- - - -
- -### variant - -
- - - -
- -### error - -
- - - -
- -### label - -
- - - -
- -### supportingText - -
- - - -
- -### counter - -
- - - -
- -### disabled - -
- - - -
- -### prefix - -
- - - -
- -### suffix - -
- - - -
- -### theme - -
- - - -
- -### startAccessory - -
- - - -
- -### endAccessory - -
- - - -
- -### render - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/TextInput/TextInputIcon.mdx b/docs/6.x/docs/components/TextInput/TextInputIcon.mdx deleted file mode 100644 index 9076293c9f..0000000000 --- a/docs/6.x/docs/components/TextInput/TextInputIcon.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: TextInput.Icon ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A component to render a leading / trailing icon in the TextInput -(return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as -`TextInputAccessoryProps`, which TextInput passes into those render props. - - - - - -## Usage -```js -import * as React from 'react'; -import { TextInput } from 'react-native-paper'; - -const MyComponent = () => { - const [text, setText] = React.useState(''); - - const searchAccessory = (props) => ( - - ); - - const clearAccessory = (props) => ( - setText('')} /> - ); - - return ( - - ); -}; - -export default MyComponent; -``` - - - - - ## Props - ### IconButton props - - - -
- -### style (required) - -
- - - -
- -### multiline (required) - -
- - - -
- -### disabled (required) - -
- - - -
- -### error (required) - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/TextInput/_meta.json b/docs/6.x/docs/components/TextInput/_meta.json deleted file mode 100644 index 0d94a124d7..0000000000 --- a/docs/6.x/docs/components/TextInput/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "TextInput", - "TextInputIcon" -] diff --git a/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx b/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx deleted file mode 100644 index e359e46802..0000000000 --- a/docs/6.x/docs/components/ToggleButton/ToggleButton.mdx +++ /dev/null @@ -1,154 +0,0 @@ ---- -title: ToggleButton ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons, -a group should share a common container. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { ToggleButton } from 'react-native-paper'; - -const ToggleButtonExample = () => { - const [status, setStatus] = React.useState('checked'); - - const onButtonToggle = value => { - setStatus(status === 'checked' ? 'unchecked' : 'checked'); - }; - - return ( - - ); -}; - -export default ToggleButtonExample; - -``` - - - ## Props - - - - -
- -### icon (required) - -
- - - -
- -### size - -
- - - -
- -### iconColor - -
- - - -
- -### disabled - -
- - - -
- -### aria-label - -
- - - -
- -### onPress - -
- - - -
- -### value - -
- - - -
- -### status - -
- - - -
- -### style - -
- - - -
- -### theme - -
- - - -
- -### ref - -
- - - -
- -### testID - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/ToggleButton/ToggleButtonGroup.mdx b/docs/6.x/docs/components/ToggleButton/ToggleButtonGroup.mdx deleted file mode 100644 index efc0aa4037..0000000000 --- a/docs/6.x/docs/components/ToggleButton/ToggleButtonGroup.mdx +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: ToggleButton.Group ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Toggle group allows to control a group of toggle buttons. -It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow). - - - - - - - -## Usage -```js -import * as React from 'react'; -import { ToggleButton } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('left'); - - return ( - setValue(value)} - value={value} - > - - - - ); -}; - -export default MyComponent; -``` - - - ## Props - - - - -
- -### onValueChange (required) - -
- - - -
- -### value (required) - -
- - - -
- -### children (required) - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/ToggleButton/ToggleButtonRow.mdx b/docs/6.x/docs/components/ToggleButton/ToggleButtonRow.mdx deleted file mode 100644 index 7274440515..0000000000 --- a/docs/6.x/docs/components/ToggleButton/ToggleButtonRow.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: ToggleButton.Row ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Toggle button row renders a group of toggle buttons in a row. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { ToggleButton } from 'react-native-paper'; - -const MyComponent = () => { - const [value, setValue] = React.useState('left'); - - return ( - setValue(value)} value={value}> - - - - ); -}; - -export default MyComponent; - -``` - - - ## Props - - - - -
- -### onValueChange (required) - -
- - - -
- -### value (required) - -
- - - -
- -### children (required) - -
- - - -
- -### style - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/ToggleButton/_meta.json b/docs/6.x/docs/components/ToggleButton/_meta.json deleted file mode 100644 index 935c2b9584..0000000000 --- a/docs/6.x/docs/components/ToggleButton/_meta.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "ToggleButton", - "ToggleButtonGroup", - "ToggleButtonRow" -] diff --git a/docs/6.x/docs/components/Tooltip/Tooltip.mdx b/docs/6.x/docs/components/Tooltip/Tooltip.mdx deleted file mode 100644 index 2e4cc57fe8..0000000000 --- a/docs/6.x/docs/components/Tooltip/Tooltip.mdx +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: Tooltip ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -Tooltips display informative text when users hover over, focus on, or tap an element. - -Plain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { IconButton, Tooltip } from 'react-native-paper'; - -const MyComponent = () => ( - - {}} /> - -); - -export default MyComponent; -``` - - - ## Props - - - - -
- -### children (required) - -
- - - -
- -### enterTouchDelay - -
- - - -
- -### leaveTouchDelay - -
- - - -
- -### title (required) - -
- - - -
- -### titleMaxFontSizeMultiplier - -
- - - -
- -### theme - -
- - - - - - - - - ## Theme colors - - - - - diff --git a/docs/6.x/docs/components/Tooltip/_meta.json b/docs/6.x/docs/components/Tooltip/_meta.json deleted file mode 100644 index c39a54301f..0000000000 --- a/docs/6.x/docs/components/Tooltip/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "Tooltip" -] diff --git a/docs/6.x/docs/components/TouchableRipple/TouchableRipple.mdx b/docs/6.x/docs/components/TouchableRipple/TouchableRipple.mdx deleted file mode 100644 index ba5c8bfcc4..0000000000 --- a/docs/6.x/docs/components/TouchableRipple/TouchableRipple.mdx +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: TouchableRipple ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -A wrapper for views that should respond to touches. -Provides a material "ink ripple" interaction effect for supported platforms (>= Android Lollipop). -On unsupported platforms, it falls back to a highlight effect. - - - - - - - -## Usage -```js -import * as React from 'react'; -import { View } from 'react-native'; -import { Text, TouchableRipple } from 'react-native-paper'; - -const MyComponent = () => ( - console.log('Pressed')} - rippleColor="rgba(0, 0, 0, .32)" - > - Press anywhere - -); - -export default MyComponent; -``` - - - - - ## Props - ### Pressable props - - - -
- -### borderless - -
- - - -
- -### background - -
- - - -
- -### centered - -
- - - -
- -### disabled - -
- - - -
- -### onPress - -
- - - -
- -### onLongPress - -
- - - -
- -### onPressIn - -
- - - -
- -### onPressOut - -
- - - -
- -### rippleColor - -
- - - -
- -### underlayColor - -
- - - -
- -### children (required) - -
- - - -
- -### style - -
- - - -
- -### ref - -
- - - -
- -### theme - -
- - - - - - - - - - diff --git a/docs/6.x/docs/components/TouchableRipple/_meta.json b/docs/6.x/docs/components/TouchableRipple/_meta.json deleted file mode 100644 index 91b70a001b..0000000000 --- a/docs/6.x/docs/components/TouchableRipple/_meta.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - "TouchableRipple" -] diff --git a/docs/6.x/docs/components/_meta.json b/docs/6.x/docs/components/_meta.json deleted file mode 100644 index 6f793d41a3..0000000000 --- a/docs/6.x/docs/components/_meta.json +++ /dev/null @@ -1,173 +0,0 @@ -[ - "ActivityIndicator", - { - "type": "dir", - "name": "Appbar", - "label": "Appbar", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Avatar", - "label": "Avatar", - "collapsible": true, - "collapsed": false - }, - "Badge", - "Banner", - { - "type": "dir", - "name": "BottomNavigation", - "label": "BottomNavigation", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Button", - "label": "Button", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Card", - "label": "Card", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Checkbox", - "label": "Checkbox", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Chip", - "label": "Chip", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "DataTable", - "label": "DataTable", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Dialog", - "label": "Dialog", - "collapsible": true, - "collapsed": false - }, - "Divider", - { - "type": "dir", - "name": "Drawer", - "label": "Drawer", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "FAB", - "label": "FAB", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "IconButton", - "label": "IconButton", - "collapsible": true, - "collapsed": false - }, - "Icon", - { - "type": "dir", - "name": "List", - "label": "List", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Menu", - "label": "Menu", - "collapsible": true, - "collapsed": false - }, - "Modal", - { - "type": "dir", - "name": "Portal", - "label": "Portal", - "collapsible": true, - "collapsed": false - }, - "ProgressBar", - { - "type": "dir", - "name": "RadioButton", - "label": "RadioButton", - "collapsible": true, - "collapsed": false - }, - "Searchbar", - { - "type": "dir", - "name": "SegmentedButtons", - "label": "SegmentedButtons", - "collapsible": true, - "collapsed": false - }, - "Snackbar", - "Surface", - { - "type": "dir", - "name": "Switch", - "label": "Switch", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "TextInput", - "label": "TextInput", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "ToggleButton", - "label": "ToggleButton", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Tooltip", - "label": "Tooltip", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "TouchableRipple", - "label": "TouchableRipple", - "collapsible": true, - "collapsed": false - }, - { - "type": "dir", - "name": "Text", - "label": "Text", - "collapsible": true, - "collapsed": false - } -] diff --git a/docs/6.x/docs/guides/theming-with-react-navigation.md b/docs/6.x/docs/guides/theming-with-react-navigation.md index 06be61e5c8..ada86a611c 100644 --- a/docs/6.x/docs/guides/theming-with-react-navigation.md +++ b/docs/6.x/docs/guides/theming-with-react-navigation.md @@ -273,7 +273,7 @@ const Header = ({ scene }) => { And now you can switch between light and dark theme! -![paperGuide1](/react-native-paper/screenshots/themingWithReactNavigationDarkLightSwitch.gif) +![paperGuide1](../../../public/screenshots/themingWithReactNavigationDarkLightSwitch.gif) Thanks to the linking of themes that we did earlier, switching themes can be controlled with only one piece of state. diff --git a/docs/6.x/docs/guides/theming.mdx b/docs/6.x/docs/guides/theming.mdx index 261fe128be..58836dc167 100644 --- a/docs/6.x/docs/guides/theming.mdx +++ b/docs/6.x/docs/guides/theming.mdx @@ -186,7 +186,7 @@ Created schemes are following the Material Design 3 color system and covering co Passed source color into the util is translated into tones to automatically provide the range of tones that map to color roles. -![customColors](/react-native-paper/screenshots/custom-colors.png) +![customColors](../../../public/screenshots/custom-colors.png) _Source: [Material You Color System](https://m3.material.io/styles/color/the-color-system/custom-colors)_ diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index cb95b349af..0000000000 --- a/docs/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Website - -This website is built using [Rspress](https://rspress.dev/) with the Callstack theme preset. - -### Installation - -``` -$ yarn -``` - -### Local Development - -``` -$ yarn start -``` - -This command regenerates the versioned content and starts the local Rspress development server. Most changes are reflected live without having to restart the server. - -### Build - -``` -$ yarn build -``` - -This command regenerates the versioned content and outputs the production site to the `build` directory. diff --git a/docs/component-docs-plugin/generatePageMDX.ts b/docs/component-docs-plugin/generatePageMDX.ts deleted file mode 100644 index 274f3622d8..0000000000 --- a/docs/component-docs-plugin/generatePageMDX.ts +++ /dev/null @@ -1,266 +0,0 @@ -import componentDocsConfig from '../component-docs.config.ts'; - -type ComponentProp = { - description?: string; - required?: boolean; -}; - -export type ComponentDoc = { - title: string; - description: string; - data: { - props: Record; - }; -}; - -type ExtendsAttribute = { - name: string; - link: string; -}; - -const { customFields } = componentDocsConfig; - -function renderBadge(annotation: string) { - const [annotType, ...annotLabel] = annotation.split(' '); - - return `${annotLabel.join(' ')}`; -} - -function generateKnownIssues(componentName: string) { - const componentKnownIssues = customFields.knownIssues[componentName]; - - if (!componentKnownIssues) { - return ``; - } - - const issues = Object.entries(componentKnownIssues) - .map(([key, value]) => { - return ` -
  • - ${key} -
  • - `; - }) - .join(''); - - return ` - ## Known Issues - -
    -
      - ${issues} -
    -
    - `; -} - -function generateMoreExamples(componentName: string) { - const componentMoreExamples = customFields.moreExamples[componentName]; - - if (!componentMoreExamples) { - return ``; - } - - const links = Object.entries(componentMoreExamples) - .map(([key, value]) => { - return ` -
  • - ${key} -
  • - `; - }) - .join(''); - - return ` - ## More Examples -
    - Toggle to grab more examples -
      - ${links} -
    -
    - `; -} - -function generateThemeColors( - componentName: string, - themeColorsData: string | undefined -) { - if (!themeColorsData) { - return ``; - } - return ` - ## Theme colors - - - `; -} - -function generateScreenshots(screenshotData: string | undefined) { - if (!screenshotData) { - return ``; - } - - return ` - - `; -} - -function generatePropsTable( - data: Record, - link: string, - extendsAttributes: ExtendsAttribute[] -) { - const ANNOTATION_OPTIONAL = '@optional'; - const ANNOTATION_INTERNAL = '@internal'; - - const props = Object.entries(data) - .map(([prop, value]) => { - if (!value.description) { - value.description = ''; - } - // Remove @optional annotations from descriptions. - if (value.description.includes(ANNOTATION_OPTIONAL)) { - value.description = value.description.replace(ANNOTATION_OPTIONAL, ''); - } - // Hide props with @internal annotations. - if (value.description.includes(ANNOTATION_INTERNAL)) { - return; - } - - let leadingBadge = ''; - let descriptionByLines = value.description?.split('\n'); - - // Find leading badge and put it next after prop name. - if (descriptionByLines?.[0].includes('@')) { - leadingBadge = descriptionByLines[0]; - } - - return ` -
    - -### ${prop} ${value.required ? '(required)' : ''} ${ - leadingBadge && renderBadge(leadingBadge) - } - -
    - - - `; - }) - .join(''); - - return ` - ## Props - ${ - extendsAttributes.length === 0 - ? `` - : `### ${extendsAttributes?.[0]?.name}` - } - ${extendsAttributes - .map((attr) => { - return ``; - }) - .join('')} - - ${props} - `; -} - -function generateExtendsAttributes(doc: ComponentDoc) { - const ANNOTATION_EXTENDS = '@extends'; - - const extendsAttributes: ExtendsAttribute[] = []; - doc.description - .split('\n') - .filter((line) => { - if (line.startsWith(ANNOTATION_EXTENDS)) { - const parts = line.split(' ').slice(1); - const link = parts.pop() ?? ''; - extendsAttributes.push({ - name: parts.join(' '), - link, - }); - return false; - } - return true; - }) - .join('\n'); - - return extendsAttributes; -} - -function generateExtendedExamples( - usage: string, - extendedExamplesData: string | undefined -) { - if (!extendedExamplesData) { - return usage; - } - - const data = JSON.parse(extendedExamplesData); - const exampleHeader = Object.keys(data)[0]; - - return ` - ${usage} - - ### ${exampleHeader} - - `; -} - -export default function generatePageMDX(doc: ComponentDoc, link: string) { - const summaryRegex = /([\s\S]*?)## Usage/; - - const description = doc.description - .replace(/<\/br>/g, '') - .replace(/style="[a-zA-Z0-9:;.\s()\-,]*"/gi, '') - .replace(/src="screenshots/g, 'src="/react-native-paper/screenshots') - .replace(/\(\.\/Portal\)/g, '(./Portal/Portal)') - .replace(/\(\.\.\/Portal\)/g, '(../Portal/Portal)') - .replace(/@extends.+$/, ''); - - const summaryMatch = summaryRegex.exec(description); - const summary = summaryMatch ? summaryMatch[1] : ''; - const usage = description.replace(summary, ''); - - const themeColorsData = JSON.stringify(customFields.themeColors[doc.title]); - const screenshotData = JSON.stringify(customFields.screenshots[doc.title]); - const extendedExamplesData = JSON.stringify( - customFields.extendedExamples[doc.title] - ); - - const extendsAttributes = generateExtendsAttributes(doc); - - const mdx = ` ---- -title: ${doc.title} ---- - -import PropTable from '@docs/components/PropTable.tsx'; -import ExtendsLink from '@docs/components/ExtendsLink.tsx'; -import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; -import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; -import ExtendedExample from '@docs/components/ExtendedExample.tsx'; - -${summary} - -${generateScreenshots(screenshotData)} - -${generateExtendedExamples(usage, extendedExamplesData)} - -${generatePropsTable(doc.data.props, link, extendsAttributes)} - -${generateMoreExamples(doc.title)} - -${generateThemeColors(doc.title, themeColorsData)} - -${generateKnownIssues(doc.title)} -`; - - return mdx - .slice(1) - .split('\n') - .map((line) => line.trimEnd()) - .join('\n'); -} diff --git a/docs/component-docs-plugin/useDocs.tsx b/docs/component-docs-plugin/useDocs.tsx deleted file mode 100644 index e4e9edc636..0000000000 --- a/docs/component-docs-plugin/useDocs.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import componentDocs5x from '../src/data/componentDocs5x.json'; -import componentDocs6x from '../src/data/componentDocs6x.json'; - -export interface ComponentDocsPluginData { - docs: { [key in string]: PageDoc }; -} - -export interface PageDoc { - filepath: string; - title: string; - description: string; - link: string; - data: Data; - type: string; - dependencies: string[]; - group?: string; -} - -export interface Data { - description: string; - displayName?: string; - props: { [key in string]: Prop }; -} - -export interface Prop { - required: boolean; - tsType?: { - name: string; - raw?: string; - } | null; - description: string; - defaultValue?: { - value: string; - } | null; -} - -const versionedDocs: { [versionName: string]: ComponentDocsPluginData } = { - '5.x': componentDocs5x, - '6.x': componentDocs6x, -}; - -function useDoc(withPath: string, versionName = '5.x') { - return versionedDocs[versionName]?.docs?.[withPath]; -} - -export default useDoc; diff --git a/docs/component-docs.config.ts b/docs/component-docs.config.ts index bad25d4eec..4196e8e7bd 100644 --- a/docs/component-docs.config.ts +++ b/docs/component-docs.config.ts @@ -7,11 +7,19 @@ import { themeColors } from './src/data/themeColors.ts'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -export type Pages = Record>; +export type Page = + | string + | { + source: string; + component?: string; + props?: string; + title?: string; + }; + +export type Pages = Record>; type ComponentDocsConfig = { - docsRootDir: string; - libsRootDir: string; + sourceRootDir: string; pages: Pages; customFields: { moreExamples: Record>; @@ -84,7 +92,11 @@ const pages = { FAB: { FAB: 'FAB/FAB', FABExtended: 'FAB/Extended', - FABMenu: 'FAB/Menu', + FABMenu: { + source: 'FAB/Menu', + props: 'MenuProps', + title: 'FAB.Menu', + }, }, IconButton: { IconButton: 'IconButton/IconButton', @@ -104,7 +116,10 @@ const pages = { }, Modal: 'Modal', Portal: { - Portal: 'Portal/Portal', + Portal: { + source: 'Portal/Portal', + component: 'Portal', + }, PortalHost: 'Portal/PortalHost', }, ProgressBar: 'ProgressBar', @@ -125,8 +140,14 @@ const pages = { Switch: 'Switch/Switch', }, TextInput: { - TextInput: 'TextInput/TextInput', - TextInputIcon: 'TextInput/TextInputIcon', + TextInput: { + source: 'TextInput/TextInput', + props: 'TextInputProps', + }, + TextInputIcon: { + source: 'TextInput/TextInputIcon', + props: 'TextInputAccessoryProps', + }, }, ToggleButton: { ToggleButton: 'ToggleButton/ToggleButton', @@ -140,13 +161,15 @@ const pages = { TouchableRipple: 'TouchableRipple/TouchableRipple', }, Text: { - Text: 'Typography/Text', + Text: { + source: 'Typography/Text', + component: 'Text', + }, }, }; const componentDocsConfig: ComponentDocsConfig = { - docsRootDir: path.join(__dirname, '6.x', 'docs', 'components'), - libsRootDir: path.join(__dirname, '..', 'src', 'components'), + sourceRootDir: path.join(__dirname, '..', 'src', 'components'), pages, customFields: { moreExamples: { diff --git a/docs/component-docs.d.ts b/docs/component-docs.d.ts deleted file mode 100644 index a270ae0ce9..0000000000 --- a/docs/component-docs.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -declare module 'component-docs/dist/parsers/component.js' { - type ComponentDoc = { - title: string; - description: string; - data: { - props: Record< - string, - { - description?: string; - required?: boolean; - } - >; - }; - }; - - const componentDocsParser: { - default: (filePath: string, options: { root: string }) => ComponentDoc; - }; - - export default componentDocsParser; -} diff --git a/docs/package.json b/docs/package.json index af0bf70b9c..50d5a6c934 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,12 +4,11 @@ "private": true, "type": "module", "scripts": { - "dev": "yarn generate && rspress dev", - "start": "yarn dev", - "build": "yarn generate && rspress build", + "dev": "rspress dev", + "build": "rspress build", "preview": "rspress preview", "typecheck": "tsc --noEmit", - "generate": "node scripts/generate-rspress-content.ts && node scripts/generate-current-component-docs.ts" + "generate:5x": "node scripts/generate-5x-docs.ts origin/5.0" }, "dependencies": { "@callstack/react-theme-provider": "^3.0.9", @@ -49,6 +48,6 @@ ] }, "engines": { - "node": ">=20" + "node": ">=24" } } diff --git a/docs/plugins/component-docs/index.ts b/docs/plugins/component-docs/index.ts new file mode 100644 index 0000000000..f861aeb914 --- /dev/null +++ b/docs/plugins/component-docs/index.ts @@ -0,0 +1,160 @@ +import type { RspressPlugin } from '@rspress/core'; +import fs from 'node:fs'; +import path from 'node:path'; + +import { createComponentParser } from './parser.ts'; +import { renderComponentPage } from './render.ts'; +import type { ComponentPageConfig } from './types.ts'; +import type { Page, Pages } from '../../component-docs.config.ts'; + +type ComponentDocsPluginOptions = { + customFields: { + extendedExamples: Record; + knownIssues: Record>; + moreExamples: Record>; + screenshots: Record; + themeColors: Record; + }; + pages: Pages; + sourceRootDir: string; + tsconfigPath: string; + version: string; +}; + +type MetaEntry = + | { + type: 'custom-link'; + label: string; + link: string; + } + | { + type: 'dir'; + name: string; + label: string; + collapsible: true; + collapsed: false; + }; + +const isPageConfig = (page: Page | Record): page is Page => + typeof page === 'string' || 'source' in page; + +const normalizePage = (page: Page): ComponentPageConfig => + typeof page === 'string' ? { source: page } : page; + +const flattenPages = (pages: Pages) => + Object.entries(pages).flatMap(([entryName, page]) => { + if (isPageConfig(page)) { + return [{ page: normalizePage(page), route: entryName }]; + } + + return Object.entries(page).map(([pageName, pageConfig]) => ({ + page: normalizePage(pageConfig), + route: `${entryName}/${pageName}`, + })); + }); + +const writeJson = (filePath: string, value: unknown) => { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`); +}; + +const createComponentLink = (route: string, title: string): MetaEntry => ({ + type: 'custom-link', + label: title, + link: `/docs/components/${route}`, +}); + +const writeNavigation = ( + rootDir: string, + options: ComponentDocsPluginOptions, + titles: ReadonlyMap +) => { + const componentsDir = path.join( + rootDir, + options.version, + 'docs', + 'components' + ); + const getTitle = (route: string) => { + const title = titles.get(route); + + if (!title) { + throw new Error(`Missing parsed component docs for ${route}`); + } + + return title; + }; + + fs.rmSync(componentsDir, { recursive: true, force: true }); + writeJson( + path.join(componentsDir, '_meta.json'), + Object.entries(options.pages).map(([entryName, page]): MetaEntry => { + if (isPageConfig(page)) { + return createComponentLink(entryName, getTitle(entryName)); + } + + return { + type: 'dir', + name: entryName, + label: entryName, + collapsible: true, + collapsed: false, + }; + }) + ); + + for (const [entryName, page] of Object.entries(options.pages)) { + if (isPageConfig(page)) { + continue; + } + + writeJson( + path.join(componentsDir, entryName, '_meta.json'), + Object.keys(page).map((pageName) => { + const route = `${entryName}/${pageName}`; + + return createComponentLink(route, getTitle(route)); + }) + ); + } +}; + +export const pluginComponentDocs = ( + options: ComponentDocsPluginOptions +): RspressPlugin => ({ + name: 'react-native-paper-component-docs', + builderConfig: { + dev: { + watchFiles: { + paths: path.join(options.sourceRootDir, '**/*.tsx'), + type: 'reload-server', + }, + }, + }, + addPages(config) { + if (!config.root) { + throw new Error('Rspress root is required to generate component docs'); + } + + const parse = createComponentParser(options.tsconfigPath); + const pages = flattenPages(options.pages).map(({ page, route }) => ({ + doc: parse(options.sourceRootDir, page), + route, + routePath: `/${options.version}/docs/components/${route}`, + })); + const routes = new Set(pages.map(({ routePath }) => routePath)); + const titles = new Map(pages.map(({ doc, route }) => [route, doc.title])); + + writeNavigation(config.root, options, titles); + + return pages.map(({ doc, routePath }) => ({ + routePath, + content: renderComponentPage( + doc, + options.customFields, + routePath, + routes + ), + })); + }, +}); diff --git a/docs/plugins/component-docs/parser.ts b/docs/plugins/component-docs/parser.ts new file mode 100644 index 0000000000..800bd8f2f3 --- /dev/null +++ b/docs/plugins/component-docs/parser.ts @@ -0,0 +1,500 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import ts from 'typescript'; + +import type { + ComponentDoc, + ComponentPageConfig, + ComponentProp, + ExtendsAttribute, +} from './types.ts'; + +const DEFAULT_PROPS_TYPE = 'Props'; + +const fail = (sourcePath: string, message: string): never => { + throw new Error(`Unable to generate docs for ${sourcePath}: ${message}`); +}; + +const readDocComment = (sourceFile: ts.SourceFile, node: ts.Node) => { + const ranges = ts.getLeadingCommentRanges(sourceFile.text, node.pos) ?? []; + const range = ranges.findLast( + (item) => + item.kind === ts.SyntaxKind.MultiLineCommentTrivia && + sourceFile.text.startsWith('/**', item.pos) + ); + + if (!range) { + return ''; + } + + return sourceFile.text + .slice(range.pos + 3, range.end - 2) + .split('\n') + .map((line) => line.replace(/^\s*\* ?/, '')) + .join('\n') + .trim(); +}; + +const getDefaultExportName = (sourceFile: ts.SourceFile) => { + for (const statement of sourceFile.statements) { + if ( + (ts.isClassDeclaration(statement) || + ts.isFunctionDeclaration(statement)) && + statement.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword + ) + ) { + const name = statement.name; + + if (!name) { + return fail(sourceFile.fileName, 'the default export must be named'); + } + + return name.text; + } + + if (ts.isExportAssignment(statement)) { + const expression = statement.expression; + + if (!ts.isIdentifier(expression)) { + return fail( + sourceFile.fileName, + 'the default export must be an identifier, or set component in the page config' + ); + } + + return expression.text; + } + } + + return fail(sourceFile.fileName, 'a default export was not found'); +}; + +const findComponentDeclaration = ( + sourceFile: ts.SourceFile, + componentName: string +) => { + for (const statement of sourceFile.statements) { + if ( + (ts.isClassDeclaration(statement) || + ts.isFunctionDeclaration(statement)) && + statement.name?.text === componentName + ) { + return { + declaration: statement, + docNode: statement, + }; + } + + if (ts.isVariableStatement(statement)) { + const declaration = statement.declarationList.declarations.find( + (item) => ts.isIdentifier(item.name) && item.name.text === componentName + ); + + if (declaration) { + return { + declaration, + docNode: statement, + }; + } + } + } + + return fail(sourceFile.fileName, `component ${componentName} was not found`); +}; + +const findPropsType = (sourceFile: ts.SourceFile, typeName: string) => { + const declaration = sourceFile.statements.find( + (statement): statement is ts.TypeAliasDeclaration => + ts.isTypeAliasDeclaration(statement) && statement.name.text === typeName + ); + + if (!declaration) { + return fail(sourceFile.fileName, `type alias ${typeName} was not found`); + } + + if ( + !declaration.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword + ) + ) { + fail(sourceFile.fileName, `type alias ${typeName} must be exported`); + } + + return declaration; +}; + +const validatePropsType = ( + sourceFile: ts.SourceFile, + declaration: ts.TypeAliasDeclaration +) => { + const literals: ts.TypeLiteralNode[] = []; + + const visit = (node: ts.TypeNode) => { + if (ts.isTypeLiteralNode(node)) { + literals.push(node); + return; + } + + if (ts.isIntersectionTypeNode(node)) { + node.types.forEach(visit); + return; + } + + if (ts.isParenthesizedTypeNode(node)) { + visit(node.type); + } + }; + + visit(declaration.type); + + if (literals.length !== 1) { + return fail( + sourceFile.fileName, + `type alias ${declaration.name.text} must contain exactly one inline object type` + ); + } + + return declaration; +}; + +const isPathInside = (parentPath: string, childPath: string) => { + const relativePath = path.relative(parentPath, childPath); + + return !relativePath.startsWith('..') && !path.isAbsolute(relativePath); +}; + +const getProps = ( + checker: ts.TypeChecker, + sourceRootDir: string, + sourceFile: ts.SourceFile, + declaration: ts.TypeAliasDeclaration, + defaults: ReadonlyMap +) => + checker + .getPropertiesOfType(checker.getTypeAtLocation(declaration)) + .flatMap((symbol): ComponentProp[] => { + const name = symbol.getName(); + const allDeclarations = symbol.getDeclarations() ?? []; + const declarations = allDeclarations.filter((item) => + isPathInside(sourceRootDir, item.getSourceFile().fileName) + ); + const property = + declarations.find((item) => item.getSourceFile() === sourceFile) ?? + declarations[0] ?? + (defaults.has(name) + ? allDeclarations.find( + (item): item is ts.PropertySignature => + ts.isPropertySignature(item) && Boolean(item.type) + ) + : undefined); + + if (!property) { + return []; + } + + if (!ts.isPropertySignature(property) || !property.type) { + return fail( + sourceFile.fileName, + `unsupported member ${property.getText(property.getSourceFile())} in ${declaration.name.text}` + ); + } + + const propertySourceFile = property.getSourceFile(); + const declarationTypes = declarations.flatMap((item) => + ts.isPropertySignature(item) && item.type + ? [item.type.getText(item.getSourceFile())] + : [] + ); + const hasUnionDeclarations = declarations.some((item) => { + let parent: ts.Node | undefined = item.parent; + + while (parent && !ts.isTypeAliasDeclaration(parent)) { + if (ts.isUnionTypeNode(parent)) { + return true; + } + + parent = parent.parent; + } + + return false; + }); + const type = + (hasUnionDeclarations && new Set(declarationTypes).size > 1) || + !declarations.includes(property) + ? checker.typeToString( + checker.getTypeOfSymbolAtLocation(symbol, property), + property, + ts.TypeFormatFlags.NoTruncation | + ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope + ) + : property.type.getText(propertySourceFile); + + return [ + { + defaultValue: defaults.get(name), + description: readDocComment(propertySourceFile, property), + name, + required: !(symbol.flags & ts.SymbolFlags.Optional), + type, + }, + ]; + }); + +const resolveLiteralDefault = ( + checker: ts.TypeChecker, + expression: ts.Expression, + visitedSymbols = new Set() +): string | undefined => { + let value = expression; + + while ( + ts.isAsExpression(value) || + ts.isParenthesizedExpression(value) || + ts.isSatisfiesExpression(value) + ) { + value = value.expression; + } + + if ( + ts.isStringLiteral(value) || + ts.isNoSubstitutionTemplateLiteral(value) || + ts.isNumericLiteral(value) || + value.kind === ts.SyntaxKind.TrueKeyword || + value.kind === ts.SyntaxKind.FalseKeyword || + value.kind === ts.SyntaxKind.NullKeyword + ) { + return value.getText(value.getSourceFile()); + } + + if (!ts.isIdentifier(value)) { + return undefined; + } + + let symbol = checker.getSymbolAtLocation(value); + + if (!symbol) { + return undefined; + } + + if (symbol.flags & ts.SymbolFlags.Alias) { + symbol = checker.getAliasedSymbol(symbol); + } + + if (visitedSymbols.has(symbol)) { + return undefined; + } + + visitedSymbols.add(symbol); + + const declaration = symbol + .getDeclarations() + ?.find( + (item): item is ts.VariableDeclaration => + ts.isVariableDeclaration(item) && + Boolean(item.initializer) && + ts.isVariableDeclarationList(item.parent) && + Boolean(item.parent.flags & ts.NodeFlags.Const) + ); + + return declaration?.initializer + ? resolveLiteralDefault(checker, declaration.initializer, visitedSymbols) + : undefined; +}; + +const getParameterDefaults = ( + checker: ts.TypeChecker, + sourceFile: ts.SourceFile, + declaration: ts.Declaration +) => { + let parameter: ts.ParameterDeclaration | undefined; + + if (ts.isFunctionDeclaration(declaration)) { + parameter = declaration.parameters[0]; + } else if (ts.isVariableDeclaration(declaration)) { + let initializer = declaration.initializer; + + while ( + initializer && + (ts.isAsExpression(initializer) || + ts.isParenthesizedExpression(initializer) || + ts.isSatisfiesExpression(initializer)) + ) { + initializer = initializer.expression; + } + + if ( + initializer && + (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) + ) { + parameter = initializer.parameters[0]; + } + } + + if (!parameter || !ts.isObjectBindingPattern(parameter.name)) { + return new Map(); + } + + return new Map( + parameter.name.elements.flatMap((element): [string, string][] => { + if (!element.initializer || element.dotDotDotToken) { + return []; + } + + const propertyName = element.propertyName ?? element.name; + + if (!ts.isIdentifier(propertyName) && !ts.isStringLiteral(propertyName)) { + return []; + } + + return [ + [ + propertyName.text, + resolveLiteralDefault(checker, element.initializer) ?? + element.initializer.getText(sourceFile), + ], + ]; + }) + ); +}; + +const getDisplayName = ( + sourceFile: ts.SourceFile, + componentName: string, + declaration: ts.Declaration +) => { + if (ts.isClassDeclaration(declaration)) { + const displayName = declaration.members.find( + (member): member is ts.PropertyDeclaration => + ts.isPropertyDeclaration(member) && + member.modifiers?.some( + (modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword + ) === true && + ts.isIdentifier(member.name) && + member.name.text === 'displayName' + )?.initializer; + + if (displayName && ts.isStringLiteral(displayName)) { + return displayName.text; + } + } + + for (const statement of sourceFile.statements) { + if (!ts.isExpressionStatement(statement)) { + continue; + } + + const expression = statement.expression; + + if ( + ts.isBinaryExpression(expression) && + expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && + ts.isPropertyAccessExpression(expression.left) && + ts.isIdentifier(expression.left.expression) && + expression.left.expression.text === componentName && + expression.left.name.text === 'displayName' && + ts.isStringLiteral(expression.right) + ) { + return expression.right.text; + } + } + + return componentName; +}; + +const extractExtendsAttributes = (description: string) => { + const extendsAttributes: ExtendsAttribute[] = []; + const lines = description.split('\n').filter((line) => { + if (!line.startsWith('@extends ')) { + return true; + } + + const parts = line.slice('@extends '.length).split(' '); + const link = parts.pop(); + + if (link) { + extendsAttributes.push({ + link, + name: parts.join(' '), + }); + } + + return false; + }); + + return { + description: lines.join('\n').trim(), + extendsAttributes, + }; +}; + +export const createComponentParser = (tsconfigPath: string) => { + const config = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + + if (config.error) { + throw new Error( + ts.flattenDiagnosticMessageText(config.error.messageText, '\n') + ); + } + + const parsedConfig = ts.parseJsonConfigFileContent( + config.config, + ts.sys, + path.dirname(tsconfigPath) + ); + const program = ts.createProgram( + parsedConfig.fileNames, + parsedConfig.options + ); + const checker = program.getTypeChecker(); + + return (sourceRootDir: string, page: ComponentPageConfig): ComponentDoc => { + const sourcePath = path.join(sourceRootDir, `${page.source}.tsx`); + const sourceFile = program.getSourceFile(sourcePath); + + if (!sourceFile || !fs.existsSync(sourcePath)) { + return fail( + sourcePath, + 'source file was not found in the TypeScript program' + ); + } + + const componentName = page.component ?? getDefaultExportName(sourceFile); + const component = findComponentDeclaration(sourceFile, componentName); + const propsType = findPropsType( + sourceFile, + page.props ?? DEFAULT_PROPS_TYPE + ); + validatePropsType(sourceFile, propsType); + const defaults = getParameterDefaults( + checker, + sourceFile, + component.declaration + ); + const props = getProps( + checker, + sourceRootDir, + sourceFile, + propsType, + defaults + ); + const componentDocs = extractExtendsAttributes( + readDocComment(sourceFile, component.docNode) + ); + + if (!componentDocs.description) { + return fail( + sourcePath, + `component ${componentName} must have a JSDoc comment` + ); + } + + return { + ...componentDocs, + props, + title: + page.title ?? + getDisplayName(sourceFile, componentName, component.declaration), + }; + }; +}; diff --git a/docs/plugins/component-docs/render.ts b/docs/plugins/component-docs/render.ts new file mode 100644 index 0000000000..0b023f696f --- /dev/null +++ b/docs/plugins/component-docs/render.ts @@ -0,0 +1,191 @@ +import path from 'node:path'; + +import type { ComponentDoc, ComponentProp } from './types.ts'; + +type CustomFields = { + extendedExamples: Record; + knownIssues: Record>; + moreExamples: Record>; + screenshots: Record; + themeColors: Record; +}; + +const renderBadge = (type: string, label: string) => + `${label}`; + +const extractLeadingBadges = (description: string) => { + const badges: string[] = []; + const lines = description.split('\n'); + + while (lines[0]?.startsWith('@')) { + const match = /^@(\S+)\s+(.+)$/.exec(lines[0]); + + const type = match?.[1]; + const label = match?.[2]; + + if (!type || !label || type === 'optional') { + lines.shift(); + continue; + } + + badges.push(renderBadge(type, label)); + lines.shift(); + } + + return { + badges, + description: lines + .join('\n') + .replace(/@optional\b/g, '') + .replace(/@(supported|renamed)\s+([^\n]+)/g, (_, type, label) => + renderBadge(type, label) + ) + .trim(), + }; +}; + +const renderProp = (prop: ComponentProp) => { + if (prop.description.includes('@internal')) { + return ''; + } + + const { badges, description } = extractLeadingBadges(prop.description); + + return ` +### ${prop.name}${prop.required ? ' (required)' : ''}${badges.length ? ` ${badges.join(' ')}` : ''} + + +`; +}; + +const renderLinkList = ( + title: string, + links: Record | undefined +) => { + if (!links) { + return ''; + } + + return ` +## ${title} + +
    + Toggle to grab more examples +
      + ${Object.entries(links) + .map(([label, href]) => `
    • ${label}
    • `) + .join('\n ')} +
    +
    +`; +}; + +const resolveRelativeLinks = ( + content: string, + routePath: string, + routes: ReadonlySet +) => + content.replace( + /\]\((?!https?:\/\/|mailto:|#|\/)([^)\s]+)([^)]*)\)/g, + (_link, href: string, suffix: string) => { + const [pathname, hash = ''] = href.split('#'); + const resolvedPath = path.posix.resolve( + path.posix.dirname(routePath), + pathname + ); + const directoryPage = `${resolvedPath}/${path.posix.basename( + resolvedPath + )}`; + const target = routes.has(resolvedPath) + ? resolvedPath + : routes.has(directoryPage) + ? directoryPage + : undefined; + + if (!target) { + throw new Error( + `Unable to generate docs for ${routePath}: relative link ${href} does not match a component page` + ); + } + + return `](${target}${hash ? `#${hash}` : ''}${suffix})`; + } + ); + +export const renderComponentPage = ( + doc: ComponentDoc, + customFields: CustomFields, + routePath: string, + routes: ReadonlySet +) => { + const summaryMatch = /([\s\S]*?)## Usage/.exec(doc.description); + const summary = summaryMatch?.[1] ?? ''; + const usage = summaryMatch + ? doc.description.slice(summary.length) + : doc.description; + const screenshot = customFields.screenshots[doc.title]; + const extendedExample = customFields.extendedExamples[doc.title]; + const extendedExampleTitle = + extendedExample && typeof extendedExample === 'object' + ? Object.keys(extendedExample)[0] + : undefined; + const themeColors = customFields.themeColors[doc.title]; + + const content = ` +--- +title: ${doc.title} +--- + +import PropTable from '@docs/components/PropTable.tsx'; +import ExtendsLink from '@docs/components/ExtendsLink.tsx'; +import ThemeColorsTable from '@docs/components/ThemeColorsTable.tsx'; +import ScreenshotTabs from '@docs/components/ScreenshotTabs.tsx'; +import ExtendedExample from '@docs/components/ExtendedExample.tsx'; + +${summary} + +${screenshot ? `` : ''} + +${usage} + +${ + extendedExample + ? `${extendedExampleTitle ? `### ${extendedExampleTitle}\n\n` : ''}` + : '' +} + +## Props + +${doc.extendsAttributes + .map( + (attribute) => + `` + ) + .join('\n')} + +${doc.props.map(renderProp).join('\n')} + +${renderLinkList('More Examples', customFields.moreExamples[doc.title])} + +${ + themeColors + ? `## Theme colors + +` + : '' +} + +${renderLinkList('Known Issues', customFields.knownIssues[doc.title])} +` + .slice(1) + .replaceAll('
    ', '
    ') + .split('\n') + .map((line) => line.trimEnd()) + .join('\n'); + + return resolveRelativeLinks(content, routePath, routes); +}; diff --git a/docs/plugins/component-docs/types.ts b/docs/plugins/component-docs/types.ts new file mode 100644 index 0000000000..e48b70e8a6 --- /dev/null +++ b/docs/plugins/component-docs/types.ts @@ -0,0 +1,26 @@ +export type ComponentPageConfig = { + source: string; + component?: string; + props?: string; + title?: string; +}; + +export type ComponentProp = { + defaultValue?: string; + description: string; + name: string; + required: boolean; + type: string; +}; + +export type ExtendsAttribute = { + link: string; + name: string; +}; + +export type ComponentDoc = { + description: string; + extendsAttributes: ExtendsAttribute[]; + props: ComponentProp[]; + title: string; +}; diff --git a/docs/rspress.config.ts b/docs/rspress.config.ts index c7c5195c42..7696c6230e 100644 --- a/docs/rspress.config.ts +++ b/docs/rspress.config.ts @@ -3,6 +3,9 @@ import { defineConfig } from '@rspress/core'; import { createRequire } from 'node:module'; import path from 'node:path'; +import componentDocsConfig from './component-docs.config.ts'; +import { pluginComponentDocs } from './plugins/component-docs/index.ts'; + const REPO_NAME = 'react-native-paper'; const REPO_ORG = 'callstack'; @@ -60,6 +63,13 @@ export default withCallstackPreset( default: '5.x', versions: ['5.x', '6.x'], }, + plugins: [ + pluginComponentDocs({ + ...componentDocsConfig, + tsconfigPath: path.resolve(__dirname, '..', 'tsconfig.source.json'), + version: '6.x', + }), + ], route: { cleanUrls: true, extensions: ['.md', '.mdx'], @@ -72,7 +82,6 @@ export default withCallstackPreset( 'test-results/**', 'tests/**', 'visual/**', - 'component-docs-plugin/**', 'README.md', ], }, diff --git a/docs/scripts/generate-5x-docs.ts b/docs/scripts/generate-5x-docs.ts new file mode 100644 index 0000000000..437522678b --- /dev/null +++ b/docs/scripts/generate-5x-docs.ts @@ -0,0 +1,526 @@ +import * as childProcess from 'node:child_process'; +import * as fs from 'node:fs'; +import { createRequire } from 'node:module'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import * as vm from 'node:vm'; + +type LegacyPages = Record>; + +type PluginOptions = { + docsRootDir: string; + libsRootDir: string; + pages: LegacyPages; +}; + +type PluginFactory = ( + context: unknown, + options: PluginOptions +) => Promise<{ + loadContent: () => Promise; +}>; + +type CustomFields = { + extendedExamples: unknown; + knownIssues: unknown; + moreExamples: unknown; + screenshots: unknown; + themeColors: unknown; +}; + +const docsDir = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..' +); +const rootDir = path.resolve(docsDir, '..'); +const versionDir = path.join(docsDir, '5.x'); +const versionDocsDir = path.join(versionDir, 'docs'); +const componentDocsPath = path.join(versionDir, 'component-docs.json'); + +const NUMERIC_PREFIX = /^\d+-/; + +const isRecord = (value: unknown): value is { [key: string]: unknown } => + typeof value === 'object' && value !== null; + +const isLegacyPages = (value: unknown): value is LegacyPages => + isRecord(value) && + Object.values(value).every( + (page) => + typeof page === 'string' || + (isRecord(page) && + Object.values(page).every((source) => typeof source === 'string')) + ); + +const isPluginOptions = (value: unknown): value is PluginOptions => { + if (!isRecord(value)) { + return false; + } + + return ( + typeof value.docsRootDir === 'string' && + typeof value.libsRootDir === 'string' && + isLegacyPages(value.pages) + ); +}; + +const isPluginFactory = (value: unknown): value is PluginFactory => + typeof value === 'function'; + +const findObjectEnd = (source: string, start: number) => { + let depth = 0; + + for (let index = start; index < source.length; index++) { + const char = source[index]; + + if (char === '{') { + depth++; + } + + if (char === '}') { + depth--; + } + + if (depth === 0) { + return index + 1; + } + } + + return -1; +}; + +const loadLegacyPluginConfig = ( + docusaurusConfigPath: string +): PluginOptions => { + const source = fs.readFileSync(docusaurusConfigPath, 'utf8'); + const pluginPathIndex = source.indexOf("'./component-docs-plugin'"); + const optionsStart = source.indexOf('{', pluginPathIndex); + const optionsEnd = findObjectEnd(source, optionsStart); + + if (pluginPathIndex === -1 || optionsStart === -1 || optionsEnd === -1) { + throw new Error( + `Unable to read component docs plugin options from ${docusaurusConfigPath}` + ); + } + + const optionsSource = source.slice(optionsStart, optionsEnd); + const pluginOptions: unknown = vm.runInNewContext(`(${optionsSource})`, { + __dirname: path.dirname(docusaurusConfigPath), + path, + }); + + if (!isPluginOptions(pluginOptions)) { + throw new Error( + `Unable to read component docs plugin options from ${docusaurusConfigPath}` + ); + } + + return pluginOptions; +}; + +const loadLegacyCustomFields = ( + docusaurusConfigPath: string, + requireFromScript: ReturnType +): CustomFields => { + const source = fs.readFileSync(docusaurusConfigPath, 'utf8'); + const customFieldsIndex = source.indexOf('customFields:'); + const customFieldsStart = source.indexOf('{', customFieldsIndex); + const customFieldsEnd = findObjectEnd(source, customFieldsStart); + + if ( + customFieldsIndex === -1 || + customFieldsStart === -1 || + customFieldsEnd === -1 + ) { + throw new Error( + `Unable to read custom fields from ${docusaurusConfigPath}` + ); + } + + const dataDir = path.join(path.dirname(docusaurusConfigPath), 'src', 'data'); + const extendedExamplesModule: unknown = requireFromScript( + path.join(dataDir, 'extendedExamples.js') + ); + const screenshotsModule: unknown = requireFromScript( + path.join(dataDir, 'screenshots.js') + ); + const themeColorsModule: unknown = requireFromScript( + path.join(dataDir, 'themeColors.js') + ); + + if ( + !isRecord(extendedExamplesModule) || + !isRecord(screenshotsModule) || + !isRecord(themeColorsModule) + ) { + throw new Error(`Unable to read custom field data from ${dataDir}`); + } + + const customFields: unknown = vm.runInNewContext( + `(${source.slice(customFieldsStart, customFieldsEnd)})`, + { + extendedExamples: extendedExamplesModule.extendedExamples, + screenshots: screenshotsModule.screenshots, + themeColors: themeColorsModule.themeColors, + } + ); + + if (!isRecord(customFields)) { + throw new Error( + `Unable to read custom fields from ${docusaurusConfigPath}` + ); + } + + return { + extendedExamples: customFields.extendedExamples, + knownIssues: customFields.knownIssues, + moreExamples: customFields.moreExamples, + screenshots: customFields.screenshots, + themeColors: customFields.themeColors, + }; +}; + +const writeDocusaurusConfigStub = ( + sourceDir: string, + customFields: CustomFields +) => { + fs.writeFileSync( + path.join(sourceDir, 'docs', 'docusaurus.config.js'), + `module.exports = { + baseUrl: '/react-native-paper/', + customFields: ${JSON.stringify(customFields)}, +}; +` + ); +}; + +const normalizeDocs = (value: unknown, sourceDir: string): unknown => { + if (Array.isArray(value)) { + return value.map((item) => normalizeDocs(item, sourceDir)); + } + + if (!isRecord(value)) { + return value; + } + + return Object.fromEntries( + Object.entries(value).map(([key, item]) => { + if (key !== 'dependencies' || !Array.isArray(item)) { + return [key, normalizeDocs(item, sourceDir)]; + } + + return [ + key, + item.map((dependency) => { + if (typeof dependency !== 'string' || !path.isAbsolute(dependency)) { + return dependency; + } + + const realDependency = fs.existsSync(dependency) + ? fs.realpathSync(dependency) + : dependency; + const relativeDependency = path.relative(sourceDir, realDependency); + + if ( + relativeDependency.startsWith('..') || + path.isAbsolute(relativeDependency) + ) { + return dependency; + } + + return relativeDependency.split(path.sep).join('/'); + }), + ]; + }) + ); +}; + +const writeJson = (destination: string, value: unknown) => { + fs.mkdirSync(path.dirname(destination), { recursive: true }); + fs.writeFileSync(destination, `${JSON.stringify(value, null, 2)}\n`); +}; + +const readStringArrayJson = (filePath: string) => { + const value: unknown = JSON.parse(fs.readFileSync(filePath, 'utf8')); + + if ( + !Array.isArray(value) || + !value.every((item) => typeof item === 'string') + ) { + throw new Error(`${filePath} must contain a string array`); + } + + return value; +}; + +const writeVersionNavigation = () => { + const guidesDir = path.join(versionDocsDir, 'guides'); + const guidesMetaPath = path.join(guidesDir, '_meta.json'); + const existingOrder = fs.existsSync(guidesMetaPath) + ? readStringArrayJson(guidesMetaPath) + : []; + const currentEntries = fs + .readdirSync(guidesDir, { withFileTypes: true }) + .filter((entry) => entry.isFile() && /\.(md|mdx)$/.test(entry.name)) + .map((entry) => + entry.name.replace(/\.(md|mdx)$/, '').replace(NUMERIC_PREFIX, '') + ); + const knownEntries = existingOrder.filter((entry) => + currentEntries.includes(entry) + ); + const newEntries = currentEntries.filter( + (entry) => !existingOrder.includes(entry) + ); + + writeJson(path.join(versionDir, '_nav.json'), [ + { + text: 'Guides', + link: '/docs/guides/getting-started', + activeMatch: '^/docs/guides/', + }, + { + text: 'Components', + link: '/docs/components/ActivityIndicator', + activeMatch: '^/docs/components/', + }, + { + text: 'Showcase', + link: '/docs/showcase', + activeMatch: '^/docs/showcase$', + }, + ]); + writeJson(path.join(versionDocsDir, '_meta.json'), [ + { + type: 'dir-section-header', + name: 'guides', + label: 'Guides', + }, + { + type: 'dir-section-header', + name: 'components', + label: 'Components', + }, + { + type: 'file', + name: 'showcase', + label: 'Showcase', + }, + ]); + writeJson(guidesMetaPath, [...knownEntries, ...newEntries]); +}; + +const writeComponentNavigation = (destination: string, pages: LegacyPages) => { + writeJson( + path.join(destination, '_meta.json'), + Object.entries(pages).map(([name, page]) => + typeof page === 'string' + ? name + : { + type: 'dir', + name, + label: name, + collapsible: true, + collapsed: false, + } + ) + ); + + for (const [name, page] of Object.entries(pages)) { + if (typeof page !== 'string') { + writeJson(path.join(destination, name, '_meta.json'), Object.keys(page)); + } + } +}; + +const resolveRelativeLinks = ( + content: string, + relativePath: string, + routes: ReadonlySet +) => { + const routePath = `/docs/components/${relativePath + .replace(/\.mdx$/, '') + .split(path.sep) + .join('/')}`; + + return content.replace( + /\]\((?!https?:\/\/|mailto:|#|\/)([^)\s]+)([^)]*)\)/g, + (_link, href: string, suffix: string) => { + const [pathname, hash = ''] = href.split('#'); + const componentRelativePath = pathname.replace(/^(?:\.\.?\/)+/, ''); + const resolvedPaths = [ + path.posix.resolve(path.posix.dirname(routePath), pathname), + path.posix.resolve('/docs/components', componentRelativePath), + ]; + const target = resolvedPaths + .flatMap((resolvedPath) => [ + resolvedPath, + `${resolvedPath}/${path.posix.basename(resolvedPath)}`, + ]) + .find((resolvedPath) => routes.has(resolvedPath)); + + if (!target) { + throw new Error( + `Unable to generate docs for ${routePath}: relative link ${href} does not match a component page` + ); + } + + return `](${target}${hash ? `#${hash}` : ''}${suffix})`; + } + ); +}; + +const writeComponentPages = ( + source: string, + destination: string, + pages: LegacyPages +) => { + fs.rmSync(destination, { recursive: true, force: true }); + const routes = new Set( + Object.entries(pages).flatMap(([name, page]) => + typeof page === 'string' + ? [`/docs/components/${name}`] + : Object.keys(page).map( + (pageName) => `/docs/components/${name}/${pageName}` + ) + ) + ); + + for (const entry of fs.readdirSync(source, { + recursive: true, + withFileTypes: true, + })) { + if (!entry.isFile() || path.extname(entry.name) !== '.mdx') { + continue; + } + + const sourcePath = path.join(entry.parentPath, entry.name); + const relativePath = path.relative(source, sourcePath); + const destinationPath = path.join(destination, relativePath); + const componentDocsImport = path + .relative(path.dirname(destinationPath), componentDocsPath) + .split(path.sep) + .join('/'); + const content = resolveRelativeLinks( + fs + .readFileSync(sourcePath, 'utf8') + .replaceAll('@site/src/components/', '@docs/components/') + .replace( + "import PropTable from '@docs/components/PropTable.tsx';", + `import PropTable from '@docs/components/PropTable.tsx';\nimport componentDocs5x from '${componentDocsImport}';` + ) + .replaceAll( + ' { + const [branchName] = process.argv.slice(2); + + if (!branchName) { + console.error('Usage: node docs/scripts/generate-5x-docs.ts '); + process.exitCode = 1; + return; + } + + const requireFromScript = createRequire(import.meta.url); + const tempDir = fs.mkdtempSync( + path.join(os.tmpdir(), 'react-native-paper-docs-') + ); + + try { + const archivePath = path.join(tempDir, 'source.tar'); + const sourceDir = path.join(tempDir, 'source'); + + childProcess.execFileSync( + 'git', + ['rev-parse', '--verify', `${branchName}^{commit}`], + { + cwd: rootDir, + stdio: 'ignore', + } + ); + childProcess.execFileSync( + 'git', + ['archive', '--format=tar', '--output', archivePath, branchName], + { + cwd: rootDir, + stdio: 'inherit', + } + ); + + fs.mkdirSync(sourceDir); + childProcess.execFileSync('tar', ['-xf', archivePath, '-C', sourceDir], { + cwd: rootDir, + stdio: 'inherit', + }); + + const nodeModulesPath = path.join(rootDir, 'docs', 'node_modules'); + const archivedNodeModulesPath = path.join( + sourceDir, + 'docs', + 'node_modules' + ); + + if (fs.existsSync(nodeModulesPath)) { + fs.symlinkSync(nodeModulesPath, archivedNodeModulesPath, 'dir'); + } + + const docusaurusConfigPath = path.join( + sourceDir, + 'docs', + 'docusaurus.config.js' + ); + const customFields = loadLegacyCustomFields( + docusaurusConfigPath, + requireFromScript + ); + const pluginOptions = loadLegacyPluginConfig(docusaurusConfigPath); + + writeDocusaurusConfigStub(sourceDir, customFields); + + const pluginFactoryPath = path.join( + sourceDir, + 'docs', + 'component-docs-plugin' + ); + const pluginFactory: unknown = requireFromScript(pluginFactoryPath); + + if (!isPluginFactory(pluginFactory)) { + throw new Error( + `Unable to read component docs plugin from ${pluginFactoryPath}` + ); + } + + const plugin = await pluginFactory({}, pluginOptions); + const docs = await plugin.loadContent(); + writeJson(componentDocsPath, { + docs: normalizeDocs(docs, fs.realpathSync(sourceDir)), + }); + + writeComponentPages( + pluginOptions.docsRootDir, + path.join(versionDocsDir, 'components'), + pluginOptions.pages + ); + writeVersionNavigation(); + } catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; + } finally { + fs.rmSync(tempDir, { recursive: true, force: true }); + } +}; + +void main(); diff --git a/docs/scripts/generate-current-component-docs.ts b/docs/scripts/generate-current-component-docs.ts deleted file mode 100644 index c36d2e4ed1..0000000000 --- a/docs/scripts/generate-current-component-docs.ts +++ /dev/null @@ -1,173 +0,0 @@ -import componentDocsParser from 'component-docs/dist/parsers/component.js'; -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import type { ComponentDoc } from '../component-docs-plugin/generatePageMDX.ts'; -import generatePageMDX from '../component-docs-plugin/generatePageMDX.ts'; -import componentDocsConfig from '../component-docs.config.ts'; - -const parseComponentDocs = componentDocsParser.default; - -const rootDir = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - '..' -); -const repoRootDir = path.resolve(rootDir, '..'); -const outputDir = componentDocsConfig.docsRootDir; -const metadataPath = path.join(rootDir, 'src', 'data', 'componentDocs6x.json'); - -const isRecord = (value: unknown): value is Record => - typeof value === 'object' && value !== null; - -const normalizeDocs = (value: unknown, sourceDir: string): unknown => { - if (Array.isArray(value)) { - return value.map((item) => normalizeDocs(item, sourceDir)); - } - - if (!isRecord(value)) { - return value; - } - - return Object.fromEntries( - Object.entries(value).map(([key, item]) => { - if (key !== 'dependencies' || !Array.isArray(item)) { - return [key, normalizeDocs(item, sourceDir)]; - } - - return [ - key, - item.map((dependency) => { - if (typeof dependency !== 'string' || !path.isAbsolute(dependency)) { - return dependency; - } - - const realDependency = fs.existsSync(dependency) - ? fs.realpathSync(dependency) - : dependency; - const relativeDependency = path.relative(sourceDir, realDependency); - - if ( - relativeDependency.startsWith('..') || - path.isAbsolute(relativeDependency) - ) { - return dependency; - } - - return relativeDependency.split(path.sep).join('/'); - }), - ]; - }) - ); -}; - -const ensureDir = (dirPath: string) => { - fs.mkdirSync(dirPath, { recursive: true }); -}; - -const writeJson = (filePath: string, value: unknown) => { - ensureDir(path.dirname(filePath)); - fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`); -}; - -const writeDocPage = ( - targetPath: string, - sourcePath: string, - docs: Record -) => { - const doc = parseComponentDocs(sourcePath, { - root: componentDocsConfig.libsRootDir, - }); - - ensureDir(path.dirname(targetPath)); - fs.writeFileSync( - targetPath, - generatePageMDX( - doc, - path - .relative(componentDocsConfig.libsRootDir, sourcePath) - .replace(/\.tsx$/, '') - .split(path.sep) - .join('/') - ) - ); - - docs[ - path - .relative(componentDocsConfig.libsRootDir, sourcePath) - .replace(/\.tsx$/, '') - .split(path.sep) - .join('/') - ] = doc; -}; - -const writeMetaFiles = () => { - writeJson( - path.join(outputDir, '_meta.json'), - Object.entries(componentDocsConfig.pages).map(([entryName, pageValue]) => { - if (typeof pageValue === 'string') { - return entryName; - } - - return { - type: 'dir', - name: entryName, - label: entryName, - collapsible: true, - collapsed: false, - }; - }) - ); - - for (const [entryName, pageValue] of Object.entries( - componentDocsConfig.pages - )) { - if (typeof pageValue === 'string') { - continue; - } - - writeJson( - path.join(outputDir, entryName, '_meta.json'), - Object.keys(pageValue) - ); - } -}; - -const main = () => { - fs.rmSync(outputDir, { recursive: true, force: true }); - ensureDir(outputDir); - - const docs: Record = {}; - - for (const [entryName, pageValue] of Object.entries( - componentDocsConfig.pages - )) { - if (typeof pageValue === 'string') { - writeDocPage( - path.join(outputDir, `${entryName}.mdx`), - path.join(componentDocsConfig.libsRootDir, `${pageValue}.tsx`), - docs - ); - continue; - } - - const groupDir = path.join(outputDir, entryName); - - for (const [subitem, source] of Object.entries(pageValue)) { - writeDocPage( - path.join(groupDir, `${subitem}.mdx`), - path.join(componentDocsConfig.libsRootDir, `${source}.tsx`), - docs - ); - } - } - - ensureDir(path.dirname(metadataPath)); - writeJson(metadataPath, { - docs: normalizeDocs(docs, fs.realpathSync(repoRootDir)), - }); - - writeMetaFiles(); -}; - -main(); diff --git a/docs/scripts/generate-rspress-content.ts b/docs/scripts/generate-rspress-content.ts deleted file mode 100644 index 1c9accd93a..0000000000 --- a/docs/scripts/generate-rspress-content.ts +++ /dev/null @@ -1,274 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import componentDocsConfig from '../component-docs.config.ts'; - -type MetaEntry = - | string - | { - type: string; - name: string; - label: string; - collapsible?: boolean; - collapsed?: boolean; - }; - -const rootDir = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - '..' -); -const sourcePagesDir = path.join(rootDir, 'src', 'pages'); -const publicDir = path.join(rootDir, 'public'); -const basePublicDir = path.join(publicDir, 'react-native-paper'); -const docsOrder = ['5.x', '6.x']; - -const NUMERIC_PREFIX = /^\d+-/; - -const ensureDir = (dirPath: string) => { - fs.mkdirSync(dirPath, { recursive: true }); -}; - -const readFile = (filePath: string) => fs.readFileSync(filePath, 'utf8'); - -const writeJson = (filePath: string, value: unknown) => { - ensureDir(path.dirname(filePath)); - fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`); -}; - -const readStringArrayJson = (filePath: string) => { - const value: unknown = JSON.parse(readFile(filePath)); - - if ( - !Array.isArray(value) || - !value.every((item) => typeof item === 'string') - ) { - throw new Error(`${filePath} must contain a string array`); - } - - return value; -}; - -const stripExtension = (fileName: string) => - fileName.replace(/\.(md|mdx)$/, ''); - -const stripNumericPrefix = (fileName: string) => - stripExtension(fileName).replace(NUMERIC_PREFIX, ''); - -const transformIndexContent = (content: string) => - content - .replace( - '![paperLogo](../../static/images/sidebar-logo.svg)', - 'React Native Paper' - ) - .replace( - "import GetStartedButtons from '../components/GetStartedButtons';", - "import GetStartedButtons from '@docs/components/GetStartedButtons';" - ) - .replace("import {Button} from 'react-native-paper';\n", '') - .replace( - "import BannerExample from '../components/BannerExample';", - "import BannerExample from '@docs/components/BannerExample';" - ) - .replace(/src="\.\/*gallery\//g, 'src="/react-native-paper/gallery/'); - -const copyFile = ( - sourcePath: string, - targetPath: string, - transform: (content: string) => string -) => { - ensureDir(path.dirname(targetPath)); - const source = readFile(sourcePath); - fs.writeFileSync(targetPath, transform(source)); -}; - -const getVersionDocsDir = (version: string) => - path.join(rootDir, version, 'docs'); - -const createNav = (version: string) => { - const activePrefix = version === '6.x' ? '^/(?:6\\.x/)?docs' : '^/docs'; - - return [ - { - text: 'Guides', - link: '/docs/guides/getting-started', - activeMatch: `${activePrefix}/guides/`, - }, - { - text: 'Components', - link: '/docs/components/ActivityIndicator', - activeMatch: `${activePrefix}/components/`, - }, - { - text: 'Showcase', - link: '/docs/showcase', - activeMatch: `${activePrefix}/showcase$`, - }, - ]; -}; - -const getVersionComponentOrder = (version: string): MetaEntry[] => { - const fromConfig = Object.entries(componentDocsConfig.pages).map( - ([entryName, value]) => { - if (typeof value === 'string') { - return entryName; - } - - return { - type: 'dir', - name: entryName, - label: entryName, - collapsible: true, - collapsed: false, - }; - } - ); - - if (version !== '5.x') { - return fromConfig; - } - - return [ - ...fromConfig, - { - type: 'dir', - name: 'HelperText', - label: 'HelperText', - collapsible: true, - collapsed: false, - }, - ]; -}; - -const getComponentChildOrder = (version: string, dirName: string) => { - const configEntry = componentDocsConfig.pages[dirName]; - - if (version !== '5.x' && configEntry && typeof configEntry !== 'string') { - return Object.keys(configEntry); - } - - const dirPath = path.join(getVersionDocsDir(version), 'components', dirName); - - return fs - .readdirSync(dirPath, { withFileTypes: true }) - .filter((entry) => entry.isFile() && /\.(md|mdx)$/.test(entry.name)) - .map((entry) => stripExtension(entry.name)); -}; - -const createGuidesMeta = (version: string) => { - const guidesDir = path.join(getVersionDocsDir(version), 'guides'); - const metaPath = path.join(guidesDir, '_meta.json'); - const existingOrder = fs.existsSync(metaPath) - ? readStringArrayJson(metaPath) - : []; - - const currentEntries = fs - .readdirSync(guidesDir, { withFileTypes: true }) - .filter((entry) => entry.isFile() && /\.(md|mdx)$/.test(entry.name)) - .map((entry) => stripNumericPrefix(entry.name)); - - const knownEntries = existingOrder.filter((entry) => - currentEntries.includes(entry) - ); - const newEntries = currentEntries.filter( - (entry) => !existingOrder.includes(entry) - ); - - return [...knownEntries, ...newEntries]; -}; - -const createComponentMeta = (version: string) => { - return getVersionComponentOrder(version); -}; - -const writeMetaFiles = (version: string, targetVersionDir: string) => { - const docsDir = path.join(targetVersionDir, 'docs'); - const componentsDir = path.join(docsDir, 'components'); - - writeJson(path.join(docsDir, '_meta.json'), [ - { - type: 'dir-section-header', - name: 'guides', - label: 'Guides', - }, - { - type: 'dir-section-header', - name: 'components', - label: 'Components', - }, - { - type: 'file', - name: 'showcase', - label: 'Showcase', - }, - ]); - - writeJson( - path.join(docsDir, 'guides', '_meta.json'), - createGuidesMeta(version) - ); - writeJson( - path.join(componentsDir, '_meta.json'), - createComponentMeta(version) - ); - - for (const entry of fs.readdirSync(componentsDir, { withFileTypes: true })) { - if (!entry.isDirectory()) { - continue; - } - - writeJson( - path.join(componentsDir, entry.name, '_meta.json'), - getComponentChildOrder(version, entry.name) - ); - } -}; - -const writeIndexPage = (targetVersionDir: string) => { - copyFile( - path.join(sourcePagesDir, 'index.mdx'), - path.join(targetVersionDir, 'index.mdx'), - transformIndexContent - ); -}; - -const copyBasePublicAssets = () => { - fs.rmSync(basePublicDir, { recursive: true, force: true }); - ensureDir(basePublicDir); - - for (const entry of fs.readdirSync(publicDir, { withFileTypes: true })) { - if (entry.name === 'react-native-paper') { - continue; - } - - fs.cpSync( - path.join(publicDir, entry.name), - path.join(basePublicDir, entry.name), - { - recursive: true, - force: true, - } - ); - } -}; - -const prepareVersionDir = (version: string) => { - const targetVersionDir = path.join(rootDir, version); - ensureDir(targetVersionDir); - - writeJson(path.join(targetVersionDir, '_nav.json'), createNav(version)); - writeIndexPage(targetVersionDir); - writeMetaFiles(version, targetVersionDir); -}; - -const main = () => { - copyBasePublicAssets(); - fs.rmSync(path.join(rootDir, 'docs'), { recursive: true, force: true }); - fs.rmSync(path.join(rootDir, 'index.mdx'), { force: true }); - - for (const version of docsOrder) { - prepareVersionDir(version); - } -}; - -main(); diff --git a/docs/src/components/PropTable.tsx b/docs/src/components/PropTable.tsx index 54e52d8f3b..2437f58bce 100644 --- a/docs/src/components/PropTable.tsx +++ b/docs/src/components/PropTable.tsx @@ -1,9 +1,6 @@ -import { useVersion } from '@rspress/core/dist/runtime/index.js'; - import Markdown from './Markdown'; -import useDoc from '../../component-docs-plugin/useDocs'; -const typeDefinitions = { +const typeDefinitions: Record = { IconSource: 'https://github.com/callstack/react-native-paper/blob/main/components/Icon.tsx#L16', ThemeProp: @@ -32,83 +29,122 @@ const renderBadge = (annotation: string) => { )} ">${annotLabel.join(' ')}
    `; }; -export default function PropTable({ - componentLink, - prop, -}: { +type LegacyProp = { + defaultValue?: { + value: string; + } | null; + description: string; + required: boolean; + tsType?: { + name: string; + raw?: string; + } | null; +}; + +type LegacyComponentDocs = Record< + string, + { + data: { + props: Record; + }; + } +>; + +type LegacyPropTableProps = { + componentDocs: LegacyComponentDocs; componentLink: string; prop: string; -}) { - const version = useVersion() || '5.x'; - const doc = useDoc(componentLink, version); +}; - if (!doc?.data?.props) { - return null; - } +type GeneratedPropTableProps = { + defaultValue?: string; + description: string; + type: string; +}; - const props = doc.data.props; +type PropTableProps = LegacyPropTableProps | GeneratedPropTableProps; + +const renderPropDetails = ({ + defaultValue, + description, + type, +}: GeneratedPropTableProps) => { + const tsTypeLink = typeDefinitions[type]; return (
    - {Object.keys(props).map((key) => { - if (key !== prop) { - return null; - } - let descriptionByLines = props[key].description.split('\n'); - - // Slice leading badge - it's handled in `generatePageMDX` - if (descriptionByLines[0].includes('@')) { - descriptionByLines = descriptionByLines.slice(1); - } - descriptionByLines = descriptionByLines.map((line: string) => { - // Replace annotations with styled badges. - if (line.includes('@')) { - const annotIndex = line.indexOf('@'); - - return `${line.substr(0, annotIndex)} ${renderBadge( - line.substr(annotIndex) - )}`; - } else { - return line; - } - }); - - const description = descriptionByLines.join('\n'); - const tsType = props[key].tsType?.raw ?? props[key].tsType?.name; - // @ts-ignore - const tsTypeLink = typeDefinitions[tsType]; - - return ( -
    -

    - Type:{' '} - {tsTypeLink ? ( - - {tsType} - - ) : ( - {tsType} - )} -

    - {props[key].defaultValue && ( -

    - Default value: {props[key].defaultValue.value} -

    - )} - -
    - ); - })} +

    + Type:{' '} + {tsTypeLink ? ( + + {type} + + ) : ( + {type} + )} +

    + {defaultValue && ( +

    + Default value: {defaultValue} +

    + )} +
    ); +}; + +export default function PropTable(tableProps: PropTableProps) { + if ('type' in tableProps) { + return renderPropDetails(tableProps); + } + + const doc = tableProps.componentDocs[tableProps.componentLink]; + + if (!doc?.data?.props) { + return null; + } + + const prop = doc.data.props[tableProps.prop]; + + if (!prop) { + return null; + } + + let descriptionByLines = prop.description.split('\n'); + + // Slice leading badge - it's handled in `generatePageMDX` + if (descriptionByLines[0].includes('@')) { + descriptionByLines = descriptionByLines.slice(1); + } + + const description = descriptionByLines + .map((line) => { + // Replace annotations with styled badges. + if (!line.includes('@')) { + return line; + } + + const annotIndex = line.indexOf('@'); + + return `${line.substr(0, annotIndex)} ${renderBadge( + line.substr(annotIndex) + )}`; + }) + .join('\n'); + + return renderPropDetails({ + defaultValue: prop.defaultValue?.value, + description, + type: prop.tsType?.raw ?? prop.tsType?.name ?? '', + }); } diff --git a/docs/src/data/componentDocs5x.json b/docs/src/data/componentDocs5x.json deleted file mode 100644 index 5aa299568c..0000000000 --- a/docs/src/data/componentDocs5x.json +++ /dev/null @@ -1,12185 +0,0 @@ -{ - "docs": { - "ActivityIndicator": { - "filepath": "ActivityIndicator.tsx", - "title": "ActivityIndicator", - "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, MD2Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "activity-indicator", - "data": { - "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, MD2Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "ActivityIndicator", - "methods": [], - "statics": [], - "props": { - "animating": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show the indicator or hide it.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "The color of the spinner." - }, - "size": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'large' | number", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "number" - } - ] - }, - "description": "Size of the indicator.", - "defaultValue": { - "value": "'small'", - "computed": false - } - }, - "hidesWhenStopped": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the indicator should hide when not animating.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ActivityIndicator.tsx" - ] - }, - "Appbar/Appbar": { - "filepath": "Appbar/Appbar.tsx", - "title": "Appbar", - "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", - "link": "appbar", - "data": { - "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "Appbar", - "methods": [], - "statics": [], - "props": { - "dark": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the background color is a dark color. A dark appbar will render light text and vice-versa." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Appbar`." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'medium' | 'large' | 'center-aligned'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "literal", - "value": "'center-aligned'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", - "defaultValue": { - "value": "'small'", - "computed": false - } - }, - "elevated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment." - }, - "safeAreaInsets": { - "required": false, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n bottom?: number;\n top?: number;\n left?: number;\n right?: number;\n}", - "signature": { - "properties": [ - { - "key": "bottom", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "top", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "left", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "right", - "value": { - "name": "number", - "required": false - } - } - ] - } - }, - "description": "Safe area insets for the Appbar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/Appbar.tsx" - ] - }, - "Appbar/AppbarAction": { - "filepath": "Appbar/AppbarAction.tsx", - "title": "Appbar.Action", - "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", - "link": "appbar-action", - "data": { - "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Appbar.Action", - "methods": [], - "statics": [], - "props": { - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for action icon." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Name of the icon to show." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Optional icon size.", - "defaultValue": { - "value": "24", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "isLeading": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n\nWhether it's the leading button. Note: If `Appbar.BackAction` is present, it will be rendered before any `isLeading` icons." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarAction.tsx" - ], - "group": "Appbar" - }, - "Appbar/AppbarBackAction": { - "filepath": "Appbar/AppbarBackAction.tsx", - "title": "Appbar.BackAction", - "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "link": "appbar-back-action", - "data": { - "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Appbar.BackAction", - "methods": [], - "statics": [], - "props": { - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for back icon." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Optional icon size." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'Back'", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarBackAction.tsx" - ], - "group": "Appbar" - }, - "Appbar/AppbarContent": { - "filepath": "Appbar/AppbarContent.tsx", - "title": "Appbar.Content", - "description": "A component used to display a title and optional subtitle in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "appbar-content", - "data": { - "description": "A component used to display a title and optional subtitle in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Appbar.Content", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text or component for the title." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the title, if `title` is a string." - }, - "titleRef": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "TextRef" - } - ] - }, - "description": "Reference for the title." - }, - "subtitle": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "@deprecated Deprecated in v5.x\nText for the subtitle." - }, - "subtitleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "@deprecated Deprecated in v5.x\nStyle for the subtitle." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "If true, disable all interactions for this component." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the text." - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'medium' | 'large' | 'center-aligned'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "literal", - "value": "'center-aligned'" - } - ] - }, - "description": "@internal", - "defaultValue": { - "value": "'small'", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'appbar-content'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarContent.tsx" - ], - "group": "Appbar" - }, - "Appbar/AppbarHeader": { - "filepath": "Appbar/AppbarHeader.tsx", - "title": "Appbar.Header", - "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "appbar-header", - "data": { - "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Appbar.Header", - "methods": [], - "statics": [], - "props": { - "dark": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the background color is a dark color. A dark header will render light text and vice-versa." - }, - "statusBarHeight": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Extra padding to add at the top of header to account for translucent status bar.\nThis is automatically handled on iOS >= 11 including iPhone X using `SafeAreaView`.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to disable the default behaviour, and customize the height." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the header." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'medium' | 'large' | 'center-aligned'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "literal", - "value": "'center-aligned'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", - "defaultValue": { - "value": "Platform.OS === 'ios' ? 'center-aligned' : 'small'", - "computed": false - } - }, - "elevated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "testID": { - "defaultValue": { - "value": "'appbar-header'", - "computed": false - }, - "required": false, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarHeader.tsx" - ], - "group": "Appbar" - }, - "Avatar/AvatarIcon": { - "filepath": "Avatar/AvatarIcon.tsx", - "title": "Avatar.Icon", - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "link": "avatar-icon", - "data": { - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "displayName": "Avatar.Icon", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Avatar`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the avatar.", - "defaultValue": { - "value": "64", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the icon." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarIcon.tsx" - ], - "group": "Avatar" - }, - "Avatar/AvatarImage": { - "filepath": "Avatar/AvatarImage.tsx", - "title": "Avatar.Image", - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", - "link": "avatar-image", - "data": { - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", - "displayName": "Avatar.Image", - "methods": [], - "statics": [], - "props": { - "source": { - "required": true, - "tsType": { - "name": "union", - "raw": "| ImageSourcePropType\n| ((props: { size: number }) => React.ReactNode)", - "elements": [ - { - "name": "ImageSourcePropType" - }, - { - "name": "unknown" - } - ] - }, - "description": "Image to display for the `Avatar`.\nIt accepts a standard React Native Image `source` prop\nOr a function that returns an `Image`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the avatar.", - "defaultValue": { - "value": "64", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "onError": { - "required": false, - "tsType": { - "name": "ImageProps['onError']", - "raw": "ImageProps['onError']" - }, - "description": "Invoked on load error." - }, - "onLayout": { - "required": false, - "tsType": { - "name": "ImageProps['onLayout']", - "raw": "ImageProps['onLayout']" - }, - "description": "Invoked on mount and on layout changes." - }, - "onLoad": { - "required": false, - "tsType": { - "name": "ImageProps['onLoad']", - "raw": "ImageProps['onLoad']" - }, - "description": "Invoked when load completes successfully." - }, - "onLoadEnd": { - "required": false, - "tsType": { - "name": "ImageProps['onLoadEnd']", - "raw": "ImageProps['onLoadEnd']" - }, - "description": "Invoked when load either succeeds or fails." - }, - "onLoadStart": { - "required": false, - "tsType": { - "name": "ImageProps['onLoadStart']", - "raw": "ImageProps['onLoadStart']" - }, - "description": "Invoked on load start." - }, - "onProgress": { - "required": false, - "tsType": { - "name": "ImageProps['onProgress']", - "raw": "ImageProps['onProgress']" - }, - "description": "Invoked on download progress." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarImage.tsx" - ], - "group": "Avatar" - }, - "Avatar/AvatarText": { - "filepath": "Avatar/AvatarText.tsx", - "title": "Avatar.Text", - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "link": "avatar-text", - "data": { - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "displayName": "Avatar.Text", - "methods": [], - "statics": [], - "props": { - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Initials to show as the text in the `Avatar`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the avatar.", - "defaultValue": { - "value": "64", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the text." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for text container" - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the title." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarText.tsx" - ], - "group": "Avatar" - }, - "Badge": { - "filepath": "Badge.tsx", - "title": "Badge", - "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", - "link": "badge", - "data": { - "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", - "displayName": "Badge", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the badge is visible", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "children": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - }, - "description": "Content of the `Badge`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the `Badge`.", - "defaultValue": { - "value": "20", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - {} - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Badge.tsx" - ] - }, - "Banner": { - "filepath": "Banner.tsx", - "title": "Banner", - "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "banner", - "data": { - "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Banner", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether banner is currently visible." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content that will be displayed inside banner." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Banner`. Can be an image." - }, - "actions": { - "required": false, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "intersection", - "raw": "{\n label: string;\n} & $RemoveChildren", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n label: string;\n}", - "signature": { - "properties": [ - { - "key": "label", - "value": { - "name": "string", - "required": true - } - } - ] - } - }, - { - "name": "$RemoveChildren", - "elements": [ - { - "name": "Button" - } - ], - "raw": "$RemoveChildren" - } - ] - } - ], - "raw": "Array<\n {\n label: string;\n } & $RemoveChildren\n>" - }, - "description": "Action items to shown in the banner.\nAn action item should contain the following properties:\n\n- `label`: label of the action button (required)\n- `onPress`: callback that is called when button is pressed (required)\n\nTo customize button you can pass other props that button component takes.", - "defaultValue": { - "value": "[]", - "computed": false - } - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of banner's inner content.\nUse this prop to apply custom width for wide layouts." - }, - "elevation": { - "required": false, - "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nChanges Banner shadow and background on iOS and Android.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "onShowAnimationFinished": { - "required": false, - "tsType": { - "name": "Animated.EndCallback" - }, - "description": "\nOptional callback that will be called after the opening animation finished running normally", - "defaultValue": { - "value": "() => {}", - "computed": false - } - }, - "onHideAnimationFinished": { - "required": false, - "tsType": { - "name": "Animated.EndCallback" - }, - "description": "\nOptional callback that will be called after the closing animation finished running normally", - "defaultValue": { - "value": "() => {}", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Banner.tsx" - ] - }, - "BottomNavigation/BottomNavigation": { - "filepath": "BottomNavigation/BottomNavigation.tsx", - "title": "BottomNavigation", - "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "bottom-navigation", - "data": { - "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "BottomNavigation", - "methods": [ - { - "name": "SceneMap", - "docblock": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance.", - "modifiers": [ - "static" - ], - "params": [ - { - "name": "scenes", - "optional": false, - "type": { - "name": "signature", - "type": "object", - "raw": "{\n [key: string]: React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n }>;\n}", - "signature": { - "properties": [ - { - "key": { - "name": "string" - }, - "value": { - "name": "ReactComponentType", - "raw": "React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n}>", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "jumpTo", - "value": { - "name": "signature", - "type": "function", - "raw": "(key: string) => void", - "signature": { - "arguments": [ - { - "name": "key", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - } - ] - } - } - ], - "required": true - } - } - ] - } - } - } - ], - "returns": null, - "description": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance." - } - ], - "statics": [], - "props": { - "shifting": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." - }, - "labeled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether tabs should be spread across the entire width." - }, - "navigationState": { - "required": true, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n index: number;\n routes: Route[];\n}", - "signature": { - "properties": [ - { - "key": "index", - "value": { - "name": "number", - "required": true - } - }, - { - "key": "routes", - "value": { - "name": "Array", - "elements": [ - { - "name": "Route" - } - ], - "raw": "Route[]", - "required": true - } - } - ] - } - }, - "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `color`: color to use as background color for shifting bottom navigation @deprecatedProperty In v5.x works only with theme version 2.\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation` is a controlled component, which means the `index` needs to be updated via the `onIndexChange` callback." - }, - "onIndexChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(index: number) => void", - "signature": { - "arguments": [ - { - "name": "index", - "type": { - "name": "number" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback which is called on tab change, receives the index of the new tab as argument.\nThe navigation state needs to be updated when it's called, otherwise the change is dropped." - }, - "renderScene": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n jumpTo: (key: string) => void;\n}) => React.ReactNode | null", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "jumpTo", - "value": { - "name": "signature", - "type": "function", - "raw": "(key: string) => void", - "signature": { - "arguments": [ - { - "name": "key", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "React.ReactNode | null", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "null" - } - ] - } - } - }, - "description": "Callback which returns a react element to render as the page for the tab. Receives an object containing the route as the argument:\n\n```js\nrenderScene = ({ route, jumpTo }) => {\n switch (route.key) {\n case 'music':\n return ;\n case 'albums':\n return ;\n }\n}\n```\n\nPages are lazily rendered, which means that a page will be rendered the first time you navigate to it.\nAfter initial render, all the pages stay rendered to preserve their state.\n\nYou need to make sure that your individual routes implement a `shouldComponentUpdate` to improve the performance.\nTo make it easier to specify the components, you can use the `SceneMap` helper:\n\n```js\nrenderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n});\n```\n\nSpecifying the components this way is easier and takes care of implementing a `shouldComponentUpdate` method.\nEach component will receive the current route and a `jumpTo` method as it's props.\nThe `jumpTo` method can be used to navigate to other tabs programmatically:\n\n```js\nthis.props.jumpTo('albums')\n```" - }, - "renderIcon": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React Element to be used as tab icon." - }, - "renderLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which React Element to be used as tab label." - }, - "renderTouchable": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: TouchableProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "elements": [ - { - "name": "TouchableRippleProps" - }, - { - "name": "signature", - "type": "object", - "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "key", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "children", - "value": { - "name": "ReactReactNode", - "raw": "React.ReactNode", - "required": true - } - }, - { - "key": "borderless", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "centered", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "rippleColor", - "value": { - "name": "ColorValue", - "required": false - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS." - }, - "getAccessibilityLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default." - }, - "getBadge": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => boolean | number | string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "boolean | number | string | undefined", - "elements": [ - { - "name": "boolean" - }, - { - "name": "number" - }, - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get badge for the tab, uses `route.badge` by default." - }, - "getColor": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get color for the tab, uses `route.color` by default." - }, - "getLabelText": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component." - }, - "getLazy": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => boolean | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "boolean | undefined", - "elements": [ - { - "name": "boolean" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get lazy for the current screen. Uses true by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.lazy", - "computed": false - } - }, - "getTestID": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get the id to locate this tab button in tests, uses `route.testID` by default." - }, - "onTabPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab press. It receives the route for the pressed tab, useful for things like scroll to top." - }, - "onTabLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab long press. It receives the route for the pressed tab, useful for things like custom action when longed pressed." - }, - "activeColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the active tab." - }, - "inactiveColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the inactive tab." - }, - "sceneAnimationEnabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether animation is enabled for scenes transitions in `shifting` mode.\nBy default, the scenes cross-fade during tab change when `shifting` is enabled.\nSpecify `sceneAnimationEnabled` as `false` to disable the animation.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "sceneAnimationType": { - "required": false, - "tsType": { - "name": "union", - "raw": "'opacity' | 'shifting'", - "elements": [ - { - "name": "literal", - "value": "'opacity'" - }, - { - "name": "literal", - "value": "'shifting'" - } - ] - }, - "description": "The scene animation effect. Specify `'shifting'` for a different effect.\nBy default, 'opacity' will be used.", - "defaultValue": { - "value": "'opacity'", - "computed": false - } - }, - "sceneAnimationEasing": { - "required": false, - "tsType": { - "name": "union", - "raw": "EasingFunction | undefined", - "elements": [ - { - "name": "EasingFunction" - }, - { - "name": "undefined" - } - ] - }, - "description": "The scene animation Easing." - }, - "keyboardHidesNavigationBar": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", - "defaultValue": { - "value": "Platform.OS === 'android'", - "computed": false - } - }, - "safeAreaInsets": { - "required": false, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", - "signature": { - "properties": [ - { - "key": "top", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "right", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "bottom", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "left", - "value": { - "name": "number", - "required": false - } - } - ] - } - }, - "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." - }, - "barStyle": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "Style for the bottom navigation bar. You can pass a custom background color here:\n\n```js\nbarStyle={{ backgroundColor: '#694fad' }}\n```" - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "activeIndicatorStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'bottom-navigation'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/BottomNavigation/BottomNavigation.tsx" - ] - }, - "BottomNavigation/BottomNavigationBar": { - "filepath": "BottomNavigation/BottomNavigationBar.tsx", - "title": "BottomNavigation.Bar", - "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", - "link": "bottom-navigation-bar", - "data": { - "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", - "displayName": "BottomNavigation.Bar", - "methods": [], - "statics": [], - "props": { - "shifting": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." - }, - "labeled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether tabs should be spread across the entire width." - }, - "navigationState": { - "required": true, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n index: number;\n routes: Route[];\n}", - "signature": { - "properties": [ - { - "key": "index", - "value": { - "name": "number", - "required": true - } - }, - { - "key": "routes", - "value": { - "name": "Array", - "elements": [ - { - "name": "Route" - } - ], - "raw": "Route[]", - "required": true - } - } - ] - } - }, - "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `color`: color to use as background color for shifting bottom navigation @deprecatedProperty In v5.x works only with theme version 2.\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `accessibilityLabel`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation.Bar` is a controlled component, which means the `index` needs to be updated via the `onTabPress` callback." - }, - "renderIcon": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React Element to be used as tab icon." - }, - "renderLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: string;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: string;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which React Element to be used as tab label." - }, - "renderTouchable": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: TouchableProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "elements": [ - { - "name": "TouchableRippleProps" - }, - { - "name": "signature", - "type": "object", - "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "key", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "children", - "value": { - "name": "ReactReactNode", - "raw": "React.ReactNode", - "required": true - } - }, - { - "key": "borderless", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "centered", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "rippleColor", - "value": { - "name": "ColorValue", - "required": false - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS.", - "defaultValue": { - "value": "({ key, ...props }: TouchableProps) => (\n \n)", - "computed": false - } - }, - "getAccessibilityLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route.accessibilityLabel` by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) =>\nroute.accessibilityLabel", - "computed": false - } - }, - "getBadge": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => boolean | number | string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "boolean | number | string | undefined", - "elements": [ - { - "name": "boolean" - }, - { - "name": "number" - }, - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get badge for the tab, uses `route.badge` by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.badge", - "computed": false - } - }, - "getColor": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get color for the tab, uses `route.color` by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.color", - "computed": false - } - }, - "getLabelText": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.title", - "computed": false - } - }, - "getTestID": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get the id to locate this tab button in tests, uses `route.testID` by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.testID", - "computed": false - } - }, - "onTabPress": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab press. It receives the route for the pressed tab. Use this to update the navigation state." - }, - "onTabLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab long press. It receives the route for the pressed tab" - }, - "activeColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the active tab." - }, - "inactiveColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the inactive tab." - }, - "animationEasing": { - "required": false, - "tsType": { - "name": "union", - "raw": "EasingFunction | undefined", - "elements": [ - { - "name": "EasingFunction" - }, - { - "name": "undefined" - } - ] - }, - "description": "The scene animation Easing." - }, - "keyboardHidesNavigationBar": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", - "defaultValue": { - "value": "Platform.OS === 'android'", - "computed": false - } - }, - "safeAreaInsets": { - "required": false, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", - "signature": { - "properties": [ - { - "key": "top", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "right", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "bottom", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "left", - "value": { - "name": "number", - "required": false - } - } - ] - } - }, - "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "activeIndicatorStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'bottom-navigation-bar'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/BottomNavigation/BottomNavigationBar.tsx" - ], - "group": "BottomNavigation" - }, - "Button/Button": { - "filepath": "Button/Button.tsx", - "title": "Button", - "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "button", - "data": { - "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Button", - "methods": [], - "statics": [], - "props": { - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal'", - "elements": [ - { - "name": "literal", - "value": "'text'" - }, - { - "name": "literal", - "value": "'outlined'" - }, - { - "name": "literal", - "value": "'contained'" - }, - { - "name": "literal", - "value": "'elevated'" - }, - { - "name": "literal", - "value": "'contained-tonal'" - } - ] - }, - "description": "Mode of the button. You can change the mode to adjust the styling to give it desired emphasis.\n- `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options.\n- `outlined` - button with an outline without background, typically used for important, but not primary action – represents medium emphasis.\n- `contained` - button with a background color, used for important action, have the most visual impact and high emphasis.\n- `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background. @supported Available in v5.x with theme version 3\n- `contained-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. @supported Available in v5.x with theme version 3", - "defaultValue": { - "value": "'text'", - "computed": false - } - }, - "dark": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the color is a dark color. A dark button will render light text and vice-versa. Only applicable for:\n * `contained` mode for theme version 2\n * `contained`, `contained-tonal` and `elevated` modes for theme version 3." - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Use a compact look, useful for `text` buttons in a row." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "@deprecated Deprecated in v5.x - use `buttonColor` or `textColor` instead.\nCustom text color for flat button, or background color for contained button." - }, - "buttonColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom button's background color." - }, - "textColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom button's text color." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "loading": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show a loading indicator." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Button`." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Label text of the button." - }, - "uppercase": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Make the label text uppercased. Note that this won't work if you pass React elements as children." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "accessibilityHint": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility hint for the button. This is read by the screen reader when the user taps the button." - }, - "accessibilityRole": { - "required": false, - "tsType": { - "name": "AccessibilityRole" - }, - "description": "Accessibility role for the button. The \"button\" role is set by default.", - "defaultValue": { - "value": "'button'", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touch is released even before onPress." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of button's inner content.\nUse this prop to apply custom height and width, to set a custom padding or to set the icon on the right with `flexDirection: 'row-reverse'`." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the button text." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "touchableRef": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "Reference for the touchable" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'button'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Button/Button.tsx" - ] - }, - "Card/Card": { - "filepath": "Card/Card.tsx", - "title": "Card", - "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "card", - "data": { - "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Card", - "methods": [], - "statics": [], - "props": { - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'elevated' | 'outlined' | 'contained'", - "elements": [ - { - "name": "literal", - "value": "'elevated'" - }, - { - "name": "literal", - "value": "'outlined'" - }, - { - "name": "literal", - "value": "'contained'" - } - ] - }, - "description": "Mode of the Card.\n- `elevated` - Card with elevation.\n- `contained` - Card without outline and elevation @supported Available in v5.x with theme version 3\n- `outlined` - Card with an outline.", - "defaultValue": { - "value": "'elevated'", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Card`." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touch is released even before onPress." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "If true, disable all interactions for this component." - }, - "elevation": { - "required": false, - "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] - }, - "description": "Changes Card shadow and background on iOS and Android.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of card's inner content." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Pass down testID from card props to touchable", - "defaultValue": { - "value": "'card'", - "computed": false - } - }, - "accessible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Pass down accessible from card props to touchable" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/Card.tsx" - ] - }, - "Card/CardActions": { - "filepath": "Card/CardActions.tsx", - "title": "Card.Actions", - "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "card-actions", - "data": { - "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Card.Actions", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Items inside the `CardActions`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardActions.tsx" - ], - "group": "Card" - }, - "Card/CardContent": { - "filepath": "Card/CardContent.tsx", - "title": "Card.Content", - "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", - "link": "card-content", - "data": { - "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Card.Content", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Items inside the `Card.Content`." - }, - "index": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "total": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "siblings": { - "required": false, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "string" - } - ], - "raw": "Array" - }, - "description": "@internal" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardContent.tsx" - ], - "group": "Card" - }, - "Card/CardCover": { - "filepath": "Card/CardCover.tsx", - "title": "Card.Cover", - "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", - "link": "card-cover", - "data": { - "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", - "displayName": "Card.Cover", - "methods": [], - "statics": [], - "props": { - "index": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "total": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardCover.tsx" - ], - "group": "Card" - }, - "Card/CardTitle": { - "filepath": "Card/CardTitle.tsx", - "title": "Card.Title", - "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", - "link": "card-title", - "data": { - "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", - "displayName": "Card.Title", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text for the title. Note that this will only accept a string or ``-based node." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the title." - }, - "titleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Number of lines for the title.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "titleVariant": { - "required": false, - "tsType": { - "name": "unknown" - }, - "description": "@supported Available in v5.x with theme version 3\n\nTitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyLarge'", - "computed": false - } - }, - "subtitle": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text for the subtitle. Note that this will only accept a string or ``-based node." - }, - "subtitleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the subtitle." - }, - "subtitleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Number of lines for the subtitle.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "subtitleVariant": { - "required": false, - "tsType": { - "name": "unknown" - }, - "description": "@supported Available in v5.x with theme version 3\n\nSubtitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyMedium'", - "computed": false - } - }, - "left": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { size: number }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ size: number }", - "signature": { - "properties": [ - { - "key": "size", - "value": { - "name": "number", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the left side." - }, - "leftStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the left element wrapper." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { size: number }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ size: number }", - "signature": { - "properties": [ - { - "key": "size", - "value": { - "name": "number", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side." - }, - "rightStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the right element wrapper." - }, - "index": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "total": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "subtitleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a subtitle font can reach." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardTitle.tsx" - ], - "group": "Card" - }, - "Checkbox/Checkbox": { - "filepath": "Checkbox/Checkbox.tsx", - "title": "Checkbox", - "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", - "link": "checkbox", - "data": { - "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Checkbox", - "methods": [], - "statics": [], - "props": { - "status": { - "required": true, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked' | 'indeterminate'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - }, - { - "name": "literal", - "value": "'indeterminate'" - } - ] - }, - "description": "Status of checkbox." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether checkbox is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked checkbox." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for checkbox." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Checkbox/Checkbox.tsx" - ] - }, - "Checkbox/CheckboxAndroid": { - "filepath": "Checkbox/CheckboxAndroid.tsx", - "title": "Checkbox.Android", - "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "checkbox-android", - "data": { - "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "Checkbox.Android", - "methods": [], - "statics": [], - "props": { - "status": { - "required": true, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked' | 'indeterminate'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - }, - { - "name": "literal", - "value": "'indeterminate'" - } - ] - }, - "description": "Status of checkbox." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether checkbox is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked checkbox." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for checkbox." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Checkbox/CheckboxAndroid.tsx" - ], - "group": "Checkbox" - }, - "Checkbox/CheckboxIOS": { - "filepath": "Checkbox/CheckboxIOS.tsx", - "title": "Checkbox.IOS", - "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "checkbox-ios", - "data": { - "description": "Checkboxes allow the selection of multiple options from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "Checkbox.IOS", - "methods": [], - "statics": [], - "props": { - "status": { - "required": true, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked' | 'indeterminate'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - }, - { - "name": "literal", - "value": "'indeterminate'" - } - ] - }, - "description": "Status of checkbox." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether checkbox is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for checkbox." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Checkbox/CheckboxIOS.tsx" - ], - "group": "Checkbox" - }, - "Checkbox/CheckboxItem": { - "filepath": "Checkbox/CheckboxItem.tsx", - "title": "Checkbox.Item", - "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "checkbox-item", - "data": { - "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Checkbox.Item", - "methods": [], - "statics": [], - "props": { - "status": { - "required": true, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked' | 'indeterminate'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - }, - { - "name": "literal", - "value": "'indeterminate'" - } - ] - }, - "description": "Status of checkbox." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether checkbox is disabled." - }, - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Label to be displayed on the item." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", - "defaultValue": { - "value": "label", - "computed": true - } - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked checkbox." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for checkbox." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Additional styles for container View." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach.", - "defaultValue": { - "value": "1.5", - "computed": false - } - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Label element." - }, - "labelVariant": { - "required": false, - "tsType": { - "name": "unknown" - }, - "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyLarge'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - }, - "position": { - "required": false, - "tsType": { - "name": "union", - "raw": "'leading' | 'trailing'", - "elements": [ - { - "name": "literal", - "value": "'leading'" - }, - { - "name": "literal", - "value": "'trailing'" - } - ] - }, - "description": "Checkbox control position.", - "defaultValue": { - "value": "'trailing'", - "computed": false - } - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'android' | 'ios'", - "elements": [ - { - "name": "literal", - "value": "'android'" - }, - { - "name": "literal", - "value": "'ios'" - } - ] - }, - "description": "Whether `` or `` should be used.\nLeft undefined `` will be used." - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Checkbox/CheckboxItem.tsx" - ], - "group": "Checkbox" - }, - "Chip/Chip": { - "filepath": "Chip/Chip.tsx", - "title": "Chip", - "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n
      \n
    • Present multiple options
    • \n
    • Represent attributes active or chosen
    • \n
    • Present filter options
    • \n
    • Trigger actions related to primary content
    • \n
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", - "link": "chip", - "data": { - "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n
      \n
    • Present multiple options
    • \n
    • Represent attributes active or chosen
    • \n
    • Present filter options
    • \n
    • Trigger actions related to primary content
    • \n
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", - "displayName": "Chip", - "methods": [], - "statics": [], - "props": { - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'outlined'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'outlined'" - } - ] - }, - "description": "Mode of the chip.\n- `flat` - flat chip without outline.\n- `outlined` - chip with an outline.", - "defaultValue": { - "value": "'flat'", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text content of the `Chip`." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Chip`. Both icon and avatar cannot be specified." - }, - "avatar": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Avatar to display for the `Chip`. Both icon and avatar cannot be specified." - }, - "closeIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display as the close button for the `Chip`. The icon appears only when the onClose prop is specified." - }, - "selected": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether chip is selected.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "selectedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Whether to style the chip color as selected.\nNote: With theme version 3 `selectedColor` doesn't apply to the `icon`.\n If you want specify custom color for the `icon`, render your own `Icon` component." - }, - "showSelectedOverlay": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether to display overlay on selected chip", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "showSelectedCheck": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to display default check icon on selected chip.\nNote: Check will not be shown if `icon` is specified. If specified, `icon` will be shown regardless of `selected`.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the chip is disabled. A disabled chip is greyed out and `onPress` is not called on touch.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the chip. This is read by the screen reader when the user taps the chip." - }, - "closeIconAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon.", - "defaultValue": { - "value": "'Close'", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touch is released even before onPress." - }, - "onClose": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on close button press. The close button appears only when this prop is specified." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nSets smaller horizontal paddings `12dp` around label, when there is only label." - }, - "elevated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether chip should have the elevation.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "textStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of chip's text" - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Pass down testID from chip props to touchable for Detox tests.", - "defaultValue": { - "value": "'chip'", - "computed": false - } - }, - "ellipsizeMode": { - "required": false, - "tsType": { - "name": "EllipsizeProp" - }, - "description": "Ellipsize Mode for the children text" - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "accessibilityRole": { - "defaultValue": { - "value": "'button'", - "computed": false - }, - "required": false, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Chip/Chip.tsx" - ] - }, - "DataTable/DataTable": { - "filepath": "DataTable/DataTable.tsx", - "title": "DataTable", - "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "data-table", - "data": { - "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "DataTable", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTable`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTable.tsx" - ] - }, - "DataTable/DataTableCell": { - "filepath": "DataTable/DataTableCell.tsx", - "title": "DataTable.Cell", - "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "data-table-cell", - "data": { - "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "DataTable.Cell", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTableCell`." - }, - "numeric": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Align the text to the right. Generally monetary or number fields are aligned to right." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "textStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Text content style of the `DataTableCell`." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableCell.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTableHeader": { - "filepath": "DataTable/DataTableHeader.tsx", - "title": "DataTable.Header", - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "link": "data-table-header", - "data": { - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "DataTable.Header", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTableHeader`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableHeader.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTablePagination": { - "filepath": "DataTable/DataTablePagination.tsx", - "title": "DataTable.Pagination", - "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "data-table-pagination", - "data": { - "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "DataTable.Pagination", - "methods": [], - "statics": [], - "props": { - "page": { - "required": true, - "tsType": { - "name": "number" - }, - "description": "The currently visible page (starting with 0)." - }, - "numberOfPages": { - "required": true, - "tsType": { - "name": "number" - }, - "description": "The total number of pages." - }, - "onPageChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(page: number) => void", - "signature": { - "arguments": [ - { - "name": "page", - "type": { - "name": "number" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on page change." - }, - "showFastPaginationControls": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show fast forward and fast rewind buttons in pagination. False by default.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "paginationControlRippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the pagination control ripple effect." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "numberOfItemsPerPage": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The current number of rows per page." - }, - "numberOfItemsPerPageList": { - "required": false, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "number" - } - ], - "raw": "Array" - }, - "description": "Options for a number of rows per page to choose from." - }, - "onItemsPerPageChange": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(numberOfItemsPerPage: number) => void", - "signature": { - "arguments": [ - { - "name": "numberOfItemsPerPage", - "type": { - "name": "number" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "The function to set the number of rows per page." - }, - "dropdownItemRippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the dropdown item ripple effect." - }, - "selectPageDropdownRippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the select page dropdown ripple effect." - }, - "selectPageDropdownLabel": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Label text for select page dropdown to display." - }, - "selectPageDropdownAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "AccessibilityLabel for `selectPageDropdownLabel`." - }, - "label": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Label text to display which indicates current pagination." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "AccessibilityLabel for `label`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTablePagination.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTableRow": { - "filepath": "DataTable/DataTableRow.tsx", - "title": "DataTable.Row", - "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "data-table-row", - "data": { - "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "DataTable.Row", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTableRow`." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "pointerEvents": { - "required": false, - "tsType": { - "name": "ViewProps['pointerEvents']", - "raw": "ViewProps['pointerEvents']" - }, - "description": "`pointerEvents` passed to the `View` container, which is wrapping children within `TouchableRipple`." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableRow.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTableTitle": { - "filepath": "DataTable/DataTableTitle.tsx", - "title": "DataTable.Title", - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "link": "data-table-title", - "data": { - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "DataTable.Title", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text content of the `DataTableTitle`." - }, - "numeric": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Align the text to the right. Generally monetary or number fields are aligned to right." - }, - "sortDirection": { - "required": false, - "tsType": { - "name": "union", - "raw": "'ascending' | 'descending'", - "elements": [ - { - "name": "literal", - "value": "'ascending'" - }, - { - "name": "literal", - "value": "'descending'" - } - ] - }, - "description": "Direction of sorting. An arrow indicating the direction is displayed when this is given." - }, - "numberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of lines to show.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "textStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Text content style of the `DataTableTitle`." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableTitle.tsx" - ], - "group": "DataTable" - }, - "Dialog/Dialog": { - "filepath": "Dialog/Dialog.tsx", - "title": "Dialog", - "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog", - "data": { - "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog", - "methods": [], - "statics": [], - "props": { - "dismissable": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking outside the dialog dismiss it.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "dismissableBackButton": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking Android hardware back button dismiss dialog.", - "defaultValue": { - "value": "dismissable", - "computed": true - } - }, - "onDismiss": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the user dismisses the dialog." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines Whether the dialog is visible.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Dialog`." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/Dialog.tsx" - ] - }, - "Dialog/DialogActions": { - "filepath": "Dialog/DialogActions.tsx", - "title": "Dialog.Actions", - "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-actions", - "data": { - "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Actions", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DialogActions`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogActions.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogContent": { - "filepath": "Dialog/DialogContent.tsx", - "title": "Dialog.Content", - "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-content", - "data": { - "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Content", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DialogContent`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogContent.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogIcon": { - "filepath": "Dialog/DialogIcon.tsx", - "title": "Dialog.Icon", - "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", - "link": "dialog-icon", - "data": { - "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Icon", - "methods": [], - "statics": [], - "props": { - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for action icon." - }, - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Name of the icon to show." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Optional icon size.", - "defaultValue": { - "value": "24", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogIcon.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogScrollArea": { - "filepath": "Dialog/DialogScrollArea.tsx", - "title": "Dialog.ScrollArea", - "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-scroll-area", - "data": { - "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.ScrollArea", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DialogScrollArea`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogScrollArea.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogTitle": { - "filepath": "Dialog/DialogTitle.tsx", - "title": "Dialog.Title", - "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-title", - "data": { - "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Title", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Title text for the `DialogTitle`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogTitle.tsx" - ], - "group": "Dialog" - }, - "Divider": { - "filepath": "Divider.tsx", - "title": "Divider", - "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", - "link": "divider", - "data": { - "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Divider", - "methods": [], - "statics": [], - "props": { - "leftInset": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@renamed Renamed from 'inset' to 'leftInset` in v5.x\nWhether divider has a left inset." - }, - "horizontalInset": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n Whether divider has a horizontal inset on both sides.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "bold": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n Whether divider should be bolded.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Divider.tsx" - ] - }, - "Drawer/DrawerCollapsedItem": { - "filepath": "Drawer/DrawerCollapsedItem.tsx", - "title": "Drawer.CollapsedItem", - "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "drawer-collapsed-item", - "data": { - "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Drawer.CollapsedItem", - "methods": [], - "statics": [], - "props": { - "label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "The label text of the item." - }, - "badge": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number | boolean", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - }, - { - "name": "boolean" - } - ] - }, - "description": "Badge to show on the icon, can be `true` to show a dot, `string` or `number` to show text.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the item is disabled." - }, - "focusedIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the focused destination icon, can be a string, an image source or a react component" - }, - "unfocusedIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the unfocused destination icon, can be a string, an image source or a react component" - }, - "active": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to highlight the drawer item as active." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'drawer-collapsed-item'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerCollapsedItem.tsx" - ], - "group": "Drawer" - }, - "Drawer/DrawerItem": { - "filepath": "Drawer/DrawerItem.tsx", - "title": "Drawer.Item", - "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "drawer-item", - "data": { - "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Drawer.Item", - "methods": [], - "statics": [], - "props": { - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "The label text of the item." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `DrawerItem`." - }, - "active": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to highlight the drawer item as active." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the item is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: string }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: string }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side. For instance a Badge." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerItem.tsx" - ], - "group": "Drawer" - }, - "Drawer/DrawerSection": { - "filepath": "Drawer/DrawerSection.tsx", - "title": "Drawer.Section", - "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "drawer-section", - "data": { - "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Drawer.Section", - "methods": [], - "statics": [], - "props": { - "title": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Title to show as the header for the section." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Drawer.Section`." - }, - "showDivider": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show `Divider` at the end of the section. True by default.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerSection.tsx" - ], - "group": "Drawer" - }, - "FAB/FAB": { - "filepath": "FAB/FAB.tsx", - "title": "FAB", - "description": "A floating action button represents the primary action on a screen. It appears in front of all screen content.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n})\n\nexport default MyComponent;\n```", - "link": "fab", - "data": { - "description": "A floating action button represents the primary action on a screen. It appears in front of all screen content.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n})\n\nexport default MyComponent;\n```", - "displayName": "FAB", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `FAB`. It's optional only if `label` is defined." - }, - "label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Optional label for extended `FAB`. It's optional only if `icon` is defined." - }, - "uppercase": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Make the label text uppercased." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB.\nUses `label` by default if specified.", - "defaultValue": { - "value": "label", - "computed": true - } - }, - "accessibilityState": { - "required": false, - "tsType": { - "name": "AccessibilityState" - }, - "description": "Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB." - }, - "animated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether an icon change is animated.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "small": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@deprecated Deprecated in v.5x - use prop size=\"small\".\n\n Whether FAB is mini-sized, used to create visual continuity with other elements. This has no effect if `label` is specified." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the icon and label of the `FAB`." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether `FAB` is currently visible.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "loading": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show a loading indicator." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "size": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'medium' | 'large'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'large'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nSize of the `FAB`.\n- `small` - FAB with small height (40).\n- `medium` - FAB with default medium height (56).\n- `large` - FAB with large height (96).", - "defaultValue": { - "value": "'medium'", - "computed": false - } - }, - "customSize": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Custom size for the `FAB`. This prop takes precedence over size prop" - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'elevated'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'elevated'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nMode of the `FAB`. You can change the mode to adjust the the shadow:\n- `flat` - button without a shadow.\n- `elevated` - button with a shadow.", - "defaultValue": { - "value": "'elevated'", - "computed": false - } - }, - "variant": { - "required": false, - "tsType": { - "name": "union", - "raw": "'primary' | 'secondary' | 'tertiary' | 'surface'", - "elements": [ - { - "name": "literal", - "value": "'primary'" - }, - { - "name": "literal", - "value": "'secondary'" - }, - { - "name": "literal", - "value": "'tertiary'" - }, - { - "name": "literal", - "value": "'surface'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nColor mappings variant for combinations of container and icon colors.", - "defaultValue": { - "value": "'primary'", - "computed": false - } - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'fab'", - "computed": false - } - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/FAB/FAB.tsx" - ] - }, - "FAB/AnimatedFAB": { - "filepath": "FAB/AnimatedFAB.tsx", - "title": "AnimatedFAB", - "description": "An animated, extending horizontally floating action button represents the primary action in an application.\n\n## Usage\n```js\nimport React from 'react';\nimport {\n StyleProp,\n ViewStyle,\n Animated,\n StyleSheet,\n Platform,\n ScrollView,\n Text,\n SafeAreaView,\n I18nManager,\n} from 'react-native';\nimport { AnimatedFAB } from 'react-native-paper';\n\nconst MyComponent = ({\n animatedValue,\n visible,\n extended,\n label,\n animateFrom,\n style,\n iconMode,\n}) => {\n const [isExtended, setIsExtended] = React.useState(true);\n\n const isIOS = Platform.OS === 'ios';\n\n const onScroll = ({ nativeEvent }) => {\n const currentScrollPosition =\n Math.floor(nativeEvent?.contentOffset?.y) ?? 0;\n\n setIsExtended(currentScrollPosition <= 0);\n };\n\n const fabStyle = { [animateFrom]: 16 };\n\n return (\n \n \n {[...new Array(100).keys()].map((_, i) => (\n {i}\n ))}\n \n console.log('Pressed')}\n visible={visible}\n animateFrom={'right'}\n iconMode={'static'}\n style={[styles.fabStyle, style, fabStyle]}\n />\n \n );\n};\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n container: {\n flexGrow: 1,\n },\n fabStyle: {\n bottom: 16,\n right: 16,\n position: 'absolute',\n },\n});\n```", - "link": "animated-fab", - "data": { - "description": "An animated, extending horizontally floating action button represents the primary action in an application.\n\n## Usage\n```js\nimport React from 'react';\nimport {\n StyleProp,\n ViewStyle,\n Animated,\n StyleSheet,\n Platform,\n ScrollView,\n Text,\n SafeAreaView,\n I18nManager,\n} from 'react-native';\nimport { AnimatedFAB } from 'react-native-paper';\n\nconst MyComponent = ({\n animatedValue,\n visible,\n extended,\n label,\n animateFrom,\n style,\n iconMode,\n}) => {\n const [isExtended, setIsExtended] = React.useState(true);\n\n const isIOS = Platform.OS === 'ios';\n\n const onScroll = ({ nativeEvent }) => {\n const currentScrollPosition =\n Math.floor(nativeEvent?.contentOffset?.y) ?? 0;\n\n setIsExtended(currentScrollPosition <= 0);\n };\n\n const fabStyle = { [animateFrom]: 16 };\n\n return (\n \n \n {[...new Array(100).keys()].map((_, i) => (\n {i}\n ))}\n \n console.log('Pressed')}\n visible={visible}\n animateFrom={'right'}\n iconMode={'static'}\n style={[styles.fabStyle, style, fabStyle]}\n />\n \n );\n};\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n container: {\n flexGrow: 1,\n },\n fabStyle: {\n bottom: 16,\n right: 16,\n position: 'absolute',\n },\n});\n```", - "displayName": "AnimatedFAB", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `FAB`." - }, - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Label for extended `FAB`." - }, - "uppercase": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Make the label text uppercased." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB.\nUses `label` by default if specified.", - "defaultValue": { - "value": "label", - "computed": true - } - }, - "accessibilityState": { - "required": false, - "tsType": { - "name": "AccessibilityState" - }, - "description": "Accessibility state for the FAB. This is read by the screen reader when the user taps the FAB." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the icon and label of the `FAB`." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether `FAB` is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether `FAB` is currently visible.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "iconMode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'static' | 'dynamic'", - "elements": [ - { - "name": "literal", - "value": "'static'" - }, - { - "name": "literal", - "value": "'dynamic'" - } - ] - }, - "description": "Whether icon should be translated to the end of extended `FAB` or be static and stay in the same place. The default value is `dynamic`.", - "defaultValue": { - "value": "'dynamic'", - "computed": false - } - }, - "animateFrom": { - "required": false, - "tsType": { - "name": "union", - "raw": "'left' | 'right'", - "elements": [ - { - "name": "literal", - "value": "'left'" - }, - { - "name": "literal", - "value": "'right'" - } - ] - }, - "description": "Indicates from which direction animation should be performed. The default value is `right`.", - "defaultValue": { - "value": "'right'", - "computed": false - } - }, - "extended": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether `FAB` should start animation to extend.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "variant": { - "required": false, - "tsType": { - "name": "union", - "raw": "'primary' | 'secondary' | 'tertiary' | 'surface'", - "elements": [ - { - "name": "literal", - "value": "'primary'" - }, - { - "name": "literal", - "value": "'secondary'" - }, - { - "name": "literal", - "value": "'tertiary'" - }, - { - "name": "literal", - "value": "'surface'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nColor mappings variant for combinations of container and icon colors.", - "defaultValue": { - "value": "'primary'", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'animated-fab'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/FAB/AnimatedFAB.tsx" - ] - }, - "FAB/FABGroup": { - "filepath": "FAB/FABGroup.tsx", - "title": "FAB.Group", - "description": "A component to display a stack of FABs with related actions in a speed dial.\nTo render the group above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { FAB, Portal, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [state, setState] = React.useState({ open: false });\n\n const onStateChange = ({ open }) => setState({ open });\n\n const { open } = state;\n\n return (\n \n \n console.log('Pressed add') },\n {\n icon: 'star',\n label: 'Star',\n onPress: () => console.log('Pressed star'),\n },\n {\n icon: 'email',\n label: 'Email',\n onPress: () => console.log('Pressed email'),\n },\n {\n icon: 'bell',\n label: 'Remind',\n onPress: () => console.log('Pressed notifications'),\n },\n ]}\n onStateChange={onStateChange}\n onPress={() => {\n if (open) {\n // do something if the speed dial is open\n }\n }}\n />\n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "fab-group", - "data": { - "description": "A component to display a stack of FABs with related actions in a speed dial.\nTo render the group above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { FAB, Portal, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [state, setState] = React.useState({ open: false });\n\n const onStateChange = ({ open }) => setState({ open });\n\n const { open } = state;\n\n return (\n \n \n console.log('Pressed add') },\n {\n icon: 'star',\n label: 'Star',\n onPress: () => console.log('Pressed star'),\n },\n {\n icon: 'email',\n label: 'Email',\n onPress: () => console.log('Pressed email'),\n },\n {\n icon: 'bell',\n label: 'Remind',\n onPress: () => console.log('Pressed notifications'),\n },\n ]}\n onStateChange={onStateChange}\n onPress={() => {\n if (open) {\n // do something if the speed dial is open\n }\n }}\n />\n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "FAB.Group", - "methods": [], - "statics": [], - "props": { - "actions": { - "required": true, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n icon: IconSource;\n label?: string;\n color?: string;\n labelTextColor?: string;\n accessibilityLabel?: string;\n accessibilityHint?: string;\n style?: Animated.WithAnimatedValue>;\n containerStyle?: Animated.WithAnimatedValue>;\n wrapperStyle?: StyleProp;\n labelStyle?: StyleProp;\n labelMaxFontSizeMultiplier?: number;\n onPress: (e: GestureResponderEvent) => void;\n size?: 'small' | 'medium';\n testID?: string;\n rippleColor?: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": true - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "color", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "labelTextColor", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "accessibilityLabel", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "accessibilityHint", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "style", - "value": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>", - "required": false - } - }, - { - "key": "containerStyle", - "value": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>", - "required": false - } - }, - { - "key": "wrapperStyle", - "value": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp", - "required": false - } - }, - { - "key": "labelStyle", - "value": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp", - "required": false - } - }, - { - "key": "labelMaxFontSizeMultiplier", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "size", - "value": { - "name": "union", - "raw": "'small' | 'medium'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - } - ], - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "rippleColor", - "value": { - "name": "ColorValue", - "required": false - } - } - ] - } - } - ], - "raw": "Array<{\n icon: IconSource;\n label?: string;\n color?: string;\n labelTextColor?: string;\n accessibilityLabel?: string;\n accessibilityHint?: string;\n style?: Animated.WithAnimatedValue>;\n containerStyle?: Animated.WithAnimatedValue>;\n wrapperStyle?: StyleProp;\n labelStyle?: StyleProp;\n labelMaxFontSizeMultiplier?: number;\n onPress: (e: GestureResponderEvent) => void;\n size?: 'small' | 'medium';\n testID?: string;\n rippleColor?: ColorValue;\n}>" - }, - "description": "Action items to display in the form of a speed dial.\nAn action item should contain the following properties:\n- `icon`: icon to display (required)\n- `label`: optional label text\n- `color`: custom icon color of the action item\n- `labelTextColor`: custom label text color of the action item\n- `accessibilityLabel`: accessibility label for the action, uses label by default if specified\n- `accessibilityHint`: accessibility hint for the action\n- `style`: pass additional styles for the fab item, for example, `backgroundColor`\n- `containerStyle`: pass additional styles for the fab item label container, for example, `backgroundColor` @supported Available in 5.x\n- `wrapperStyle`: pass additional styles for the wrapper of the action item.\n- `labelStyle`: pass additional styles for the fab item label, for example, `fontSize`\n- `labelMaxFontSizeMultiplier`: specifies the largest possible scale a title font can reach.\n- `onPress`: callback that is called when `FAB` is pressed (required)\n- `onLongPress`: callback that is called when `FAB` is long pressed\n- `toggleStackOnLongPress`: callback that is called when `FAB` is long pressed\n- `size`: size of action item. Defaults to `small`. @supported Available in v5.x\n- `testID`: testID to be used on tests\n- `rippleColor`: color of the ripple effect." - }, - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `FAB`.\nYou can toggle it based on whether the speed dial is open to display a different icon." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the FAB. This is read by the screen reader when the user taps the FAB." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the `FAB`." - }, - "backdropColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom backdrop color for opened speed dial background." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on pressing the `FAB`." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long pressing the `FAB`." - }, - "toggleStackOnLongPress": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Makes actions stack appear on long press instead of on press." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Changes the delay for long press reaction.", - "defaultValue": { - "value": "200", - "computed": false - } - }, - "enableLongPressWhenStackOpened": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Allows for onLongPress when stack is opened.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "open": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether the speed dial is open." - }, - "onStateChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(state: { open: boolean }) => void", - "signature": { - "arguments": [ - { - "name": "state", - "type": { - "name": "signature", - "type": "object", - "raw": "{ open: boolean }", - "signature": { - "properties": [ - { - "key": "open", - "value": { - "name": "boolean", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback which is called on opening and closing the speed dial.\nThe open state needs to be updated when it's called, otherwise the change is dropped." - }, - "visible": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether `FAB` is currently visible." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the group. You can use it to pass additional styles if you need.\nFor example, you can set an additional padding if you have a tab bar at the bottom." - }, - "fabStyle": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "Style for the FAB. It allows to pass the FAB button styles, such as backgroundColor." - }, - "variant": { - "required": false, - "tsType": { - "name": "union", - "raw": "'primary' | 'secondary' | 'tertiary' | 'surface'", - "elements": [ - { - "name": "literal", - "value": "'primary'" - }, - { - "name": "literal", - "value": "'secondary'" - }, - { - "name": "literal", - "value": "'tertiary'" - }, - { - "name": "literal", - "value": "'surface'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nColor mappings variant for combinations of container and icon colors.", - "defaultValue": { - "value": "'primary'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Optional label for `FAB`." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Pass down testID from Group props to FAB." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/FAB/FABGroup.tsx" - ], - "group": "FAB" - }, - "HelperText/HelperText": { - "filepath": "HelperText/HelperText.tsx", - "title": "HelperText", - "description": "Helper text is used in conjuction with input elements to provide additional hints for the user.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { HelperText, TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const onChangeText = text => setText(text);\n\n const hasErrors = () => {\n return !text.includes('@');\n };\n\n return (\n \n \n \n Email address is invalid!\n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "helper-text", - "data": { - "description": "Helper text is used in conjuction with input elements to provide additional hints for the user.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { HelperText, TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const onChangeText = text => setText(text);\n\n const hasErrors = () => {\n return !text.includes('@');\n };\n\n return (\n \n \n \n Email address is invalid!\n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "HelperText", - "methods": [], - "statics": [], - "props": { - "type": { - "required": false, - "tsType": { - "name": "union", - "raw": "'error' | 'info'", - "elements": [ - { - "name": "literal", - "value": "'error'" - }, - { - "name": "literal", - "value": "'info'" - } - ] - }, - "description": "Type of the helper text.", - "defaultValue": { - "value": "'info'", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text content of the HelperText." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to display the helper text.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "padding": { - "required": false, - "tsType": { - "name": "union", - "raw": "'none' | 'normal'", - "elements": [ - { - "name": "literal", - "value": "'none'" - }, - { - "name": "literal", - "value": "'normal'" - } - ] - }, - "description": "Whether to apply padding to the helper text.", - "defaultValue": { - "value": "'normal'", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the text input tied with helper text is disabled." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/HelperText/HelperText.tsx" - ] - }, - "IconButton/IconButton": { - "filepath": "IconButton/IconButton.tsx", - "title": "IconButton", - "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "icon-button", - "data": { - "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "IconButton", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'outlined' | 'contained' | 'contained-tonal'", - "elements": [ - { - "name": "literal", - "value": "'outlined'" - }, - { - "name": "literal", - "value": "'contained'" - }, - { - "name": "literal", - "value": "'contained-tonal'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nMode of the icon button. By default there is no specified mode - only pressable icon will be rendered." - }, - "iconColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "@renamed Renamed from 'color' to 'iconColor' in v5.x\nColor of the icon." - }, - "containerColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Background color of the icon container." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "selected": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether icon button is selected. A selected button receives alternative combination of icon and container colors.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the icon.", - "defaultValue": { - "value": "24", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "animated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether an icon change is animated.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of button's inner content.\nUse this prop to apply custom height and width or to set a custom padding`." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'icon-button'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "loading": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show a loading indicator.", - "defaultValue": { - "value": "false", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/IconButton/IconButton.tsx" - ] - }, - "Icon": { - "filepath": "Icon.tsx", - "title": "Icon", - "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "icon", - "data": { - "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Icon", - "methods": [], - "statics": [], - "props": { - "size": { - "required": true, - "tsType": { - "name": "number" - }, - "description": "Size of icon." - }, - "allowFontScaling": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "" - }, - "source": { - "required": true, - "tsType": { - "name": "any" - }, - "description": "Icon to display." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Color of the icon." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Icon.tsx" - ] - }, - "List/ListAccordion": { - "filepath": "List/ListAccordion.tsx", - "title": "List.Accordion", - "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "list-accordion", - "data": { - "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "List.Accordion", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Title text for the list accordion." - }, - "description": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Description text for the list accordion." - }, - "left": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: string; style: Style }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: string; style: Style }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the left side." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { isExpanded: boolean }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ isExpanded: boolean }", - "signature": { - "properties": [ - { - "key": "isExpanded", - "value": { - "name": "boolean", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side." - }, - "expanded": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the accordion is expanded\nIf this prop is provided, the accordion will behave as a \"controlled component\".\nYou'll need to update this prop when you want to toggle the component or on `onPress`." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the section." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the root TouchableRipple container." - }, - "containerStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the content container, which wraps the title and description." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Title element." - }, - "descriptionStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Description element." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "titleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "descriptionNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "descriptionMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a description font can reach." - }, - "id": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - }, - "description": "Id is used for distinguishing specific accordion when using List.AccordionGroup. Property is required when using List.AccordionGroup and has no impact on behavior when using standalone List.Accordion." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the TouchableRipple. This is read by the screen reader when the user taps the touchable." - }, - "pointerEvents": { - "required": false, - "tsType": { - "name": "ViewProps['pointerEvents']", - "raw": "ViewProps['pointerEvents']" - }, - "description": "`pointerEvents` passed to the `View` container", - "defaultValue": { - "value": "'none'", - "computed": false - } - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Amount of space between the touchable area and the edge of the component.\nThis can be used to enlarge the touchable area beyond the visible component." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListAccordion.tsx" - ], - "group": "List" - }, - "List/ListAccordionGroup": { - "filepath": "List/ListAccordionGroup.tsx", - "title": "List.AccordionGroup", - "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "list-accordion-group", - "data": { - "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "List.AccordionGroup", - "methods": [], - "statics": [], - "props": { - "onAccordionPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(expandedId: string | number) => void", - "signature": { - "arguments": [ - { - "name": "expandedId", - "type": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "expandedId": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - }, - "description": "Id of the currently expanded list accordion" - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing list accordions" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListAccordionGroup.tsx" - ], - "group": "List" - }, - "List/ListIcon": { - "filepath": "List/ListIcon.tsx", - "title": "List.Icon", - "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "list-icon", - "data": { - "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "List.Icon", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to show." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Color for the icon." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListIcon.tsx" - ], - "group": "List" - }, - "List/ListItem": { - "filepath": "List/ListItem.tsx", - "title": "List.Item", - "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "list-item", - "data": { - "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "List.Item", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "union", - "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: string;\n fontSize: number;\n }) => React.ReactNode)", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "unknown" - } - ] - }, - "description": "Title text for the list item." - }, - "description": { - "required": false, - "tsType": { - "name": "union", - "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: string;\n fontSize: number;\n }) => React.ReactNode)", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "unknown" - } - ] - }, - "description": "Description text for the list item or callback which returns a React element to display the description." - }, - "left": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: string; style: Style }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: string; style: Style }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the left side." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: string; style?: Style }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: string; style?: Style }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": false - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the root TouchableRipple container." - }, - "containerStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the content container, which wraps the title and description." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Title element." - }, - "descriptionStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Description element." - }, - "titleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "descriptionNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "titleEllipsizeMode": { - "required": false, - "tsType": { - "name": "EllipsizeProp" - }, - "description": "Ellipsize Mode for the Title. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" - }, - "descriptionEllipsizeMode": { - "required": false, - "tsType": { - "name": "EllipsizeProp" - }, - "description": "Ellipsize Mode for the Description. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "descriptionMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a description font can reach." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListItem.tsx" - ], - "group": "List" - }, - "List/ListSection": { - "filepath": "List/ListSection.tsx", - "title": "List.Section", - "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", - "link": "list-section", - "data": { - "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "List.Section", - "methods": [], - "statics": [], - "props": { - "title": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Title text for the section." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the section." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Title element." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListSection.tsx" - ], - "group": "List" - }, - "List/ListSubheader": { - "filepath": "List/ListSubheader.tsx", - "title": "List.Subheader", - "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", - "link": "list-subheader", - "data": { - "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", - "displayName": "List.Subheader", - "methods": [], - "statics": [], - "props": { - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Text element." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListSubheader.tsx" - ], - "group": "List" - }, - "Menu/Menu": { - "filepath": "Menu/Menu.tsx", - "title": "Menu", - "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n
    \n \n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", - "link": "menu", - "data": { - "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n
    \n
    \n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", - "displayName": "Menu", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether the Menu is currently visible." - }, - "anchor": { - "required": true, - "tsType": { - "name": "union", - "raw": "React.ReactNode | { x: number; y: number }", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "signature", - "type": "object", - "raw": "{ x: number; y: number }", - "signature": { - "properties": [ - { - "key": "x", - "value": { - "name": "number", - "required": true - } - }, - { - "key": "y", - "value": { - "name": "number", - "required": true - } - } - ] - } - } - ] - }, - "description": "The anchor to open the menu from. In most cases, it will be a button that opens the menu." - }, - "anchorPosition": { - "required": false, - "tsType": { - "name": "union", - "raw": "'top' | 'bottom'", - "elements": [ - { - "name": "literal", - "value": "'top'" - }, - { - "name": "literal", - "value": "'bottom'" - } - ] - }, - "description": "Whether the menu should open at the top of the anchor or at its bottom.\nApplied only when anchor is a node, not an x/y position." - }, - "statusBarHeight": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Extra margin to add at the top of the menu to account for translucent status bar on Android.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to and customize it.\nThis is automatically handled on iOS." - }, - "onDismiss": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback called when Menu is dismissed. The `visible` prop needs to be updated when this is called." - }, - "overlayAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the menu.", - "defaultValue": { - "value": "'Close menu'", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Menu`." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "Style of menu's inner content." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "elevation": { - "required": false, - "tsType": { - "name": "MD3Elevation" - }, - "description": "Elevation level of the menu's content. Shadow styles are calculated based on this value. Default `backgroundColor` is taken from the corresponding `theme.colors.elevation` property. By default equals `2`.\n@supported Available in v5.x with theme version 3", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'elevated'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'elevated'" - } - ] - }, - "description": "Mode of the menu's content.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.\n\n@supported Available in v5.x with theme version 3", - "defaultValue": { - "value": "'elevated'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "keyboardShouldPersistTaps": { - "required": false, - "tsType": { - "name": "ScrollViewProps['keyboardShouldPersistTaps']", - "raw": "ScrollViewProps['keyboardShouldPersistTaps']" - }, - "description": "Inner ScrollView prop" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'menu'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Menu/Menu.tsx" - ] - }, - "Menu/MenuItem": { - "filepath": "Menu/MenuItem.tsx", - "title": "Menu.Item", - "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", - "link": "menu-item", - "data": { - "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Menu.Item", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Title text for the `MenuItem`." - }, - "leadingIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@renamed Renamed from 'icon' to 'leadingIcon' in v5.x\n\nLeading icon to display for the `MenuItem`." - }, - "trailingIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@supported Available in v5.x with theme version 3\n\nTrailing icon to display for the `MenuItem`." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the 'item' is disabled. A disabled 'item' is greyed out and `onPress` is not called on touch." - }, - "dense": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n\nSets min height with densed layout." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach.", - "defaultValue": { - "value": "1.5", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the root TouchableRipple container.\n" - }, - "containerStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the outermost container that wraps the entire content, including leading and trailing icons and title text." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the content container, which wraps the title text." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the Title element." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'menu-item'", - "computed": false - } - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the Touchable. This is read by the screen reader when the user taps the component." - }, - "accessibilityState": { - "required": false, - "tsType": { - "name": "AccessibilityState" - }, - "description": "Accessibility state for the Touchable. This is read by the screen reader when the user taps the component." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Menu/MenuItem.tsx" - ], - "group": "Menu" - }, - "Modal": { - "filepath": "Modal.tsx", - "title": "Modal", - "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "modal", - "data": { - "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n const containerStyle = { backgroundColor: 'white', padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Modal", - "methods": [], - "statics": [], - "props": { - "dismissable": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking outside the modal dismisses it.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "dismissableBackButton": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking Android hardware back button dismisses the dialog.", - "defaultValue": { - "value": "dismissable", - "computed": true - } - }, - "onDismiss": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the user dismisses the modal.", - "defaultValue": { - "value": "() => {}", - "computed": false - } - }, - "overlayAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal.", - "defaultValue": { - "value": "'Close modal'", - "computed": false - } - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines Whether the modal is visible.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Modal`." - }, - "contentContainerStyle": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "Style for the content of the modal" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the wrapper of the modal.\nUse this prop to change the default wrapper style or to override safe area insets with marginTop and marginBottom." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'modal'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Modal.tsx" - ] - }, - "Portal/Portal": { - "filepath": "Portal/Portal.tsx", - "title": "Portal", - "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", - "link": "portal", - "data": { - "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Portal", - "methods": [], - "statics": [ - { - "name": "Host", - "docblock": null, - "type": { - "name": "static", - "raw": "static" - }, - "link": "portal-host.html" - } - ], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Portal`." - }, - "theme": { - "required": true, - "tsType": { - "name": "InternalTheme" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Portal/Portal.tsx" - ] - }, - "Portal/PortalHost": { - "filepath": "Portal/PortalHost.tsx", - "title": "Portal.Host", - "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", - "link": "portal-host", - "data": { - "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", - "displayName": "Portal.Host", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Portal/PortalHost.tsx" - ], - "group": "Portal" - }, - "ProgressBar": { - "filepath": "ProgressBar.tsx", - "title": "ProgressBar", - "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "progress-bar", - "data": { - "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, MD3Colors } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "ProgressBar", - "methods": [], - "statics": [], - "props": { - "animatedValue": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Animated value (between 0 and 1). This tells the progress bar to rely on this value to animate it.\nNote: It should not be used in parallel with the `progress` prop." - }, - "progress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Progress value (between 0 and 1).\nNote: It should not be used in parallel with the `animatedValue` prop.", - "defaultValue": { - "value": "0", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Color of the progress bar. The background color will be calculated based on this but you can change it by passing `backgroundColor` to `style` prop." - }, - "indeterminate": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "If the progress bar will show indeterminate progress." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show the ProgressBar (true, the default) or hide it (false).", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "fillStyle": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "Style of filled part of the ProgresBar." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'progress-bar'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ProgressBar.tsx" - ] - }, - "RadioButton/RadioButton": { - "filepath": "RadioButton/RadioButton.tsx", - "title": "RadioButton", - "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "radio-button", - "data": { - "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "RadioButton", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button" - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked radio." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButton.tsx" - ] - }, - "RadioButton/RadioButtonAndroid": { - "filepath": "RadioButton/RadioButtonAndroid.tsx", - "title": "RadioButton.Android", - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "radio-button-android", - "data": { - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "RadioButton.Android", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button" - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(param?: any) => void", - "signature": { - "arguments": [ - { - "name": "param", - "type": { - "name": "any" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked radio." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonAndroid.tsx" - ], - "group": "RadioButton" - }, - "RadioButton/RadioButtonGroup": { - "filepath": "RadioButton/RadioButtonGroup.tsx", - "title": "RadioButton.Group", - "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "radio-button-group", - "data": { - "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "RadioButton.Group", - "methods": [], - "statics": [], - "props": { - "onValueChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: string) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the currently selected radio button." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing radio buttons." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonGroup.tsx" - ], - "group": "RadioButton" - }, - "RadioButton/RadioButtonIOS": { - "filepath": "RadioButton/RadioButtonIOS.tsx", - "title": "RadioButton.IOS", - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "radio-button-ios", - "data": { - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "RadioButton.IOS", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button" - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonIOS.tsx" - ], - "group": "RadioButton" - }, - "RadioButton/RadioButtonItem": { - "filepath": "RadioButton/RadioButtonItem.tsx", - "title": "RadioButton.Item", - "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "radio-button-item", - "data": { - "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "RadioButton.Item", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button." - }, - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Label to be displayed on the item." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", - "defaultValue": { - "value": "label", - "computed": true - } - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked radio." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Additional styles for container View." - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Label element." - }, - "labelVariant": { - "required": false, - "tsType": { - "name": "unknown" - }, - "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyLarge'", - "computed": false - } - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'android' | 'ios'", - "elements": [ - { - "name": "literal", - "value": "'android'" - }, - { - "name": "literal", - "value": "'ios'" - } - ] - }, - "description": "Whether `` or `` should be used.\nLeft undefined `` will be used." - }, - "position": { - "required": false, - "tsType": { - "name": "union", - "raw": "'leading' | 'trailing'", - "elements": [ - { - "name": "literal", - "value": "'leading'" - }, - { - "name": "literal", - "value": "'trailing'" - } - ] - }, - "description": "Radio button control position.", - "defaultValue": { - "value": "'trailing'", - "computed": false - } - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonItem.tsx" - ], - "group": "RadioButton" - }, - "Searchbar": { - "filepath": "Searchbar.tsx", - "title": "Searchbar", - "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", - "link": "searchbar", - "data": { - "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", - "displayName": "Searchbar", - "methods": [], - "statics": [], - "props": { - "placeholder": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Hint text shown when the input is empty." - }, - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "The value of the text input." - }, - "onChangeText": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(query: string) => void", - "signature": { - "arguments": [ - { - "name": "query", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the text input's text changes." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'bar' | 'view'", - "elements": [ - { - "name": "literal", - "value": "'bar'" - }, - { - "name": "literal", - "value": "'view'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nSearch layout mode, the default value is \"bar\".", - "defaultValue": { - "value": "'bar'", - "computed": false - } - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon name for the left icon button (see `onIconPress`)." - }, - "iconColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon, default will be derived from theme" - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "onIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback to execute if we want the left icon to act as button." - }, - "onClearIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback to execute if we want to add custom behaviour to close icon button." - }, - "searchAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'search'", - "computed": false - } - }, - "clearIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Custom icon for clear button, default will be icon close. It's visible when `loading` is set to `false`.\nIn v5.x with theme version 3, `clearIcon` is visible only if `right` prop is not defined." - }, - "clearAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'clear'", - "computed": false - } - }, - "traileringIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@supported Available in v5.x with theme version 3\nIcon name for the right trailering icon button.\nWorks only when `mode` is set to \"bar\". It won't be displayed if `loading` is set to `true`." - }, - "traileringIconColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "@supported Available in v5.x with theme version 3\nCustom color for the right trailering icon, default will be derived from theme" - }, - "traileringRippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "@supported Available in v5.x with theme version 3\nColor of the trailering icon ripple effect." - }, - "onTraileringIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "@supported Available in v5.x with theme version 3\nCallback to execute on the right trailering icon button press." - }, - "traileringIconAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the right trailering icon button. This is read by the screen reader when the user taps the button." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n color: string;\n style: Style;\n testID: string;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n color: string;\n style: Style;\n testID: string;\n}", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": true - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "@supported Available in v5.x with theme version 3\nCallback which returns a React element to display on the right side.\nWorks only when `mode` is set to \"bar\"." - }, - "showDivider": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether to show `Divider` at the bottom of the search.\nWorks only when `mode` is set to \"view\". True by default.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "elevation": { - "required": false, - "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nChanges Searchbar shadow and background on iOS and Android.", - "defaultValue": { - "value": "0", - "computed": false - } - }, - "inputStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Set style of the TextInput component inside the searchbar" - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "loading": { - "required": false, - "tsType": { - "name": "Boolean" - }, - "description": "Custom flag for replacing clear button with activity indicator.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'search-bar'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Searchbar.tsx" - ] - }, - "SegmentedButtons/SegmentedButtons": { - "filepath": "SegmentedButtons/SegmentedButtons.tsx", - "title": "SegmentedButtons", - "description": "Segmented buttons can be used to select options, switch views or sort elements.
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", - "link": "segmented-buttons", - "data": { - "description": "Segmented buttons can be used to select options, switch views or sort elements.
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "SegmentedButtons", - "methods": [], - "statics": [], - "props": { - "buttons": { - "required": true, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "value", - "value": { - "name": "T", - "required": true - } - }, - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "disabled", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "accessibilityLabel", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "checkedColor", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "uncheckedColor", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(event: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "event", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "showSelectedCheck", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "style", - "value": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp", - "required": false - } - }, - { - "key": "labelStyle", - "value": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - } - ], - "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n accessibilityLabel?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}[]" - }, - "description": "Buttons to display as options in toggle button.\nButton should contain the following properties:\n- `value`: value of button (required)\n- `icon`: icon to display for the item\n- `disabled`: whether the button is disabled\n- `accessibilityLabel`: acccessibility label for the button. This is read by the screen reader when the user taps the button.\n- `checkedColor`: custom color for checked Text and Icon\n- `uncheckedColor`: custom color for unchecked Text and Icon\n- `onPress`: callback that is called when button is pressed\n- `label`: label text of the button\n- `showSelectedCheck`: show optional check icon to indicate selected state\n- `style`: pass additional styles for the button\n- `testID`: testID to be used on tests" - }, - "density": { - "required": false, - "tsType": { - "name": "union", - "raw": "'regular' | 'small' | 'medium' | 'high'", - "elements": [ - { - "name": "literal", - "value": "'regular'" - }, - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'high'" - } - ] - }, - "description": "Density is applied to the height, to allow usage in denser UIs" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/SegmentedButtons/SegmentedButtons.tsx" - ] - }, - "Snackbar": { - "filepath": "Snackbar.tsx", - "title": "Snackbar", - "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", - "link": "snackbar", - "data": { - "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "Snackbar", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether the Snackbar is currently visible." - }, - "action": { - "required": false, - "tsType": { - "name": "intersection", - "raw": "$RemoveChildren & {\n label: string;\n}", - "elements": [ - { - "name": "$RemoveChildren", - "elements": [ - { - "name": "Button" - } - ], - "raw": "$RemoveChildren" - }, - { - "name": "signature", - "type": "object", - "raw": "{\n label: string;\n}", - "signature": { - "properties": [ - { - "key": "label", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - ] - }, - "description": "Label and press callback for the action button. It should contain the following properties:\n- `label` - Label of the action button\n- `onPress` - Callback that is called when action button is pressed." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@supported Available in v5.x with theme version 3\nIcon to display when `onIconPress` is defined. Default will be `close` icon." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "@supported Available in v5.x with theme version 3\nColor of the ripple effect." - }, - "onIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "@supported Available in v5.x with theme version 3\nFunction to execute on icon button press. The icon button appears only when this prop is specified." - }, - "iconAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "@supported Available in v5.x with theme version 3\nAccessibility label for the icon button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'Close icon'", - "computed": false - } - }, - "duration": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The duration for which the Snackbar is shown.", - "defaultValue": { - "value": "7000", - "computed": false - } - }, - "onDismiss": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback called when Snackbar is dismissed. The `visible` prop needs to be updated when this is called." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text content of the Snackbar." - }, - "elevation": { - "required": false, - "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nChanges Snackbar shadow and background on iOS and Android.", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "wrapperStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the wrapper of the snackbar" - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the content of the snackbar" - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Snackbar.tsx" - ] - }, - "Surface": { - "filepath": "Surface.tsx", - "title": "Surface", - "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", - "link": "surface", - "data": { - "description": "Surface is a basic container that can give depth to an element with elevation shadow.\nOn dark theme with `adaptive` mode, surface is constructed by also placing a semi-transparent white overlay over a component surface.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\nOverlay and shadow can be applied by specifying the `elevation` property both on Android and iOS.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n padding: 8,\n height: 80,\n width: 80,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", - "displayName": "Surface", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Surface`." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "elevation": { - "required": false, - "tsType": { - "name": "union", - "raw": "0 | 1 | 2 | 3 | 4 | 5 | Animated.Value", - "elements": [ - { - "name": "literal", - "value": "0" - }, - { - "name": "literal", - "value": "1" - }, - { - "name": "literal", - "value": "2" - }, - { - "name": "literal", - "value": "3" - }, - { - "name": "literal", - "value": "4" - }, - { - "name": "literal", - "value": "5" - }, - { - "name": "Animated.Value" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nChanges shadows and background on iOS and Android.\nUsed to create UI hierarchy between components.\n\nNote: If `mode` is set to `flat`, Surface doesn't have a shadow.\n\nNote: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`.\nIt's no longer supported with theme version 3 and you should use `elevation` property instead.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'elevated'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'elevated'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nMode of the Surface.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.", - "defaultValue": { - "value": "'elevated'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'surface'", - "computed": false - } - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "container": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@internal" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Surface.tsx" - ] - }, - "Switch/Switch": { - "filepath": "Switch/Switch.tsx", - "title": "Switch", - "description": "Switch is a visual toggle between two mutually exclusive states — on and off.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [isSwitchOn, setIsSwitchOn] = React.useState(false);\n\n const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn);\n\n return ;\n};\n\nexport default MyComponent;\n```", - "link": "switch", - "data": { - "description": "Switch is a visual toggle between two mutually exclusive states — on and off.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [isSwitchOn, setIsSwitchOn] = React.useState(false);\n\n const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn);\n\n return ;\n};\n\nexport default MyComponent;\n```", - "displayName": "Switch", - "methods": [], - "statics": [], - "props": { - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Disable toggling the switch." - }, - "value": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Value of the switch, true means 'on', false means 'off'." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for switch." - }, - "onValueChange": { - "required": false, - "tsType": { - "name": "Function" - }, - "description": "Callback called with the new value when it changes." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Switch/Switch.tsx" - ] - }, - "TextInput/TextInput": { - "filepath": "TextInput/TextInput.tsx", - "title": "TextInput", - "description": "A component to allow users to input text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState(\"\");\n\n return (\n setText(text)}\n />\n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", - "link": "text-input", - "data": { - "description": "A component to allow users to input text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState(\"\");\n\n return (\n setText(text)}\n />\n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", - "displayName": "TextInput", - "methods": [], - "statics": [], - "props": { - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'outlined'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'outlined'" - } - ] - }, - "description": "Mode of the TextInput.\n- `flat` - flat input with an underline.\n- `outlined` - input with an outline.\n\nIn `outlined` mode, the background color of the label is derived from `colors?.background` in theme or the `backgroundColor` style.\nThis component render TextInputOutlined or TextInputFlat based on that props", - "defaultValue": { - "value": "'flat'", - "computed": false - } - }, - "left": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "The adornment placed on the left side of the input. It can be either `TextInput.Icon` or `TextInput.Affix`." - }, - "right": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "The adornment placed on the right side of the input. It can be either `TextInput.Icon` or `TextInput.Affix`." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "If true, user won't be able to interact with the component.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "label": { - "required": false, - "tsType": { - "name": "TextInputLabelProp" - }, - "description": "The text or component to use for the floating label." - }, - "placeholder": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Placeholder for the input." - }, - "error": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to style the TextInput with error style.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "onChangeText": { - "required": false, - "tsType": { - "name": "Function" - }, - "description": "Callback that is called when the text input's text changes. Changed text is passed as an argument to the callback handler." - }, - "selectionColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Selection color of the input. On iOS, it sets both the selection color and cursor color.\nOn Android, it sets only the selection color." - }, - "cursorColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "@platform Android only\nCursor (or \"caret\") color of the input on Android.\nThis property has no effect on iOS." - }, - "underlineColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Inactive underline color of the input." - }, - "activeUnderlineColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Active underline color of the input." - }, - "outlineColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Inactive outline color of the input." - }, - "activeOutlineColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Active outline color of the input." - }, - "textColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Color of the text in the input." - }, - "dense": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Sets min height with densed layout. For `TextInput` in `flat` mode\nheight is `64dp` or in dense layout - `52dp` with label or `40dp` without label.\nFor `TextInput` in `outlined` mode\nheight is `56dp` or in dense layout - `40dp` regardless of label.\nWhen you apply `height` prop in style the `dense` prop affects only `paddingVertical` inside `TextInput`", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "multiline": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the input can have multiple lines.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "numberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@platform Android only\nThe number of lines to show in the input (Android only)." - }, - "onFocus": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(args: any) => void", - "signature": { - "arguments": [ - { - "name": "args", - "type": { - "name": "any" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the text input is focused." - }, - "onBlur": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(args: any) => void", - "signature": { - "arguments": [ - { - "name": "args", - "type": { - "name": "any" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the text input is blurred." - }, - "render": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: RenderProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "RenderProps" - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback to render a custom input component such as `react-native-text-input-mask`\ninstead of the default `TextInput` component from `react-native`.\n\nExample:\n```js\n\n \n }\n/>\n```", - "defaultValue": { - "value": "(props: RenderProps) => ", - "computed": false - } - }, - "value": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Value of the text input." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Pass `fontSize` prop to modify the font size inside `TextInput`.\nPass `height` prop to set `TextInput` height. When `height` is passed,\n`dense` prop will affect only input's `paddingVertical`.\nPass `paddingHorizontal` to modify horizontal padding.\nThis can be used to get MD Guidelines v1 TextInput look." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Pass custom style directly to the input itself.\nOverrides input style\nExample: `paddingLeft`, `backgroundColor`" - }, - "outlineStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Pass style to override the default style of outlined wrapper.\nOverrides style when mode is set to `outlined`\nExample: `borderRadius`, `borderColor`" - }, - "underlineStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Pass style to override the default style of underlined wrapper.\nOverrides style when mode is set to `flat`\nExample: `borderRadius`, `borderColor`" - }, - "editable": { - "defaultValue": { - "value": "true", - "computed": false - }, - "required": false, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/TextInput/TextInput.tsx" - ] - }, - "TextInput/Adornment/TextInputAffix": { - "filepath": "TextInput/Adornment/TextInputAffix.tsx", - "title": "TextInput.Affix", - "description": "A component to render a leading / trailing text in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", - "link": "text-input-affix", - "data": { - "description": "A component to render a leading / trailing text in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", - "displayName": "TextInput.Affix", - "methods": [], - "statics": [], - "props": { - "text": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Text to show." - }, - "onLayout": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(event: LayoutChangeEvent) => void", - "signature": { - "arguments": [ - { - "name": "event", - "type": { - "name": "LayoutChangeEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "" - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the affix. This is read by the screen reader when the user taps the affix.", - "defaultValue": { - "value": "text", - "computed": true - } - }, - "textStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the Text element." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/TextInput/Adornment/TextInputAffix.tsx" - ], - "group": "TextInput" - }, - "TextInput/Adornment/TextInputIcon": { - "filepath": "TextInput/Adornment/TextInputIcon.tsx", - "title": "TextInput.Icon", - "description": "A component to render a leading / trailing icon in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", - "link": "text-input-icon", - "data": { - "description": "A component to render a leading / trailing icon in the TextInput\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n return (\n }\n />\n );\n};\n\nexport default MyComponent;\n```", - "displayName": "TextInput.Icon", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "@renamed Renamed from 'name' to 'icon` in v5.x\nIcon to show." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "forceTextInputFocus": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the TextInput will focus after onPress.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "union", - "raw": "((isTextInputFocused: boolean) => string | undefined) | string", - "elements": [ - { - "name": "unknown" - }, - { - "name": "string" - } - ] - }, - "description": "Color of the icon or a function receiving a boolean indicating whether the TextInput is focused and returning the color." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/TextInput/Adornment/TextInputIcon.tsx" - ], - "group": "TextInput" - }, - "ToggleButton/ToggleButton": { - "filepath": "ToggleButton/ToggleButton.tsx", - "title": "ToggleButton", - "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", - "link": "toggle-button", - "data": { - "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", - "displayName": "ToggleButton", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `ToggleButton`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the icon." - }, - "iconColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom text color for button." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled." - }, - "accessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value?: GestureResponderEvent | string) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "union", - "raw": "GestureResponderEvent | string", - "elements": [ - { - "name": "GestureResponderEvent" - }, - { - "name": "string" - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "value": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Value of button." - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of button." - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButton.tsx" - ] - }, - "ToggleButton/ToggleButtonGroup": { - "filepath": "ToggleButton/ToggleButtonGroup.tsx", - "title": "ToggleButton.Group", - "description": "Toggle group allows to control a group of toggle buttons.
    \nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "toggle-button-group", - "data": { - "description": "Toggle group allows to control a group of toggle buttons.
    \nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "ToggleButton.Group", - "methods": [], - "statics": [], - "props": { - "onValueChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: Value) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "Value" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "value": { - "required": true, - "tsType": { - "name": "union", - "raw": "Value | null", - "elements": [ - { - "name": "Value" - }, - { - "name": "null" - } - ] - }, - "description": "Value of the currently selected toggle button." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing toggle buttons." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButtonGroup.tsx" - ], - "group": "ToggleButton" - }, - "ToggleButton/ToggleButtonRow": { - "filepath": "ToggleButton/ToggleButtonRow.tsx", - "title": "ToggleButton.Row", - "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", - "link": "toggle-button-row", - "data": { - "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", - "displayName": "ToggleButton.Row", - "methods": [], - "statics": [], - "props": { - "onValueChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: string) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the currently selected toggle button." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing toggle buttons." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButtonRow.tsx" - ], - "group": "ToggleButton" - }, - "Tooltip/Tooltip": { - "filepath": "Tooltip/Tooltip.tsx", - "title": "Tooltip", - "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "link": "tooltip", - "data": { - "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Tooltip", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactElement", - "raw": "React.ReactElement" - }, - "description": "Tooltip reference element. Needs to be able to hold a ref." - }, - "enterTouchDelay": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before showing the tooltip.", - "defaultValue": { - "value": "500", - "computed": false - } - }, - "leaveTouchDelay": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds after the user stops touching an element before hiding the tooltip.", - "defaultValue": { - "value": "1500", - "computed": false - } - }, - "title": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Tooltip title" - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Tooltip/Tooltip.tsx" - ] - }, - "TouchableRipple/TouchableRipple": { - "filepath": "TouchableRipple/TouchableRipple.tsx", - "title": "TouchableRipple", - "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n \n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", - "link": "touchable-ripple", - "data": { - "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n \n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", - "displayName": "TouchableRipple", - "methods": [], - "statics": [], - "props": { - "borderless": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to render the ripple outside the view bounds.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "background": { - "required": false, - "tsType": { - "name": "Object" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "centered": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to start the ripple at the center (Web)." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to prevent interaction with the touchable." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press. If not set, will cause the touchable to be disabled." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute immediately when a touch is engaged, before `onPressOut` and `onPress`." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute when a touch is released." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect (Android >= 5.0 and Web)." - }, - "underlayColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Color of the underlay for the highlight effect (Android < 5.0 and iOS)." - }, - "children": { - "required": true, - "tsType": { - "name": "union", - "raw": "| ((state: PressableStateCallbackType) => React.ReactNode)\n| React.ReactNode", - "elements": [ - { - "name": "unknown" - }, - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - ] - }, - "description": "Content of the `TouchableRipple`." - }, - "style": { - "required": false, - "tsType": { - "name": "union", - "raw": "| StyleProp\n| ((state: PressableStateCallbackType) => StyleProp)\n| undefined", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - { - "name": "unknown" - }, - { - "name": "undefined" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/TouchableRipple/TouchableRipple.tsx" - ] - }, - "Typography/Text": { - "filepath": "Typography/Text.tsx", - "title": "Text", - "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", - "link": "text", - "data": { - "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", - "displayName": "Text", - "methods": [], - "statics": [], - "props": { - "variant": { - "required": false, - "tsType": { - "name": "VariantProp", - "elements": [ - { - "name": "T" - } - ], - "raw": "VariantProp" - }, - "description": "@supported Available in v5.x with theme version 3\n\nVariant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`" - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Typography/Text.tsx" - ] - } - } -} diff --git a/docs/src/data/componentDocs6x.json b/docs/src/data/componentDocs6x.json deleted file mode 100644 index b223c9c0a8..0000000000 --- a/docs/src/data/componentDocs6x.json +++ /dev/null @@ -1,12420 +0,0 @@ -{ - "docs": { - "ActivityIndicator": { - "filepath": "ActivityIndicator.tsx", - "title": "ActivityIndicator", - "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "activity-indicator", - "data": { - "description": "Activity indicator is used to present progress of some activity in the app.\nIt can be used as a drop-in replacement for the ActivityIndicator shipped with React Native.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ActivityIndicator, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "ActivityIndicator", - "methods": [], - "statics": [], - "props": { - "animating": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show the indicator or hide it.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "The color of the spinner." - }, - "size": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'large' | number", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "number" - } - ] - }, - "description": "Size of the indicator.", - "defaultValue": { - "value": "'small'", - "computed": false - } - }, - "hidesWhenStopped": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the indicator should hide when not animating.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ActivityIndicator.tsx" - ] - }, - "Appbar/Appbar": { - "filepath": "Appbar/Appbar.tsx", - "title": "Appbar", - "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", - "link": "appbar", - "data": { - "description": "A component to display action items in a bar. It can be placed at the top or bottom.\nThe top bar usually contains the screen title, controls such as navigation buttons, menu button etc.\nThe bottom bar usually provides access to a drawer and up to four actions.\n\nBy default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations\n\n## Usage\n### Top bar\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```\n\n### Bottom bar\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Appbar, FAB, useTheme } from 'react-native-paper';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\n\nconst BOTTOM_APPBAR_HEIGHT = 80;\nconst MEDIUM_FAB_HEIGHT = 56;\n\nconst MyComponent = () => {\n const { bottom } = useSafeAreaInsets();\n const theme = useTheme();\n\n return (\n \n {}} />\n {}} />\n {}} />\n {}} />\n {}}\n style={[\n styles.fab,\n { top: (BOTTOM_APPBAR_HEIGHT - MEDIUM_FAB_HEIGHT) / 2 },\n ]}\n />\n \n );\n};\n\nconst styles = StyleSheet.create({\n bottom: {\n backgroundColor: 'aquamarine',\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n },\n fab: {\n position: 'absolute',\n right: 16,\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "Appbar", - "methods": [], - "statics": [], - "props": { - "dark": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the background color is a dark color. A dark appbar will render light text and vice-versa." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Appbar`." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'medium' | 'large' | 'center-aligned'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "literal", - "value": "'center-aligned'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", - "defaultValue": { - "value": "'small'", - "computed": false - } - }, - "elevated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "safeAreaInsets": { - "required": false, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n bottom?: number;\n top?: number;\n left?: number;\n right?: number;\n}", - "signature": { - "properties": [ - { - "key": "bottom", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "top", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "left", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "right", - "value": { - "name": "number", - "required": false - } - } - ] - } - }, - "description": "Safe area insets for the Appbar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "Omit", - "elements": [ - { - "name": "ViewStyle" - }, - { - "name": "literal", - "value": "'elevation'" - } - ], - "raw": "Omit" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/Appbar.tsx" - ] - }, - "Appbar/AppbarAction": { - "filepath": "Appbar/AppbarAction.tsx", - "title": "Appbar.Action", - "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", - "link": "appbar-action", - "data": { - "description": "A component used to display an action item in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\nimport { Platform } from 'react-native';\n\nconst MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical';\n\nconst MyComponent = () => (\n \n \n {}} />\n {}} />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Appbar.Action", - "methods": [], - "statics": [], - "props": { - "color": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom color for action icon." - }, - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Name of the icon to show." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Optional icon size.", - "defaultValue": { - "value": "24", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "isLeading": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n\nWhether it's the leading button. Note: If `Appbar.BackAction` is present, it will be rendered before any `isLeading` icons." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "AnimatedStyle" - } - ], - "raw": "StyleProp>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarAction.tsx" - ], - "group": "Appbar" - }, - "Appbar/AppbarBackAction": { - "filepath": "Appbar/AppbarBackAction.tsx", - "title": "Appbar.BackAction", - "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "link": "appbar-back-action", - "data": { - "description": "A component used to display a back button in the appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Appbar.BackAction", - "methods": [], - "statics": [], - "props": { - "color": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom color for back icon." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Optional icon size." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'Back'", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "AnimatedStyle" - } - ], - "raw": "StyleProp>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarBackAction.tsx" - ], - "group": "Appbar" - }, - "Appbar/AppbarContent": { - "filepath": "Appbar/AppbarContent.tsx", - "title": "Appbar.Content", - "description": "A component used to display a title in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "appbar-content", - "data": { - "description": "A component used to display a title in an appbar.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Appbar.Content", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text or component for the title." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the title, if `title` is a string." - }, - "titleRef": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "TextRef" - } - ] - }, - "description": "Reference for the title." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "If true, disable all interactions for this component." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the text." - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'medium' | 'large' | 'center-aligned'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "literal", - "value": "'center-aligned'" - } - ] - }, - "description": "@internal", - "defaultValue": { - "value": "'small'", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'appbar-content'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarContent.tsx" - ], - "group": "Appbar" - }, - "Appbar/AppbarHeader": { - "filepath": "Appbar/AppbarHeader.tsx", - "title": "Appbar.Header", - "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "appbar-header", - "data": { - "description": "A component to use as a header at the top of the screen.\nIt can contain the screen title, controls such as navigation buttons, menu button etc.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Appbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const _goBack = () => console.log('Went back');\n\n const _handleSearch = () => console.log('Searching');\n\n const _handleMore = () => console.log('Shown more');\n\n return (\n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Appbar.Header", - "methods": [], - "statics": [], - "props": { - "dark": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the background color is a dark color. A dark header will render light text and vice-versa." - }, - "statusBarHeight": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Extra padding to add at the top of header to account for translucent status bar.\nThis is automatically handled on iOS >= 11 including iPhone X using `SafeAreaView`.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to disable the default behaviour, and customize the height." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the header." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'small' | 'medium' | 'large' | 'center-aligned'", - "elements": [ - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'large'" - }, - { - "name": "literal", - "value": "'center-aligned'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\n\nMode of the Appbar.\n- `small` - Appbar with default height (64).\n- `medium` - Appbar with medium height (112).\n- `large` - Appbar with large height (152).\n- `center-aligned` - Appbar with default height and center-aligned title.", - "defaultValue": { - "value": "Platform.OS === 'ios' ? 'center-aligned' : 'small'", - "computed": false - } - }, - "elevated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether Appbar background should have the elevation along with primary color pigment.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AppbarStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "testID": { - "defaultValue": { - "value": "'appbar-header'", - "computed": false - }, - "required": false, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Appbar/AppbarHeader.tsx" - ], - "group": "Appbar" - }, - "Avatar/AvatarIcon": { - "filepath": "Avatar/AvatarIcon.tsx", - "title": "Avatar.Icon", - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "link": "avatar-icon", - "data": { - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "displayName": "Avatar.Icon", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Avatar`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the avatar.", - "defaultValue": { - "value": "64", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the icon." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarIcon.tsx" - ], - "group": "Avatar" - }, - "Avatar/AvatarImage": { - "filepath": "Avatar/AvatarImage.tsx", - "title": "Avatar.Image", - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", - "link": "avatar-image", - "data": { - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\nexport default MyComponent\n```", - "displayName": "Avatar.Image", - "methods": [], - "statics": [], - "props": { - "source": { - "required": true, - "tsType": { - "name": "union", - "raw": "| ImageSourcePropType\n| ((props: { size: number }) => React.ReactNode)", - "elements": [ - { - "name": "ImageSourcePropType" - }, - { - "name": "unknown" - } - ] - }, - "description": "Image to display for the `Avatar`.\nIt accepts a standard React Native Image `source` prop\nOr a function that returns an `Image`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the avatar.", - "defaultValue": { - "value": "64", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "onError": { - "required": false, - "tsType": { - "name": "ImageProps['onError']", - "raw": "ImageProps['onError']" - }, - "description": "Invoked on load error." - }, - "onLayout": { - "required": false, - "tsType": { - "name": "ImageProps['onLayout']", - "raw": "ImageProps['onLayout']" - }, - "description": "Invoked on mount and on layout changes." - }, - "onLoad": { - "required": false, - "tsType": { - "name": "ImageProps['onLoad']", - "raw": "ImageProps['onLoad']" - }, - "description": "Invoked when load completes successfully." - }, - "onLoadEnd": { - "required": false, - "tsType": { - "name": "ImageProps['onLoadEnd']", - "raw": "ImageProps['onLoadEnd']" - }, - "description": "Invoked when load either succeeds or fails." - }, - "onLoadStart": { - "required": false, - "tsType": { - "name": "ImageProps['onLoadStart']", - "raw": "ImageProps['onLoadStart']" - }, - "description": "Invoked on load start." - }, - "onProgress": { - "required": false, - "tsType": { - "name": "ImageProps['onProgress']", - "raw": "ImageProps['onProgress']" - }, - "description": "Invoked on download progress." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarImage.tsx" - ], - "group": "Avatar" - }, - "Avatar/AvatarText": { - "filepath": "Avatar/AvatarText.tsx", - "title": "Avatar.Text", - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "link": "avatar-text", - "data": { - "description": "Avatars can be used to represent people in a graphical way.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n```", - "displayName": "Avatar.Text", - "methods": [], - "statics": [], - "props": { - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Initials to show as the text in the `Avatar`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the avatar.", - "defaultValue": { - "value": "64", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for the text." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for text container" - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the title." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Avatar/AvatarText.tsx" - ], - "group": "Avatar" - }, - "Badge": { - "filepath": "Badge.tsx", - "title": "Badge", - "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\nThe badge is styled differently based on whether `children` is passed:\n- Small dot when it doesn't have `children`\n- Larger pill when it has `children`\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", - "link": "badge", - "data": { - "description": "Badges are small status descriptors for UI elements.\nA badge consists of a small circle, typically containing a number or other short set of characters, that appears in proximity to another object.\n\nThe badge is styled differently based on whether `children` is passed:\n- Small dot when it doesn't have `children`\n- Larger pill when it has `children`\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Badge } from 'react-native-paper';\n\nconst MyComponent = () => (\n 3\n);\n\nexport default MyComponent;\n```", - "displayName": "Badge", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the badge is visible", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "children": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - }, - "description": "Content of the `Badge`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "AnimatedStyle" - } - ], - "raw": "StyleProp>" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Badge.tsx" - ] - }, - "Banner": { - "filepath": "Banner.tsx", - "title": "Banner", - "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "banner", - "data": { - "description": "Banner displays a prominent message and related actions.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Image } from 'react-native';\nimport { Banner } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(true);\n\n return (\n setVisible(false),\n },\n {\n label: 'Learn more',\n onPress: () => setVisible(false),\n },\n ]}\n icon={({size}) => (\n \n )}>\n There was a problem processing a transaction on your credit card.\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Banner", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether banner is currently visible." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content that will be displayed inside banner." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Banner`. Can be an image." - }, - "actions": { - "required": false, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "intersection", - "raw": "{\n label: string;\n} & $RemoveChildren", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n label: string;\n}", - "signature": { - "properties": [ - { - "key": "label", - "value": { - "name": "string", - "required": true - } - } - ] - } - }, - { - "name": "$RemoveChildren", - "elements": [ - { - "name": "Button" - } - ], - "raw": "$RemoveChildren" - } - ] - } - ], - "raw": "Array<\n {\n label: string;\n } & $RemoveChildren\n>" - }, - "description": "Action items to shown in the banner.\nAn action item should contain the following properties:\n\n- `label`: label of the action button (required)\n- `onPress`: callback that is called when button is pressed (required)\n\nTo customize button you can pass other props that button component takes.", - "defaultValue": { - "value": "[]", - "computed": false - } - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of banner's inner content.\nUse this prop to apply custom width for wide layouts." - }, - "elevation": { - "required": false, - "tsType": { - "name": "Elevation" - }, - "description": "@supported Available in v5.x with theme version 3\nChanges Banner shadow and background on iOS and Android.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "onShowAnimationFinished": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(result: { finished: boolean }) => void", - "signature": { - "arguments": [ - { - "name": "result", - "type": { - "name": "signature", - "type": "object", - "raw": "{ finished: boolean }", - "signature": { - "properties": [ - { - "key": "finished", - "value": { - "name": "boolean", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "\nOptional callback that will be called after the opening animation finished running normally", - "defaultValue": { - "value": "() => {}", - "computed": false - } - }, - "onHideAnimationFinished": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(result: { finished: boolean }) => void", - "signature": { - "arguments": [ - { - "name": "result", - "type": { - "name": "signature", - "type": "object", - "raw": "{ finished: boolean }", - "signature": { - "properties": [ - { - "key": "finished", - "value": { - "name": "boolean", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "\nOptional callback that will be called after the closing animation finished running normally", - "defaultValue": { - "value": "() => {}", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Banner.tsx" - ] - }, - "BottomNavigation/BottomNavigation": { - "filepath": "BottomNavigation/BottomNavigation.tsx", - "title": "BottomNavigation", - "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "bottom-navigation", - "data": { - "description": "BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar.\nIt is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar).\n\nBy default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead.\nSee [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { BottomNavigation, Text } from 'react-native-paper';\n\nconst MusicRoute = () => Music;\n\nconst AlbumsRoute = () => Albums;\n\nconst RecentsRoute = () => Recents;\n\nconst NotificationsRoute = () => Notifications;\n\nconst MyComponent = () => {\n const [index, setIndex] = React.useState(0);\n const [routes] = React.useState([\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]);\n\n const renderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n recents: RecentsRoute,\n notifications: NotificationsRoute,\n });\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "BottomNavigation", - "methods": [ - { - "name": "SceneMap", - "docblock": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance.", - "modifiers": [ - "static" - ], - "params": [ - { - "name": "scenes", - "optional": false, - "type": { - "name": "signature", - "type": "object", - "raw": "{\n [key: string]: React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n }>;\n}", - "signature": { - "properties": [ - { - "key": { - "name": "string" - }, - "value": { - "name": "ReactComponentType", - "raw": "React.ComponentType<{\n route: Route;\n jumpTo: (key: string) => void;\n}>", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "jumpTo", - "value": { - "name": "signature", - "type": "function", - "raw": "(key: string) => void", - "signature": { - "arguments": [ - { - "name": "key", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - } - ] - } - } - ], - "required": true - } - } - ] - } - } - } - ], - "returns": null, - "description": "Function which takes a map of route keys to components.\nPure components are used to minimize re-rendering of the pages.\nThis drastically improves the animation performance." - } - ], - "statics": [], - "props": { - "shifting": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." - }, - "labeled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether tabs should be spread across the entire width." - }, - "navigationState": { - "required": true, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n index: number;\n routes: Route[];\n}", - "signature": { - "properties": [ - { - "key": "index", - "value": { - "name": "number", - "required": true - } - }, - { - "key": "routes", - "value": { - "name": "Array", - "elements": [ - { - "name": "Route" - } - ], - "raw": "Route[]", - "required": true - } - } - ] - } - }, - "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `aria-label`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation` is a controlled component, which means the `index` needs to be updated via the `onIndexChange` callback." - }, - "onIndexChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(index: number) => void", - "signature": { - "arguments": [ - { - "name": "index", - "type": { - "name": "number" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback which is called on tab change, receives the index of the new tab as argument.\nThe navigation state needs to be updated when it's called, otherwise the change is dropped." - }, - "renderScene": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n jumpTo: (key: string) => void;\n}) => React.ReactNode | null", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n jumpTo: (key: string) => void;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "jumpTo", - "value": { - "name": "signature", - "type": "function", - "raw": "(key: string) => void", - "signature": { - "arguments": [ - { - "name": "key", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "React.ReactNode | null", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "null" - } - ] - } - } - }, - "description": "Callback which returns a react element to render as the page for the tab. Receives an object containing the route as the argument:\n\n```js\nrenderScene = ({ route, jumpTo }) => {\n switch (route.key) {\n case 'music':\n return ;\n case 'albums':\n return ;\n }\n}\n```\n\nPages are lazily rendered, which means that a page will be rendered the first time you navigate to it.\nAfter initial render, all the pages stay rendered to preserve their state.\n\nYou need to make sure that your individual routes implement a `shouldComponentUpdate` to improve the performance.\nTo make it easier to specify the components, you can use the `SceneMap` helper:\n\n```js\nrenderScene = BottomNavigation.SceneMap({\n music: MusicRoute,\n albums: AlbumsRoute,\n});\n```\n\nSpecifying the components this way is easier and takes care of implementing a `shouldComponentUpdate` method.\nEach component will receive the current route and a `jumpTo` method as it's props.\nThe `jumpTo` method can be used to navigate to other tabs programmatically:\n\n```js\nthis.props.jumpTo('albums')\n```" - }, - "renderIcon": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React Element to be used as tab icon." - }, - "renderLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which React Element to be used as tab label." - }, - "renderTouchable": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: TouchableProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "elements": [ - { - "name": "TouchableRippleProps" - }, - { - "name": "signature", - "type": "object", - "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "key", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "children", - "value": { - "name": "ReactReactNode", - "raw": "React.ReactNode", - "required": true - } - }, - { - "key": "borderless", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "centered", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "rippleColor", - "value": { - "name": "ColorValue", - "required": false - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS." - }, - "getAccessibilityLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route['aria-label']` by default." - }, - "getBadge": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => boolean | number | string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "boolean | number | string | undefined", - "elements": [ - { - "name": "boolean" - }, - { - "name": "number" - }, - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get badge for the tab, uses `route.badge` by default." - }, - "getLabelText": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component." - }, - "getLazy": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => boolean | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "boolean | undefined", - "elements": [ - { - "name": "boolean" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get lazy for the current screen. Uses true by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.lazy", - "computed": false - } - }, - "getTestID": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get the id to locate this tab button in tests, uses `route.testID` by default." - }, - "onTabPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab press. It receives the route for the pressed tab, useful for things like scroll to top." - }, - "onTabLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab long press. It receives the route for the pressed tab, useful for things like custom action when longed pressed." - }, - "activeColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the active tab." - }, - "inactiveColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the inactive tab." - }, - "sceneAnimationEnabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether animation is enabled for scenes transitions in `shifting` mode.\nBy default, the scenes cross-fade during tab change when `shifting` is enabled.\nSpecify `sceneAnimationEnabled` as `false` to disable the animation.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "sceneAnimationType": { - "required": false, - "tsType": { - "name": "union", - "raw": "'opacity' | 'shifting'", - "elements": [ - { - "name": "literal", - "value": "'opacity'" - }, - { - "name": "literal", - "value": "'shifting'" - } - ] - }, - "description": "The scene animation effect. Specify `'shifting'` for a different effect.\nBy default, 'opacity' will be used.", - "defaultValue": { - "value": "'opacity'", - "computed": false - } - }, - "sceneAnimationEasing": { - "required": false, - "tsType": { - "name": "union", - "raw": "EasingFunction | undefined", - "elements": [ - { - "name": "EasingFunction" - }, - { - "name": "undefined" - } - ] - }, - "description": "The scene animation Easing." - }, - "keyboardHidesNavigationBar": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", - "defaultValue": { - "value": "Platform.OS === 'android'", - "computed": false - } - }, - "safeAreaInsets": { - "required": false, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", - "signature": { - "properties": [ - { - "key": "top", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "right", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "bottom", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "left", - "value": { - "name": "number", - "required": false - } - } - ] - } - }, - "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." - }, - "barStyle": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "Style for the bottom navigation bar. You can pass a custom background color here:\n\n```js\nbarStyle={{ backgroundColor: '#694fad' }}\n```" - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "activeIndicatorStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'bottom-navigation'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/BottomNavigation/BottomNavigation.tsx" - ] - }, - "BottomNavigation/BottomNavigationBar": { - "filepath": "BottomNavigation/BottomNavigationBar.tsx", - "title": "BottomNavigation.Bar", - "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", - "link": "bottom-navigation-bar", - "data": { - "description": "A navigation bar which can easily be integrated with [React Navigation's Bottom Tabs Navigator](https://reactnavigation.org/docs/bottom-tab-navigator/).\n\n## Usage\n### without React Navigation\n```js\nimport React from 'react';\nimport { useState } from 'react';\nimport { View } from 'react-native';\nimport { BottomNavigation, Text, Provider } from 'react-native-paper';\nimport MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';\n\nfunction HomeScreen() {\n return (\n \n Home!\n \n );\n}\n\nfunction SettingsScreen() {\n return (\n \n Settings!\n \n );\n}\n\nexport default function MyComponent() {\n const [index, setIndex] = useState(0);\n\n const routes = [\n { key: 'home', title: 'Home', icon: 'home' },\n { key: 'settings', title: 'Settings', icon: 'cog' },\n ];\n\n const renderScene = ({ route }) => {\n switch (route.key) {\n case 'home':\n return ;\n case 'settings':\n return ;\n default:\n return null;\n }\n };\n\n return (\n \n {renderScene({ route: routes[index] })}\n {\n const newIndex = routes.findIndex((r) => r.key === route.key);\n if (newIndex !== -1) {\n setIndex(newIndex);\n }\n }}\n renderIcon={({ route, color }) => (\n \n )}\n getLabelText={({ route }) => route.title}\n />\n \n );\n}\n```", - "displayName": "BottomNavigation.Bar", - "methods": [], - "statics": [], - "props": { - "shifting": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the shifting style is used, the active tab icon shifts up to show the label and the inactive tabs won't have a label.\n\nBy default, this is `false` with theme version 3 and `true` when you have more than 3 tabs.\nPass `shifting={false}` to explicitly disable this animation, or `shifting={true}` to always use this animation.\nNote that you need at least 2 tabs be able to run this animation." - }, - "labeled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show labels in tabs. When `false`, only icons will be displayed.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether tabs should be spread across the entire width." - }, - "navigationState": { - "required": true, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n index: number;\n routes: Route[];\n}", - "signature": { - "properties": [ - { - "key": "index", - "value": { - "name": "number", - "required": true - } - }, - { - "key": "routes", - "value": { - "name": "Array", - "elements": [ - { - "name": "Route" - } - ], - "raw": "Route[]", - "required": true - } - } - ] - } - }, - "description": "State for the bottom navigation. The state should contain the following properties:\n\n- `index`: a number representing the index of the active route in the `routes` array\n- `routes`: an array containing a list of route objects used for rendering the tabs\n\nEach route object should contain the following properties:\n\n- `key`: a unique key to identify the route (required)\n- `title`: title of the route to use as the tab label\n- `focusedIcon`: icon to use as the focused tab icon, can be a string, an image source or a react component @renamed Renamed from 'icon' to 'focusedIcon' in v5.x\n- `unfocusedIcon`: icon to use as the unfocused tab icon, can be a string, an image source or a react component @supported Available in v5.x with theme version 3\n- `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.\n- `aria-label`: accessibility label for the tab button\n- `testID`: test id for the tab button\n\nExample:\n\n```js\n{\n index: 1,\n routes: [\n { key: 'music', title: 'Favorites', focusedIcon: 'heart', unfocusedIcon: 'heart-outline'},\n { key: 'albums', title: 'Albums', focusedIcon: 'album' },\n { key: 'recents', title: 'Recents', focusedIcon: 'history' },\n { key: 'notifications', title: 'Notifications', focusedIcon: 'bell', unfocusedIcon: 'bell-outline' },\n ]\n}\n```\n\n`BottomNavigation.Bar` is a controlled component, which means the `index` needs to be updated via the `onTabPress` callback." - }, - "renderIcon": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React Element to be used as tab icon." - }, - "renderLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n route: Route;\n focused: boolean;\n color: ColorValue;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n route: Route;\n focused: boolean;\n color: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "focused", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which React Element to be used as tab label." - }, - "renderTouchable": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: TouchableProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "TouchableRippleProps & {\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "elements": [ - { - "name": "TouchableRippleProps" - }, - { - "name": "signature", - "type": "object", - "raw": "{\n key: string;\n route: Route;\n children: React.ReactNode;\n borderless?: boolean;\n centered?: boolean;\n rippleColor?: ColorValue;\n}", - "signature": { - "properties": [ - { - "key": "key", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - }, - { - "key": "children", - "value": { - "name": "ReactReactNode", - "raw": "React.ReactNode", - "required": true - } - }, - { - "key": "borderless", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "centered", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "rippleColor", - "value": { - "name": "ColorValue", - "required": false - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to be used as the touchable for the tab item.\nRenders a `TouchableRipple` on Android and `Pressable` on iOS.", - "defaultValue": { - "value": "({ key, ...props }: TouchableProps) => (\n \n)", - "computed": false - } - }, - "getAccessibilityLabel": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get accessibility label for the tab button. This is read by the screen reader when the user taps the tab.\nUses `route['aria-label']` by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route['aria-label']", - "computed": false - } - }, - "getBadge": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => boolean | number | string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "boolean | number | string | undefined", - "elements": [ - { - "name": "boolean" - }, - { - "name": "number" - }, - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get badge for the tab, uses `route.badge` by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.badge", - "computed": false - } - }, - "getLabelText": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get label text for the tab, uses `route.title` by default. Use `renderLabel` to replace label component.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.title", - "computed": false - } - }, - "getTestID": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route }) => string | undefined", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "union", - "raw": "string | undefined", - "elements": [ - { - "name": "string" - }, - { - "name": "undefined" - } - ] - } - } - }, - "description": "Get the id to locate this tab button in tests, uses `route.testID` by default.", - "defaultValue": { - "value": "({ route }: { route: Route }) => route.testID", - "computed": false - } - }, - "onTabPress": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab press. It receives the route for the pressed tab. Use this to update the navigation state." - }, - "onTabLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { route: Route } & TabPressEvent) => void", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "{ route: Route } & TabPressEvent", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{ route: Route }", - "signature": { - "properties": [ - { - "key": "route", - "value": { - "name": "Route", - "required": true - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n defaultPrevented: boolean;\n preventDefault(): void;\n}", - "signature": { - "properties": [ - { - "key": "defaultPrevented", - "value": { - "name": "boolean", - "required": true - } - }, - { - "key": "preventDefault", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on tab long press. It receives the route for the pressed tab" - }, - "activeColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the active tab." - }, - "inactiveColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for icon and label in the inactive tab." - }, - "animationEasing": { - "required": false, - "tsType": { - "name": "union", - "raw": "EasingFunction | undefined", - "elements": [ - { - "name": "EasingFunction" - }, - { - "name": "undefined" - } - ] - }, - "description": "The scene animation Easing." - }, - "keyboardHidesNavigationBar": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the bottom navigation bar is hidden when keyboard is shown.\nOn Android, this works best when [`windowSoftInputMode`](https://developer.android.com/guide/topics/manifest/activity-element#wsoft) is set to `adjustResize`.", - "defaultValue": { - "value": "Platform.OS === 'android'", - "computed": false - } - }, - "safeAreaInsets": { - "required": false, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n top?: number;\n right?: number;\n bottom?: number;\n left?: number;\n}", - "signature": { - "properties": [ - { - "key": "top", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "right", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "bottom", - "value": { - "name": "number", - "required": false - } - }, - { - "key": "left", - "value": { - "name": "number", - "required": false - } - } - ] - } - }, - "description": "Safe area insets for the tab bar. This can be used to avoid elements like the navigation bar on Android and bottom safe area on iOS.\nThe bottom insets for iOS is added by default. You can override the behavior with this option." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "" - }, - "activeIndicatorStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'bottom-navigation-bar'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/BottomNavigation/BottomNavigationBar.tsx" - ], - "group": "BottomNavigation" - }, - "Button/Button": { - "filepath": "Button/Button.tsx", - "title": "Button", - "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "button", - "data": { - "description": "A button is component that the user can press to trigger an action.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Button", - "methods": [], - "statics": [], - "props": { - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'text' | 'outlined' | 'contained' | 'elevated' | 'contained-tonal'", - "elements": [ - { - "name": "literal", - "value": "'text'" - }, - { - "name": "literal", - "value": "'outlined'" - }, - { - "name": "literal", - "value": "'contained'" - }, - { - "name": "literal", - "value": "'elevated'" - }, - { - "name": "literal", - "value": "'contained-tonal'" - } - ] - }, - "description": "Mode of the button. You can change the mode to adjust the styling to give it desired emphasis.\n- `text` - flat button without background or outline, used for the lowest priority actions, especially when presenting multiple options.\n- `outlined` - button with an outline without background, typically used for important, but not primary action – represents medium emphasis.\n- `contained` - button with a background color, used for important action, have the most visual impact and high emphasis.\n- `elevated` - button with a background color and elevation, used when absolutely necessary e.g. button requires visual separation from a patterned background. @supported Available in v5.x with theme version 3\n- `contained-tonal` - button with a secondary background color, an alternative middle ground between contained and outlined buttons. @supported Available in v5.x with theme version 3", - "defaultValue": { - "value": "'text'", - "computed": false - } - }, - "dark": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the color is a dark color. A dark button will render light text and vice-versa. Only applicable for:\n * `contained` mode for theme version 2\n * `contained`, `contained-tonal` and `elevated` modes for theme version 3." - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Use a compact look, useful for `text` buttons in a row." - }, - "buttonColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom button's background color." - }, - "textColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom button's text color." - }, - "loading": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show a loading indicator." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Button`." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Label text of the button." - }, - "uppercase": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Make the label text uppercased. Note that this won't work if you pass React elements as children." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "accessibilityHint": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility hint for the button. This is read by the screen reader when the user taps the button." - }, - "role": { - "required": false, - "tsType": { - "name": "Role" - }, - "description": "Accessibility role for the button. The \"button\" role is set by default.", - "defaultValue": { - "value": "'button'", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touch is released even before onPress." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of button's inner content.\nUse this prop to apply custom height and width, to set a custom padding or to set the icon on the right with `flexDirection: 'row-reverse'`." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the button text." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "touchableRef": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "Reference for the touchable" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'button'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Button/Button.tsx" - ] - }, - "Card/Card": { - "filepath": "Card/Card.tsx", - "title": "Card", - "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "card", - "data": { - "description": "A card is a sheet of material that serves as an entry point to more detailed information.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Button, Card, Text } from 'react-native-paper';\n\nconst LeftContent = props => \n\nconst MyComponent = () => (\n \n \n \n Card title\n Card content\n \n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Card", - "methods": [], - "statics": [], - "props": { - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'elevated' | 'outlined' | 'contained'", - "elements": [ - { - "name": "literal", - "value": "'elevated'" - }, - { - "name": "literal", - "value": "'outlined'" - }, - { - "name": "literal", - "value": "'contained'" - } - ] - }, - "description": "Mode of the Card.\n- `elevated` - Card with elevation.\n- `contained` - Card without outline and elevation @supported Available in v5.x with theme version 3\n- `outlined` - Card with an outline.", - "defaultValue": { - "value": "'elevated'", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Card`." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touch is released even before onPress." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "If true, disable all interactions for this component." - }, - "elevation": { - "required": false, - "tsType": { - "name": "Elevation" - }, - "description": "Changes Card shadow and background on iOS and Android.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of card's inner content." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Pass down testID from card props to touchable", - "defaultValue": { - "value": "'card'", - "computed": false - } - }, - "accessible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Pass down accessible from card props to touchable" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "Reference to the card container." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/Card.tsx" - ] - }, - "Card/CardActions": { - "filepath": "Card/CardActions.tsx", - "title": "Card.Actions", - "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "card-actions", - "data": { - "description": "A component to show a list of actions inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Button } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Card.Actions", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Items inside the `CardActions`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardActions.tsx" - ], - "group": "Card" - }, - "Card/CardContent": { - "filepath": "Card/CardContent.tsx", - "title": "Card.Content", - "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", - "link": "card-content", - "data": { - "description": "A component to show content inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n Card title\n Card content\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Card.Content", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Items inside the `Card.Content`." - }, - "index": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "total": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "siblings": { - "required": false, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "union", - "raw": "string | null", - "elements": [ - { - "name": "string" - }, - { - "name": "null" - } - ] - } - ], - "raw": "Array" - }, - "description": "@internal" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardContent.tsx" - ], - "group": "Card" - }, - "Card/CardCover": { - "filepath": "Card/CardCover.tsx", - "title": "Card.Cover", - "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", - "link": "card-cover", - "data": { - "description": "A component to show a cover image inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Card } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```\n\n@extends Image props https://reactnative.dev/docs/image#props", - "displayName": "Card.Cover", - "methods": [], - "statics": [], - "props": { - "index": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "total": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardCover.tsx" - ], - "group": "Card" - }, - "Card/CardTitle": { - "filepath": "Card/CardTitle.tsx", - "title": "Card.Title", - "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", - "link": "card-title", - "data": { - "description": "A component to show a title, subtitle and an avatar inside a Card.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Avatar, Card, IconButton } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n right={(props) => {}} />}\n />\n);\n\nexport default MyComponent;\n```", - "displayName": "Card.Title", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text for the title. Note that this will only accept a string or ``-based node." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the title." - }, - "titleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Number of lines for the title.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "titleVariant": { - "required": false, - "tsType": { - "name": "TypescaleKey" - }, - "description": "@supported Available in v5.x with theme version 3\n\nTitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyLarge'", - "computed": false - } - }, - "subtitle": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text for the subtitle. Note that this will only accept a string or ``-based node." - }, - "subtitleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the subtitle." - }, - "subtitleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Number of lines for the subtitle.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "subtitleVariant": { - "required": false, - "tsType": { - "name": "TypescaleKey" - }, - "description": "@supported Available in v5.x with theme version 3\n\nSubtitle text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyMedium'", - "computed": false - } - }, - "left": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { size: number }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ size: number }", - "signature": { - "properties": [ - { - "key": "size", - "value": { - "name": "number", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the left side." - }, - "leftStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the left element wrapper." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { size: number }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ size: number }", - "signature": { - "properties": [ - { - "key": "size", - "value": { - "name": "number", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side." - }, - "rightStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the right element wrapper." - }, - "index": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "total": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "@internal" - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "subtitleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a subtitle font can reach." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Card/CardTitle.tsx" - ], - "group": "Card" - }, - "Checkbox/Checkbox": { - "filepath": "Checkbox/Checkbox.tsx", - "title": "Checkbox", - "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", - "link": "checkbox", - "data": { - "description": "Checkboxes allow the selection of multiple options from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState(false);\n\n return (\n {\n setChecked(!checked);\n }}\n />\n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Checkbox", - "methods": [], - "statics": [], - "props": { - "status": { - "required": true, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked' | 'indeterminate'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - }, - { - "name": "literal", - "value": "'indeterminate'" - } - ] - }, - "description": "Status of checkbox." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether checkbox is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom color for unchecked checkbox." - }, - "color": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom color for checkbox." - }, - "error": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the checkbox is in an error state. When true, the outline\n(unchecked) and container (selected) use `theme.colors.error`.\n`disabled` and explicit `color`/`uncheckedColor` overrides take\nprecedence." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Custom style to override the default tap target. Passed through to\nthe underlying `TouchableRipple`." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Checkbox/Checkbox.tsx" - ] - }, - "Checkbox/CheckboxItem": { - "filepath": "Checkbox/CheckboxItem.tsx", - "title": "Checkbox.Item", - "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "checkbox-item", - "data": { - "description": "Checkbox.Item allows you to press the whole row (item) instead of only the Checkbox.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Checkbox } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Checkbox.Item", - "methods": [], - "statics": [], - "props": { - "status": { - "required": true, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked' | 'indeterminate'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - }, - { - "name": "literal", - "value": "'indeterminate'" - } - ] - }, - "description": "Status of checkbox." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether checkbox is disabled." - }, - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Label to be displayed on the item." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", - "defaultValue": { - "value": "label", - "computed": true - } - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked checkbox." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for checkbox." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Additional styles for container View." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach.", - "defaultValue": { - "value": "1.5", - "computed": false - } - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Label element." - }, - "labelVariant": { - "required": false, - "tsType": { - "name": "TypescaleKey" - }, - "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyLarge'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - }, - "position": { - "required": false, - "tsType": { - "name": "union", - "raw": "'leading' | 'trailing'", - "elements": [ - { - "name": "literal", - "value": "'leading'" - }, - { - "name": "literal", - "value": "'trailing'" - } - ] - }, - "description": "Checkbox control position.", - "defaultValue": { - "value": "'trailing'", - "computed": false - } - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Checkbox/CheckboxItem.tsx" - ], - "group": "Checkbox" - }, - "Chip/Chip": { - "filepath": "Chip/Chip.tsx", - "title": "Chip", - "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n
      \n
    • Present multiple options
    • \n
    • Represent attributes active or chosen
    • \n
    • Present filter options
    • \n
    • Trigger actions related to primary content
    • \n
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", - "link": "chip", - "data": { - "description": "Chips are compact elements that can represent inputs, attributes, or actions.\nThey can have an icon or avatar on the left, and a close button icon on the right.\nThey are typically used to:\n
      \n
    • Present multiple options
    • \n
    • Represent attributes active or chosen
    • \n
    • Present filter options
    • \n
    • Trigger actions related to primary content
    • \n
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { Chip } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}>Example Chip\n);\n\nexport default MyComponent;\n```", - "displayName": "Chip", - "methods": [], - "statics": [], - "props": { - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'outlined'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'outlined'" - } - ] - }, - "description": "Mode of the chip.\n- `flat` - flat chip without outline.\n- `outlined` - chip with an outline.", - "defaultValue": { - "value": "'flat'", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text content of the `Chip`." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `Chip`. Both icon and avatar cannot be specified." - }, - "avatar": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Avatar to display for the `Chip`. Both icon and avatar cannot be specified." - }, - "closeIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display as the close button for the `Chip`. The icon appears only when the onClose prop is specified." - }, - "selected": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether chip is selected.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "selectedColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Whether to style the chip color as selected.\nNote: With theme version 3 `selectedColor` doesn't apply to the `icon`.\n If you want specify custom color for the `icon`, render your own `Icon` component." - }, - "showSelectedOverlay": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether to display overlay on selected chip" - }, - "showSelectedCheck": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to display default check icon on selected chip.\nNote: Check will not be shown if `icon` is specified. If specified, `icon` will be shown regardless of `selected`.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the chip is disabled. A disabled chip is greyed out and `onPress` is not called on touch.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the chip. This is read by the screen reader when the user taps the chip." - }, - "closeIconAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon.", - "defaultValue": { - "value": "'Close'", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touchable element is pressed and invoked even before onPress." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute as soon as the touch is released even before onPress." - }, - "onClose": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on close button press. The close button appears only when this prop is specified." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "compact": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nSets smaller horizontal paddings `12dp` around label, when there is only label." - }, - "elevated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether chip should have the elevation.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "textStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of chip's text" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Pass down testID from chip props to touchable for Detox tests.", - "defaultValue": { - "value": "'chip'", - "computed": false - } - }, - "ellipsizeMode": { - "required": false, - "tsType": { - "name": "EllipsizeProp" - }, - "description": "Ellipsize Mode for the children text" - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "Reference to the chip container." - }, - "role": { - "defaultValue": { - "value": "'button'", - "computed": false - }, - "required": false, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Chip/Chip.tsx" - ] - }, - "DataTable/DataTable": { - "filepath": "DataTable/DataTable.tsx", - "title": "DataTable", - "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "data-table", - "data": { - "description": "Data tables allow displaying sets of data.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPageList] = React.useState([2, 3, 4]);\n const [itemsPerPage, onItemsPerPageChange] = React.useState(\n numberOfItemsPerPageList[0]\n );\n\n const [items] = React.useState([\n {\n key: 1,\n name: 'Cupcake',\n calories: 356,\n fat: 16,\n },\n {\n key: 2,\n name: 'Eclair',\n calories: 262,\n fat: 16,\n },\n {\n key: 3,\n name: 'Frozen yogurt',\n calories: 159,\n fat: 6,\n },\n {\n key: 4,\n name: 'Gingerbread',\n calories: 305,\n fat: 3.7,\n },\n ]);\n\n const from = page * itemsPerPage;\n const to = Math.min((page + 1) * itemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [itemsPerPage]);\n\n return (\n \n \n Dessert\n Calories\n Fat\n \n\n {items.slice(from, to).map((item) => (\n \n {item.name}\n {item.calories}\n {item.fat}\n \n ))}\n\n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={itemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n showFastPaginationControls\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "DataTable", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTable`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTable.tsx" - ] - }, - "DataTable/DataTableCell": { - "filepath": "DataTable/DataTableCell.tsx", - "title": "DataTable.Cell", - "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "data-table-cell", - "data": { - "description": "A component to show a single cell inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\nIf you want to support multiline text, please use View instead, as multiline text doesn't comply with\nMD Guidelines (https://github.com/callstack/react-native-paper/issues/2381).\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "DataTable.Cell", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTableCell`." - }, - "numeric": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Align the text to the right. Generally monetary or number fields are aligned to right." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "textStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Text content style of the `DataTableCell`." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableCell.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTableHeader": { - "filepath": "DataTable/DataTableHeader.tsx", - "title": "DataTable.Header", - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "link": "data-table-header", - "data": { - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "DataTable.Header", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTableHeader`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableHeader.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTablePagination": { - "filepath": "DataTable/DataTablePagination.tsx", - "title": "DataTable.Pagination", - "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "data-table-pagination", - "data": { - "description": "A component to show pagination for data table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst numberOfItemsPerPageList = [2, 3, 4];\n\nconst items = [\n {\n key: 1,\n name: 'Page 1',\n },\n {\n key: 2,\n name: 'Page 2',\n },\n {\n key: 3,\n name: 'Page 3',\n },\n];\n\nconst MyComponent = () => {\n const [page, setPage] = React.useState(0);\n const [numberOfItemsPerPage, onItemsPerPageChange] = React.useState(numberOfItemsPerPageList[0]);\n const from = page * numberOfItemsPerPage;\n const to = Math.min((page + 1) * numberOfItemsPerPage, items.length);\n\n React.useEffect(() => {\n setPage(0);\n }, [numberOfItemsPerPage]);\n\n return (\n \n setPage(page)}\n label={`${from + 1}-${to} of ${items.length}`}\n showFastPaginationControls\n numberOfItemsPerPageList={numberOfItemsPerPageList}\n numberOfItemsPerPage={numberOfItemsPerPage}\n onItemsPerPageChange={onItemsPerPageChange}\n selectPageDropdownLabel={'Rows per page'}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "DataTable.Pagination", - "methods": [], - "statics": [], - "props": { - "page": { - "required": true, - "tsType": { - "name": "number" - }, - "description": "The currently visible page (starting with 0)." - }, - "numberOfPages": { - "required": true, - "tsType": { - "name": "number" - }, - "description": "The total number of pages." - }, - "onPageChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(page: number) => void", - "signature": { - "arguments": [ - { - "name": "page", - "type": { - "name": "number" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on page change." - }, - "showFastPaginationControls": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show fast forward and fast rewind buttons in pagination. False by default.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "numberOfItemsPerPage": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The current number of rows per page." - }, - "numberOfItemsPerPageList": { - "required": false, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "number" - } - ], - "raw": "Array" - }, - "description": "Options for a number of rows per page to choose from." - }, - "onItemsPerPageChange": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(numberOfItemsPerPage: number) => void", - "signature": { - "arguments": [ - { - "name": "numberOfItemsPerPage", - "type": { - "name": "number" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "The function to set the number of rows per page." - }, - "selectPageDropdownLabel": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Label text for select page dropdown to display." - }, - "selectPageDropdownAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "AccessibilityLabel for `selectPageDropdownLabel`." - }, - "label": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Label text to display which indicates current pagination." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "AccessibilityLabel for `label`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTablePagination.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTableRow": { - "filepath": "DataTable/DataTableRow.tsx", - "title": "DataTable.Row", - "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "data-table-row", - "data": { - "description": "A component to show a single row inside of a table.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n 1\n 2\n 3\n 4\n \n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "DataTable.Row", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DataTableRow`." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "pointerEvents": { - "required": false, - "tsType": { - "name": "ViewProps['pointerEvents']", - "raw": "ViewProps['pointerEvents']" - }, - "description": "`pointerEvents` passed to the `View` container, which is wrapping children within `TouchableRipple`." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableRow.tsx" - ], - "group": "DataTable" - }, - "DataTable/DataTableTitle": { - "filepath": "DataTable/DataTableTitle.tsx", - "title": "DataTable.Title", - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "link": "data-table-title", - "data": { - "description": "A component to display title in table header.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { DataTable } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n Dessert\n \n Calories\n Fat (g)\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "DataTable.Title", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text content of the `DataTableTitle`." - }, - "numeric": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Align the text to the right. Generally monetary or number fields are aligned to right." - }, - "sortDirection": { - "required": false, - "tsType": { - "name": "union", - "raw": "'ascending' | 'descending'", - "elements": [ - { - "name": "literal", - "value": "'ascending'" - }, - { - "name": "literal", - "value": "'descending'" - } - ] - }, - "description": "Direction of sorting. An arrow indicating the direction is displayed when this is given." - }, - "numberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of lines to show.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "textStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Text content style of the `DataTableTitle`." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/DataTable/DataTableTitle.tsx" - ], - "group": "DataTable" - }, - "Dialog/Dialog": { - "filepath": "Dialog/Dialog.tsx", - "title": "Dialog", - "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog", - "data": { - "description": "Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks.\nTo render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showDialog = () => setVisible(true);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n Alert\n \n This is simple dialog\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog", - "methods": [], - "statics": [], - "props": { - "dismissable": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking outside the dialog dismiss it.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "dismissableBackButton": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking Android hardware back button dismiss dialog.", - "defaultValue": { - "value": "dismissable", - "computed": true - } - }, - "onDismiss": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the user dismisses the dialog." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines Whether the dialog is visible.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Dialog`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/Dialog.tsx" - ] - }, - "Dialog/DialogActions": { - "filepath": "Dialog/DialogActions.tsx", - "title": "Dialog.Actions", - "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-actions", - "data": { - "description": "A component to show a list of actions in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Button, Dialog, Portal } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Actions", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DialogActions`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogActions.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogContent": { - "filepath": "Dialog/DialogContent.tsx", - "title": "Dialog.Content", - "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-content", - "data": { - "description": "A component to show content in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Content", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DialogContent`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogContent.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogIcon": { - "filepath": "Dialog/DialogIcon.tsx", - "title": "Dialog.Icon", - "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", - "link": "dialog-icon", - "data": { - "description": "@supported Available in v5.x with theme version 3\nA component to show an icon in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n title: {\n textAlign: 'center',\n },\n})\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Icon", - "methods": [], - "statics": [], - "props": { - "color": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom color for action icon." - }, - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Name of the icon to show." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Optional icon size.", - "defaultValue": { - "value": "24", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogIcon.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogScrollArea": { - "filepath": "Dialog/DialogScrollArea.tsx", - "title": "Dialog.ScrollArea", - "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-scroll-area", - "data": { - "description": "A component to show a scrollable content in a Dialog. The component only provides appropriate styling.\nFor the scrollable content you can use `ScrollView`, `FlatList` etc. depending on your requirement.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ScrollView } from 'react-native';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n \n \n This is a scrollable area\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.ScrollArea", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `DialogScrollArea`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogScrollArea.tsx" - ], - "group": "Dialog" - }, - "Dialog/DialogTitle": { - "filepath": "Dialog/DialogTitle.tsx", - "title": "Dialog.Title", - "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "dialog-title", - "data": { - "description": "A component to show a title in a Dialog.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Dialog, Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const hideDialog = () => setVisible(false);\n\n return (\n \n \n This is a title\n \n This is simple dialog\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Dialog.Title", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Title text for the `DialogTitle`." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Dialog/DialogTitle.tsx" - ], - "group": "Dialog" - }, - "Divider": { - "filepath": "Divider.tsx", - "title": "Divider", - "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", - "link": "divider", - "data": { - "description": "A divider is a thin, lightweight separator that groups content in lists and page layouts.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Divider, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Lemon\n \n Mango\n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "Divider", - "methods": [], - "statics": [], - "props": { - "leftInset": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@renamed Renamed from 'inset' to 'leftInset` in v5.x\nWhether divider has a left inset." - }, - "horizontalInset": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n Whether divider has a horizontal inset on both sides.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "bold": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n Whether divider should be bolded.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Divider.tsx" - ] - }, - "Drawer/DrawerCollapsedItem": { - "filepath": "Drawer/DrawerCollapsedItem.tsx", - "title": "Drawer.CollapsedItem", - "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "drawer-collapsed-item", - "data": { - "description": "Note: Available in v5.x with theme version 3\n\nCollapsed component used to show an action item with an icon and optionally label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Drawer.CollapsedItem", - "methods": [], - "statics": [], - "props": { - "label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "The label text of the item." - }, - "badge": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number | boolean", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - }, - { - "name": "boolean" - } - ] - }, - "description": "Badge to show on the icon, can be `true` to show a dot, `string` or `number` to show text.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the item is disabled." - }, - "focusedIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the focused destination icon, can be a string, an image source or a react component" - }, - "unfocusedIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@renamed Renamed from 'icon' to 'focusedIcon' in v5.x\nIcon to use as the unfocused destination icon, can be a string, an image source or a react component" - }, - "active": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to highlight the drawer item as active." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'drawer-collapsed-item'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerCollapsedItem.tsx" - ], - "group": "Drawer" - }, - "Drawer/DrawerItem": { - "filepath": "Drawer/DrawerItem.tsx", - "title": "Drawer.Item", - "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "drawer-item", - "data": { - "description": "A component used to show an action item with an icon and a label in a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Drawer.Item", - "methods": [], - "statics": [], - "props": { - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "The label text of the item." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `DrawerItem`." - }, - "active": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to highlight the drawer item as active." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the item is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: ColorValue }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: ColorValue }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side. For instance a Badge." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerItem.tsx" - ], - "group": "Drawer" - }, - "Drawer/DrawerSection": { - "filepath": "Drawer/DrawerSection.tsx", - "title": "Drawer.Section", - "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "drawer-section", - "data": { - "description": "A component to group content inside a navigation drawer.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Drawer } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [active, setActive] = React.useState('');\n\n return (\n \n setActive('first')}\n />\n setActive('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Drawer.Section", - "methods": [], - "statics": [], - "props": { - "title": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Title to show as the header for the section." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Drawer.Section`." - }, - "showDivider": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show `Divider` at the end of the section. True by default.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Drawer/DrawerSection.tsx" - ], - "group": "Drawer" - }, - "FAB/FAB": { - "filepath": "FAB/FAB.tsx", - "title": "FAB", - "description": "A floating action button represents the primary action on a screen.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", - "link": "fab", - "data": { - "description": "A floating action button represents the primary action on a screen.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n right: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "FAB", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display inside the FAB." - }, - "variant": { - "required": false, - "tsType": { - "name": "Variant" - }, - "description": "Role-color preset. Defaults to `tonalPrimary`.", - "defaultValue": { - "value": "'tonalPrimary'", - "computed": false - } - }, - "containerColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Override the container (background) color." - }, - "contentColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Override the content (icon) color." - }, - "size": { - "required": false, - "tsType": { - "name": "Size" - }, - "description": "Spec size. Defaults to `default`.", - "defaultValue": { - "value": "'default'", - "computed": false - } - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the FAB is currently visible. Toggling animates the spec'd enter\nand exit (scale + alpha) on the FAB itself.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label. Falls back to nothing if unset." - }, - "aria-checked": { - "required": false, - "tsType": { - "name": "union", - "raw": "boolean | 'mixed'", - "elements": [ - { - "name": "boolean" - }, - { - "name": "literal", - "value": "'mixed'" - } - ] - }, - "description": "Indicates whether the element is checked. Accepts `true`, `false`,\nor `'mixed'` for an indeterminate state." - }, - "aria-selected": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element is selected." - }, - "aria-busy": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element is currently busy (e.g. loading)." - }, - "aria-expanded": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element's controlled content is expanded." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawable to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "AnimatedStyle" - } - ], - "raw": "StyleProp>" - }, - "description": "Style for positioning the FAB. The visual treatment (size, shape, color)\nis driven by `variant` and `size`." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes.", - "defaultValue": { - "value": "'floating-action-button'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/FAB/FAB.tsx" - ] - }, - "FAB/Extended": { - "filepath": "FAB/Extended.tsx", - "title": "Extended", - "description": "An extended floating action button represents the primary action on a screen\nand shows a label next to the icon. Animates between expanded (icon + label)\nand collapsed (icon only) states.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n return (\n setExpanded((v) => !v)}\n style={styles.fab}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n left: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", - "link": "extended", - "data": { - "description": "An extended floating action button represents the primary action on a screen\nand shows a label next to the icon. Animates between expanded (icon + label)\nand collapsed (icon only) states.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { StyleSheet } from 'react-native';\nimport { FAB } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n return (\n setExpanded((v) => !v)}\n style={styles.fab}\n />\n );\n};\n\nconst styles = StyleSheet.create({\n fab: {\n position: 'absolute',\n margin: 16,\n left: 0,\n bottom: 0,\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "Extended", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display inside the FAB." - }, - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Label rendered next to the icon when expanded." - }, - "variant": { - "required": false, - "tsType": { - "name": "Variant" - }, - "description": "Role-color preset. Defaults to `tonalPrimary`.", - "defaultValue": { - "value": "'tonalPrimary'", - "computed": false - } - }, - "containerColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Override the container (background) color. When set without `contentColor`,\nthe icon and label colors are derived automatically via `contentColorFor`." - }, - "contentColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Override the content (icon + label) color." - }, - "size": { - "required": false, - "tsType": { - "name": "Size" - }, - "description": "Spec size. Defaults to `default`.", - "defaultValue": { - "value": "'default'", - "computed": false - } - }, - "expanded": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether the FAB is expanded (icon + label) or collapsed (icon only). The\nwidth and label opacity animate per the MD3 Expressive spec on change." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the FAB is currently visible. Toggling animates the spec'd enter\nand exit (scale + alpha) on the FAB itself.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label. Falls back to `label` if unset.", - "defaultValue": { - "value": "label", - "computed": true - } - }, - "aria-checked": { - "required": false, - "tsType": { - "name": "union", - "raw": "boolean | 'mixed'", - "elements": [ - { - "name": "boolean" - }, - { - "name": "literal", - "value": "'mixed'" - } - ] - }, - "description": "Indicates whether the element is checked. Accepts `true`, `false`,\nor `'mixed'` for an indeterminate state." - }, - "aria-selected": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element is selected." - }, - "aria-busy": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element is currently busy (e.g. loading)." - }, - "aria-expanded": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element's controlled content is expanded." - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawable to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "AnimatedStyle" - } - ], - "raw": "StyleProp>" - }, - "description": "Style for positioning the FAB. The visual treatment (size, shape, color)\nis driven by `variant` and `size`." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes.", - "defaultValue": { - "value": "'extended-floating-action-button'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/FAB/Extended.tsx" - ] - }, - "FAB/Menu": { - "filepath": "FAB/Menu.tsx", - "title": "Menu", - "description": "Floating action button menu. Wraps a trigger FAB; when `expanded` is true,\nitems appear stacked above and the trigger morphs into the spec'd close\nbutton (`shape: 'full'`, 56 dp, saturated role color).\n\nNo visual backdrop and no outside-tap dismiss — that matches the MD3 spec\nand lets the user keep interacting with the content underneath. Dismiss\nvia the close button or by tapping an item.\n\n## Usage\n```tsx\nconst [open, setOpen] = React.useState(false);\n\n\n setOpen(false)}\n trigger={{ icon: 'plus', variant: 'primary', onPress: () => setOpen(true) }}\n items={[\n { icon: 'email', label: 'Send', onPress: () => {} },\n { icon: 'bell', label: 'Remind', onPress: () => {} },\n ]}\n />\n\n```", - "link": "menu", - "data": { - "description": "Floating action button menu. Wraps a trigger FAB; when `expanded` is true,\nitems appear stacked above and the trigger morphs into the spec'd close\nbutton (`shape: 'full'`, 56 dp, saturated role color).\n\nNo visual backdrop and no outside-tap dismiss — that matches the MD3 spec\nand lets the user keep interacting with the content underneath. Dismiss\nvia the close button or by tapping an item.\n\n## Usage\n```tsx\nconst [open, setOpen] = React.useState(false);\n\n\n setOpen(false)}\n trigger={{ icon: 'plus', variant: 'primary', onPress: () => setOpen(true) }}\n items={[\n { icon: 'email', label: 'Send', onPress: () => {} },\n { icon: 'bell', label: 'Remind', onPress: () => {} },\n ]}\n />\n\n```", - "displayName": "Menu", - "methods": [], - "statics": [], - "props": { - "expanded": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether the menu is open." - }, - "onDismiss": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Called when the user taps the close button or taps an item." - }, - "trigger": { - "required": true, - "tsType": { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Icon displayed in the trigger FAB (and cross-faded to `closeIcon` when\n * the menu is open).\n */\n icon: IconSource;\n variant?: Variant;\n size?: Size;\n containerColor?: ColorValue;\n contentColor?: ColorValue;\n visible?: boolean;\n onPress?: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label for the trigger FAB.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": true - } - }, - { - "key": "variant", - "value": { - "name": "Variant", - "required": false - } - }, - { - "key": "size", - "value": { - "name": "Size", - "required": false - } - }, - { - "key": "containerColor", - "value": { - "name": "ColorValue", - "required": false - } - }, - { - "key": "contentColor", - "value": { - "name": "ColorValue", - "required": false - } - }, - { - "key": "visible", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": false - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - "description": "Trigger FAB configuration. The menu renders a morphing FAB that\nanimates between the trigger appearance and the spec'd close button." - }, - "alignment": { - "required": false, - "tsType": { - "name": "union", - "raw": "'start' | 'center' | 'end'", - "elements": [ - { - "name": "literal", - "value": "'start'" - }, - { - "name": "literal", - "value": "'center'" - }, - { - "name": "literal", - "value": "'end'" - } - ] - }, - "description": "Horizontal side the menu sits on. Default `'end'`.", - "defaultValue": { - "value": "'end'", - "computed": false - } - }, - "closeIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon used by the close button when the menu is expanded. Default\n`'close'`.", - "defaultValue": { - "value": "'close'", - "computed": false - } - }, - "items": { - "required": true, - "tsType": { - "name": "union", - "raw": "| [T, T]\n| [T, T, T]\n| [T, T, T, T]\n| [T, T, T, T, T]\n| [T, T, T, T, T, T]", - "elements": [ - { - "name": "tuple", - "raw": "[T, T]", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - } - ] - }, - { - "name": "tuple", - "raw": "[T, T, T]", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - } - ] - }, - { - "name": "tuple", - "raw": "[T, T, T, T]", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - } - ] - }, - { - "name": "tuple", - "raw": "[T, T, T, T, T]", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - } - ] - }, - { - "name": "tuple", - "raw": "[T, T, T, T, T, T]", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - }, - { - "name": "signature", - "type": "object", - "raw": "{\n /**\n * Optional icon for the item.\n */\n icon?: IconSource;\n /**\n * Mandatory label.\n */\n label: string;\n /**\n * Called when the item is pressed. The menu is dismissed automatically\n * after `onPress` runs.\n */\n onPress: (e: GestureResponderEvent) => void;\n /**\n * Accessibility label. Falls back to `label`.\n */\n 'aria-label'?: string;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": true - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": true - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - } - ] - } - ] - }, - "description": "Menu items. Spec calls for 2 to 6 items." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "", - "defaultValue": { - "value": "'floating-action-button-menu'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/FAB/Menu.tsx" - ] - }, - "IconButton/IconButton": { - "filepath": "IconButton/IconButton.tsx", - "title": "IconButton", - "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "icon-button", - "data": { - "description": "An icon button is a button which displays only an icon without a label.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "IconButton", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'outlined' | 'contained' | 'contained-tonal'", - "elements": [ - { - "name": "literal", - "value": "'outlined'" - }, - { - "name": "literal", - "value": "'contained'" - }, - { - "name": "literal", - "value": "'contained-tonal'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nMode of the icon button. By default there is no specified mode - only pressable icon will be rendered." - }, - "iconColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "@renamed Renamed from 'color' to 'iconColor' in v5.x\nColor of the icon." - }, - "containerColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Background color of the icon container." - }, - "selected": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether icon button is selected. A selected button receives alternative combination of icon and container colors.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the icon.", - "defaultValue": { - "value": "24", - "computed": false - } - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled. A disabled button is greyed out and `onPress` is not called on touch." - }, - "animated": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether an icon change is animated.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of button's inner content.\nUse this prop to apply custom height and width or to set a custom padding`." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "AnimatedStyle" - } - ], - "raw": "StyleProp>" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'icon-button'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "loading": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show a loading indicator.", - "defaultValue": { - "value": "false", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/IconButton/IconButton.tsx" - ] - }, - "Icon": { - "filepath": "Icon.tsx", - "title": "Icon", - "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "icon", - "data": { - "description": "An icon component which renders icon from vector library.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Icon, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Icon", - "methods": [], - "statics": [], - "props": { - "size": { - "required": true, - "tsType": { - "name": "number" - }, - "description": "Size of icon." - }, - "allowFontScaling": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "" - }, - "source": { - "required": true, - "tsType": { - "name": "any" - }, - "description": "Icon to display." - }, - "color": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the icon." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Icon.tsx" - ] - }, - "List/ListAccordion": { - "filepath": "List/ListAccordion.tsx", - "title": "List.Accordion", - "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "list-accordion", - "data": { - "description": "A component used to display an expandable list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [expanded, setExpanded] = React.useState(true);\n\n const handlePress = () => setExpanded(!expanded);\n\n return (\n \n }>\n \n \n \n\n }\n expanded={expanded}\n onPress={handlePress}>\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "List.Accordion", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Title text for the list accordion." - }, - "description": { - "required": false, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Description text for the list accordion." - }, - "left": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: ColorValue; style: Style }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: ColorValue; style: Style }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the left side." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { isExpanded: boolean }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ isExpanded: boolean }", - "signature": { - "properties": [ - { - "key": "isExpanded", - "value": { - "name": "boolean", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side." - }, - "expanded": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the accordion is expanded\nIf this prop is provided, the accordion will behave as a \"controlled component\".\nYou'll need to update this prop when you want to toggle the component or on `onPress`." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "delayLongPress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before executing `onLongPress`." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the section." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the root TouchableRipple container." - }, - "containerStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the content container, which wraps the title and description." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Title element." - }, - "descriptionStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Description element." - }, - "titleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "descriptionNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "descriptionMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a description font can reach." - }, - "id": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - }, - "description": "Id is used for distinguishing specific accordion when using List.AccordionGroup. Property is required when using List.AccordionGroup and has no impact on behavior when using standalone List.Accordion." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the TouchableRipple. This is read by the screen reader when the user taps the touchable." - }, - "pointerEvents": { - "required": false, - "tsType": { - "name": "ViewProps['pointerEvents']", - "raw": "ViewProps['pointerEvents']" - }, - "description": "`pointerEvents` passed to the `View` container", - "defaultValue": { - "value": "'none'", - "computed": false - } - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Amount of space between the touchable area and the edge of the component.\nThis can be used to enlarge the touchable area beyond the visible component." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListAccordion.tsx" - ], - "group": "List" - }, - "List/ListAccordionGroup": { - "filepath": "List/ListAccordionGroup.tsx", - "title": "List.AccordionGroup", - "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "list-accordion-group", - "data": { - "description": "List.AccordionGroup allows to control a group of List Accordions. `id` prop for List.Accordion is required in order for group to work.\nList.AccordionGroup can be a controlled or uncontrolled component. The example shows the uncontrolled version.\nAt most one Accordion can be expanded at a given time.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, Text } from 'react-native';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n \n \n \n \n \n \n \n \n List.Accordion can be wrapped because implementation uses React.Context.\n \n \n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "List.AccordionGroup", - "methods": [], - "statics": [], - "props": { - "onAccordionPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(expandedId: string | number) => void", - "signature": { - "arguments": [ - { - "name": "expandedId", - "type": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "expandedId": { - "required": false, - "tsType": { - "name": "union", - "raw": "string | number", - "elements": [ - { - "name": "string" - }, - { - "name": "number" - } - ] - }, - "description": "Id of the currently expanded list accordion" - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing list accordions" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListAccordionGroup.tsx" - ], - "group": "List" - }, - "List/ListIcon": { - "filepath": "List/ListIcon.tsx", - "title": "List.Icon", - "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", - "link": "list-icon", - "data": { - "description": "A component to show an icon in a list item.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n \n \n \n \n);\n\nexport default MyComponent;\n```", - "displayName": "List.Icon", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to show." - }, - "color": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color for the icon." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListIcon.tsx" - ], - "group": "List" - }, - "List/ListItem": { - "filepath": "List/ListItem.tsx", - "title": "List.Item", - "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "list-item", - "data": { - "description": "A component to show tiles inside a List.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => (\n }\n />\n);\n\nexport default MyComponent;\n```\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "List.Item", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "union", - "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: ColorValue;\n fontSize: number;\n }) => React.ReactNode)", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "unknown" - } - ] - }, - "description": "Title text for the list item." - }, - "description": { - "required": false, - "tsType": { - "name": "union", - "raw": "| React.ReactNode\n| ((props: {\n selectable: boolean;\n ellipsizeMode: EllipsizeProp | undefined;\n color: ColorValue;\n fontSize: number;\n }) => React.ReactNode)", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "unknown" - } - ] - }, - "description": "Description text for the list item or callback which returns a React element to display the description." - }, - "left": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: ColorValue; style: Style }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: ColorValue; style: Style }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the left side." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: { color: ColorValue; style?: Style }) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{ color: ColorValue; style?: Style }", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": false - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback which returns a React element to display on the right side." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the root TouchableRipple container." - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "containerStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the outermost container that wraps the entire content, including left and right items and both title and description." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the content container, which wraps the title and description." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Title element." - }, - "descriptionStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Description element." - }, - "titleNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Title text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "descriptionNumberOfLines": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Truncate Description text such that the total number of lines does not\nexceed this number.", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "titleEllipsizeMode": { - "required": false, - "tsType": { - "name": "EllipsizeProp" - }, - "description": "Ellipsize Mode for the Title. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" - }, - "descriptionEllipsizeMode": { - "required": false, - "tsType": { - "name": "EllipsizeProp" - }, - "description": "Ellipsize Mode for the Description. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.\n\nSee [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)" - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "descriptionMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a description font can reach." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListItem.tsx" - ], - "group": "List" - }, - "List/ListSection": { - "filepath": "List/ListSection.tsx", - "title": "List.Section", - "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", - "link": "list-section", - "data": { - "description": "A component used to group list items.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Some title\n } />\n }\n />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "List.Section", - "methods": [], - "statics": [], - "props": { - "title": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Title text for the section." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the section." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Title element." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListSection.tsx" - ], - "group": "List" - }, - "List/ListSubheader": { - "filepath": "List/ListSubheader.tsx", - "title": "List.Subheader", - "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", - "link": "list-subheader", - "data": { - "description": "A component used to display a header in lists.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { List } from 'react-native-paper';\n\nconst MyComponent = () => My List Title;\n\nexport default MyComponent;\n```", - "displayName": "List.Subheader", - "methods": [], - "statics": [], - "props": { - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Text element." - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/List/ListSubheader.tsx" - ], - "group": "List" - }, - "Menu/Menu": { - "filepath": "Menu/Menu.tsx", - "title": "Menu", - "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n
    \n \n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", - "link": "menu", - "data": { - "description": "Menus display a list of choices on temporary elevated surfaces. Their placement varies based on the element that opens them.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Button, Menu, Divider, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const openMenu = () => setVisible(true);\n\n const closeMenu = () => setVisible(false);\n\n return (\n \n \n Show menu}>\n {}} title=\"Item 1\" />\n {}} title=\"Item 2\" />\n \n {}} title=\"Item 3\" />\n
    \n \n \n );\n};\n\nexport default MyComponent;\n```\n\n### Note\nWhen using `Menu` within a React Native's `Modal` component, you need to wrap all\n`Modal` contents within a `PaperProvider` in order for the menu to show. This\nwrapping is not necessary if you use Paper's `Modal` instead.", - "displayName": "Menu", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether the Menu is currently visible." - }, - "anchor": { - "required": true, - "tsType": { - "name": "union", - "raw": "React.ReactNode | { x: number; y: number }", - "elements": [ - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - { - "name": "signature", - "type": "object", - "raw": "{ x: number; y: number }", - "signature": { - "properties": [ - { - "key": "x", - "value": { - "name": "number", - "required": true - } - }, - { - "key": "y", - "value": { - "name": "number", - "required": true - } - } - ] - } - } - ] - }, - "description": "The anchor to open the menu from. In most cases, it will be a button that opens the menu." - }, - "anchorPosition": { - "required": false, - "tsType": { - "name": "union", - "raw": "'top' | 'bottom'", - "elements": [ - { - "name": "literal", - "value": "'top'" - }, - { - "name": "literal", - "value": "'bottom'" - } - ] - }, - "description": "Whether the menu should open at the top of the anchor or at its bottom.\nApplied only when anchor is a node, not an x/y position." - }, - "statusBarHeight": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Extra margin to add at the top of the menu to account for translucent status bar on Android.\nIf you are using Expo, we assume translucent status bar and set a height for status bar automatically.\nPass `0` or a custom value to and customize it.\nThis is automatically handled on iOS." - }, - "onDismiss": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback called when Menu is dismissed. The `visible` prop needs to be updated when this is called." - }, - "overlayAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the menu.", - "defaultValue": { - "value": "'Close menu'", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Menu`." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style of menu's inner content." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "elevation": { - "required": false, - "tsType": { - "name": "Elevation" - }, - "description": "Elevation level of the menu's content. Shadow styles are calculated based on this value. Default `backgroundColor` is taken from the corresponding `theme.colors.elevation` property. By default equals `2`.\n@supported Available in v5.x with theme version 3", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'elevated'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'elevated'" - } - ] - }, - "description": "Mode of the menu's content.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.\n\n@supported Available in v5.x with theme version 3", - "defaultValue": { - "value": "'elevated'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "keyboardShouldPersistTaps": { - "required": false, - "tsType": { - "name": "ScrollViewProps['keyboardShouldPersistTaps']", - "raw": "ScrollViewProps['keyboardShouldPersistTaps']" - }, - "description": "Inner ScrollView prop" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'menu'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Menu/Menu.tsx" - ] - }, - "Menu/MenuItem": { - "filepath": "Menu/MenuItem.tsx", - "title": "Menu.Item", - "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", - "link": "menu-item", - "data": { - "description": "A component to show a single list item inside a Menu.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Menu } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} title=\"Redo\" />\n {}} title=\"Undo\" />\n {}} title=\"Cut\" disabled />\n {}} title=\"Copy\" disabled />\n {}} title=\"Paste\" />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Menu.Item", - "methods": [], - "statics": [], - "props": { - "title": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Title text for the `MenuItem`." - }, - "leadingIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@renamed Renamed from 'icon' to 'leadingIcon' in v5.x\n\nLeading icon to display for the `MenuItem`." - }, - "trailingIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@supported Available in v5.x with theme version 3\n\nTrailing icon to display for the `MenuItem`." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the 'item' is disabled. A disabled 'item' is greyed out and `onPress` is not called on touch." - }, - "dense": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\n\nSets min height with densed layout." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach.", - "defaultValue": { - "value": "1.5", - "computed": false - } - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the root TouchableRipple container.\n" - }, - "containerStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the outermost container that wraps the entire content, including leading and trailing icons and title text." - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the content container, which wraps the title text." - }, - "titleStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to the Title element." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'menu-item'", - "computed": false - } - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the Touchable. This is read by the screen reader when the user taps the component." - }, - "aria-checked": { - "required": false, - "tsType": { - "name": "union", - "raw": "boolean | 'mixed'", - "elements": [ - { - "name": "boolean" - }, - { - "name": "literal", - "value": "'mixed'" - } - ] - }, - "description": "Indicates whether the element is checked. Accepts `true`, `false`,\nor `'mixed'` for an indeterminate state." - }, - "aria-selected": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element is selected." - }, - "aria-busy": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element is currently busy (e.g. loading)." - }, - "aria-expanded": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Indicates whether the element's controlled content is expanded." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Menu/MenuItem.tsx" - ], - "group": "Menu" - }, - "Modal": { - "filepath": "Modal.tsx", - "title": "Modal", - "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n\n const containerStyle = { padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "modal", - "data": { - "description": "The Modal component is a simple way to present content above an enclosing view.\nTo render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component.\nNote that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const showModal = () => setVisible(true);\n const hideModal = () => setVisible(false);\n\n const containerStyle = { padding: 20 };\n\n return (\n \n \n \n Example Modal. Click outside this area to dismiss.\n \n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "Modal", - "methods": [], - "statics": [], - "props": { - "dismissable": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking outside the modal dismisses it.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "dismissableBackButton": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines whether clicking Android hardware back button dismisses the dialog.", - "defaultValue": { - "value": "dismissable", - "computed": true - } - }, - "onDismiss": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the user dismisses the modal.", - "defaultValue": { - "value": "() => {}", - "computed": false - } - }, - "overlayAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal.", - "defaultValue": { - "value": "'Close modal'", - "computed": false - } - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Determines Whether the modal is visible.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Modal`." - }, - "contentContainerStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the content of the modal.\n\nBackground color and border radius should be specified via props instead:\n- `contentBackgroundColor`\n- `contentBorderRadius`" - }, - "contentBackgroundColor": { - "required": false, - "tsType": { - "name": "SurfaceProps['backgroundColor']", - "raw": "SurfaceProps['backgroundColor']" - }, - "description": "Background color of the modal content. Defaults to transparent.", - "defaultValue": { - "value": "'transparent'", - "computed": false - } - }, - "contentBorderRadius": { - "required": false, - "tsType": { - "name": "SurfaceProps['borderRadius']", - "raw": "SurfaceProps['borderRadius']" - }, - "description": "Border radius of the modal content." - }, - "contentElevation": { - "required": false, - "tsType": { - "name": "Elevation" - }, - "description": "Elevation level of the modal content. Defaults to level 1." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the wrapper of the modal.\nUse this prop to change the default wrapper style or to override safe area insets with marginTop and marginBottom." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'modal'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Modal.tsx" - ] - }, - "Portal/Portal": { - "filepath": "Portal/Portal.tsx", - "title": "Portal", - "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", - "link": "portal", - "data": { - "description": "Portal allows rendering a component at a different place in the parent tree.\nYou can use it to render content which should appear above other elements, similar to `Modal`.\nIt requires a [`Portal.Host`](PortalHost) component to be rendered somewhere in the parent tree.\nNote that if you're using the `Provider` component, this already includes a `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Portal, Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n This is rendered at a different place\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Portal", - "methods": [], - "statics": [ - { - "name": "Host", - "docblock": null, - "type": { - "name": "static", - "raw": "static" - }, - "link": "portal-host.html" - } - ], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Portal`." - }, - "theme": { - "required": true, - "tsType": { - "name": "InternalTheme" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Portal/Portal.tsx" - ] - }, - "Portal/PortalHost": { - "filepath": "Portal/PortalHost.tsx", - "title": "Portal.Host", - "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", - "link": "portal-host", - "data": { - "description": "Portal host renders all of its children `Portal` elements.\nFor example, you can wrap a screen in `Portal.Host` to render items above the screen.\nIf you're using the `Provider` component, it already includes `Portal.Host`.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native';\nimport { Portal } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n Content of the app\n \n);\n\nexport default MyComponent;\n```\n\nHere any `Portal` elements under `` are rendered alongside `` and will appear above `` like a `Modal`.", - "displayName": "Portal.Host", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Portal/PortalHost.tsx" - ], - "group": "Portal" - }, - "ProgressBar": { - "filepath": "ProgressBar.tsx", - "title": "ProgressBar", - "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "link": "progress-bar", - "data": { - "description": "Progress bar is an indicator used to present progress of some activity in the app.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ProgressBar, Palette } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n);\n\nexport default MyComponent;\n```", - "displayName": "ProgressBar", - "methods": [], - "statics": [], - "props": { - "animatedValue": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Animated value (between 0 and 1). This tells the progress bar to rely on this value to animate it.\nNote: It should not be used in parallel with the `progress` prop." - }, - "progress": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Progress value (between 0 and 1).\nNote: It should not be used in parallel with the `animatedValue` prop.", - "defaultValue": { - "value": "0", - "computed": false - } - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Color of the progress bar. The background color will be calculated based on this but you can change it by passing `backgroundColor` to `style` prop." - }, - "indeterminate": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "If the progress bar will show indeterminate progress." - }, - "visible": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to show the ProgressBar (true, the default) or hide it (false).", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "fillStyle": { - "required": false, - "tsType": { - "name": "Animated.WithAnimatedValue", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - } - ], - "raw": "Animated.WithAnimatedValue>" - }, - "description": "Style of filled part of the ProgresBar." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests.", - "defaultValue": { - "value": "'progress-bar'", - "computed": false - } - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ProgressBar.tsx" - ] - }, - "RadioButton/RadioButton": { - "filepath": "RadioButton/RadioButton.tsx", - "title": "RadioButton", - "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "link": "radio-button", - "data": { - "description": "Radio buttons allow the selection a single option from a set.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [checked, setChecked] = React.useState('first');\n\n return (\n \n setChecked('first')}\n />\n setChecked('second')}\n />\n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "RadioButton", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button" - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked radio." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButton.tsx" - ] - }, - "RadioButton/RadioButtonAndroid": { - "filepath": "RadioButton/RadioButtonAndroid.tsx", - "title": "RadioButton.Android", - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "radio-button-android", - "data": { - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for Android, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "RadioButton.Android", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button" - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(param?: any) => void", - "signature": { - "arguments": [ - { - "name": "param", - "type": { - "name": "any" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked radio." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonAndroid.tsx" - ], - "group": "RadioButton" - }, - "RadioButton/RadioButtonGroup": { - "filepath": "RadioButton/RadioButtonGroup.tsx", - "title": "RadioButton.Group", - "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "radio-button-group", - "data": { - "description": "Radio button group allows to control a group of radio buttons.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { RadioButton, Text } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(newValue)} value={value}>\n \n First\n \n \n \n Second\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "RadioButton.Group", - "methods": [], - "statics": [], - "props": { - "onValueChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: string) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the currently selected radio button." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing radio buttons." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonGroup.tsx" - ], - "group": "RadioButton" - }, - "RadioButton/RadioButtonIOS": { - "filepath": "RadioButton/RadioButtonIOS.tsx", - "title": "RadioButton.IOS", - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "link": "radio-button-ios", - "data": { - "description": "Radio buttons allow the selection a single option from a set.\nThis component follows platform guidelines for iOS, but can be used\non any platform.\n\n@extends TouchableRipple props https://callstack.github.io/react-native-paper/docs/components/TouchableRipple", - "displayName": "RadioButton.IOS", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button" - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonIOS.tsx" - ], - "group": "RadioButton" - }, - "RadioButton/RadioButtonItem": { - "filepath": "RadioButton/RadioButtonItem.tsx", - "title": "RadioButton.Item", - "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "radio-button-item", - "data": { - "description": "RadioButton.Item allows you to press the whole row (item) instead of only the RadioButton.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { RadioButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('first');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "RadioButton.Item", - "methods": [], - "statics": [], - "props": { - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the radio button." - }, - "label": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Label to be displayed on the item." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether radio is disabled." - }, - "background": { - "required": false, - "tsType": { - "name": "PressableAndroidRippleConfig" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the touchable. This is read by the screen reader when the user taps the touchable.", - "defaultValue": { - "value": "label", - "computed": true - } - }, - "uncheckedColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for unchecked radio." - }, - "color": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom color for radio." - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of radio button." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Additional styles for container View." - }, - "labelStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style that is passed to Label element." - }, - "labelVariant": { - "required": false, - "tsType": { - "name": "TypescaleKey" - }, - "description": "@supported Available in v5.x with theme version 3\n\nLabel text variant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`", - "defaultValue": { - "value": "'bodyLarge'", - "computed": false - } - }, - "labelMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a label font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'android' | 'ios'", - "elements": [ - { - "name": "literal", - "value": "'android'" - }, - { - "name": "literal", - "value": "'ios'" - } - ] - }, - "description": "Whether `` or `` should be used.\nLeft undefined `` will be used." - }, - "position": { - "required": false, - "tsType": { - "name": "union", - "raw": "'leading' | 'trailing'", - "elements": [ - { - "name": "literal", - "value": "'leading'" - }, - { - "name": "literal", - "value": "'trailing'" - } - ] - }, - "description": "Radio button control position.", - "defaultValue": { - "value": "'trailing'", - "computed": false - } - }, - "hitSlop": { - "required": false, - "tsType": { - "name": "TouchableRippleProps['hitSlop']", - "raw": "TouchableRippleProps['hitSlop']" - }, - "description": "Sets additional distance outside of element in which a press can be detected." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/RadioButton/RadioButtonItem.tsx" - ], - "group": "RadioButton" - }, - "Searchbar": { - "filepath": "Searchbar.tsx", - "title": "Searchbar", - "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", - "link": "searchbar", - "data": { - "description": "Searchbar is a simple input box where users can type search queries.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Searchbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [searchQuery, setSearchQuery] = React.useState('');\n\n return (\n \n );\n};\n\nexport default MyComponent;\n\n```", - "displayName": "Searchbar", - "methods": [], - "statics": [], - "props": { - "placeholder": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Hint text shown when the input is empty." - }, - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "The value of the text input." - }, - "onChangeText": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(query: string) => void", - "signature": { - "arguments": [ - { - "name": "query", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback that is called when the text input's text changes." - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'bar' | 'view'", - "elements": [ - { - "name": "literal", - "value": "'bar'" - }, - { - "name": "literal", - "value": "'view'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nSearch layout mode, the default value is \"bar\".", - "defaultValue": { - "value": "'bar'", - "computed": false - } - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon name for the left icon button (see `onIconPress`)." - }, - "iconColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Custom color for icon, default will be derived from theme" - }, - "onIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback to execute if we want the left icon to act as button." - }, - "onClearIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback to execute if we want to add custom behaviour to close icon button." - }, - "searchAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'search'", - "computed": false - } - }, - "clearIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Custom icon for clear button, default will be icon close. It's visible when `loading` is set to `false`.\nIn v5.x with theme version 3, `clearIcon` is visible only if `right` prop is not defined." - }, - "clearAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'clear'", - "computed": false - } - }, - "traileringIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@supported Available in v5.x with theme version 3\nIcon name for the right trailering icon button.\nWorks only when `mode` is set to \"bar\". It won't be displayed if `loading` is set to `true`." - }, - "traileringIconColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "@supported Available in v5.x with theme version 3\nCustom color for the right trailering icon, default will be derived from theme" - }, - "onTraileringIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Callback to execute on the right trailering icon button press." - }, - "traileringIconAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the right trailering icon button. This is read by the screen reader when the user taps the button." - }, - "right": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: {\n color: ColorValue;\n style: Style;\n testID: string;\n}) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "signature", - "type": "object", - "raw": "{\n color: ColorValue;\n style: Style;\n testID: string;\n}", - "signature": { - "properties": [ - { - "key": "color", - "value": { - "name": "ColorValue", - "required": true - } - }, - { - "key": "style", - "value": { - "name": "Style", - "required": true - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "@supported Available in v5.x with theme version 3\nCallback which returns a React element to display on the right side.\nWorks only when `mode` is set to \"bar\"." - }, - "showDivider": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "@supported Available in v5.x with theme version 3\nWhether to show `Divider` at the bottom of the search.\nWorks only when `mode` is set to \"view\". True by default.", - "defaultValue": { - "value": "true", - "computed": false - } - }, - "elevation": { - "required": false, - "tsType": { - "name": "Elevation" - }, - "description": "@supported Available in v5.x with theme version 3\nChanges Searchbar shadow and background on iOS and Android.", - "defaultValue": { - "value": "0", - "computed": false - } - }, - "inputStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Set style of the TextInput component inside the searchbar" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "loading": { - "required": false, - "tsType": { - "name": "Boolean" - }, - "description": "Custom flag for replacing clear button with activity indicator.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes", - "defaultValue": { - "value": "'search-bar'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "Pick", - "elements": [ - { - "name": "TextInput" - }, - { - "name": "union", - "raw": "'setNativeProps' | 'isFocused' | 'clear' | 'blur' | 'focus' | 'setSelection'", - "elements": [ - { - "name": "literal", - "value": "'setNativeProps'" - }, - { - "name": "literal", - "value": "'isFocused'" - }, - { - "name": "literal", - "value": "'clear'" - }, - { - "name": "literal", - "value": "'blur'" - }, - { - "name": "literal", - "value": "'focus'" - }, - { - "name": "literal", - "value": "'setSelection'" - } - ] - } - ], - "raw": "Pick<\n TextInput,\n 'setNativeProps' | 'isFocused' | 'clear' | 'blur' | 'focus' | 'setSelection'\n>" - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Searchbar.tsx" - ] - }, - "SegmentedButtons/SegmentedButtons": { - "filepath": "SegmentedButtons/SegmentedButtons.tsx", - "title": "SegmentedButtons", - "description": "Segmented buttons can be used to select options, switch views or sort elements.
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", - "link": "segmented-buttons", - "data": { - "description": "Segmented buttons can be used to select options, switch views or sort elements.
    \n\n## Usage\n```js\nimport * as React from 'react';\nimport { SafeAreaView, StyleSheet } from 'react-native';\nimport { SegmentedButtons } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('');\n\n return (\n \n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: 'center',\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "SegmentedButtons", - "methods": [], - "statics": [], - "props": { - "buttons": { - "required": true, - "tsType": { - "name": "Array", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n 'aria-label'?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}", - "signature": { - "properties": [ - { - "key": "value", - "value": { - "name": "T", - "required": true - } - }, - { - "key": "icon", - "value": { - "name": "IconSource", - "required": false - } - }, - { - "key": "disabled", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "aria-label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "checkedColor", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "uncheckedColor", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "onPress", - "value": { - "name": "signature", - "type": "function", - "raw": "(event: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "event", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - }, - "required": false - } - }, - { - "key": "label", - "value": { - "name": "string", - "required": false - } - }, - { - "key": "showSelectedCheck", - "value": { - "name": "boolean", - "required": false - } - }, - { - "key": "style", - "value": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp", - "required": false - } - }, - { - "key": "labelStyle", - "value": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp", - "required": false - } - }, - { - "key": "testID", - "value": { - "name": "string", - "required": false - } - } - ] - } - } - ], - "raw": "{\n value: T;\n icon?: IconSource;\n disabled?: boolean;\n 'aria-label'?: string;\n checkedColor?: string;\n uncheckedColor?: string;\n onPress?: (event: GestureResponderEvent) => void;\n label?: string;\n showSelectedCheck?: boolean;\n style?: StyleProp;\n labelStyle?: StyleProp;\n testID?: string;\n}[]" - }, - "description": "Buttons to display as options in toggle button.\nButton should contain the following properties:\n- `value`: value of button (required)\n- `icon`: icon to display for the item\n- `disabled`: whether the button is disabled\n- `aria-label`: accessibility label for the button. This is read by the screen reader when the user taps the button.\n- `checkedColor`: custom color for checked Text and Icon\n- `uncheckedColor`: custom color for unchecked Text and Icon\n- `onPress`: callback that is called when button is pressed\n- `label`: label text of the button\n- `showSelectedCheck`: show optional check icon to indicate selected state\n- `style`: pass additional styles for the button\n- `testID`: testID to be used on tests" - }, - "density": { - "required": false, - "tsType": { - "name": "union", - "raw": "'regular' | 'small' | 'medium' | 'high'", - "elements": [ - { - "name": "literal", - "value": "'regular'" - }, - { - "name": "literal", - "value": "'small'" - }, - { - "name": "literal", - "value": "'medium'" - }, - { - "name": "literal", - "value": "'high'" - } - ] - }, - "description": "Density is applied to the height, to allow usage in denser UIs" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/SegmentedButtons/SegmentedButtons.tsx" - ] - }, - "Snackbar": { - "filepath": "Snackbar.tsx", - "title": "Snackbar", - "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", - "link": "snackbar", - "data": { - "description": "Snackbars provide brief feedback about an operation through a message rendered at the bottom of the container in which it's wrapped.\n\nNote: To display it as a popup, regardless of the parent's position, wrap it with a `Portal` component – refer to the example in the \"More Examples` section.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport { Button, Snackbar } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [visible, setVisible] = React.useState(false);\n\n const onToggleSnackBar = () => setVisible(!visible);\n\n const onDismissSnackBar = () => setVisible(false);\n\n return (\n \n \n {\n // Do something\n },\n }}>\n Hey there! I'm a Snackbar.\n \n \n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: 'space-between',\n },\n});\n\nexport default MyComponent;\n```", - "displayName": "Snackbar", - "methods": [], - "statics": [], - "props": { - "visible": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "Whether the Snackbar is currently visible." - }, - "action": { - "required": false, - "tsType": { - "name": "intersection", - "raw": "$RemoveChildren & {\n label: string;\n}", - "elements": [ - { - "name": "$RemoveChildren", - "elements": [ - { - "name": "Button" - } - ], - "raw": "$RemoveChildren" - }, - { - "name": "signature", - "type": "object", - "raw": "{\n label: string;\n}", - "signature": { - "properties": [ - { - "key": "label", - "value": { - "name": "string", - "required": true - } - } - ] - } - } - ] - }, - "description": "Label and press callback for the action button. It should contain the following properties:\n- `label` - Label of the action button\n- `onPress` - Callback that is called when action button is pressed." - }, - "icon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "@supported Available in v5.x with theme version 3\nIcon to display when `onIconPress` is defined. Default will be `close` icon." - }, - "onIconPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on icon button press. The icon button appears only when this prop is specified." - }, - "iconAccessibilityLabel": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "@supported Available in v5.x with theme version 3\nAccessibility label for the icon button. This is read by the screen reader when the user taps the button.", - "defaultValue": { - "value": "'Close icon'", - "computed": false - } - }, - "duration": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The duration for which the Snackbar is shown.", - "defaultValue": { - "value": "7000", - "computed": false - } - }, - "onDismiss": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "() => void", - "signature": { - "arguments": [], - "return": { - "name": "void" - } - } - }, - "description": "Callback called when Snackbar is dismissed. The `visible` prop needs to be updated when this is called." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Text content of the Snackbar." - }, - "elevation": { - "required": false, - "tsType": { - "name": "Elevation" - }, - "description": "@supported Available in v5.x with theme version 3\nChanges Snackbar shadow and background on iOS and Android.", - "defaultValue": { - "value": "2", - "computed": false - } - }, - "maxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a text font can reach." - }, - "wrapperStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the wrapper of the snackbar" - }, - "contentStyle": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "Style for the content of the snackbar" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "SurfaceStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Snackbar.tsx" - ] - }, - "Surface": { - "filepath": "Surface.tsx", - "title": "Surface", - "description": "Surface is a basic container that can give depth to an element with elevation shadow.\n\nOn Android, Surface uses the native `elevation` style,\nand falls back to shadows that approximate the elevation on other platforms.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n height: 80,\n width: 80,\n padding: 8,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", - "link": "surface", - "data": { - "description": "Surface is a basic container that can give depth to an element with elevation shadow.\n\nOn Android, Surface uses the native `elevation` style,\nand falls back to shadows that approximate the elevation on other platforms.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Surface, Text } from 'react-native-paper';\nimport { StyleSheet } from 'react-native';\n\nconst MyComponent = () => (\n \n Surface\n \n);\n\nexport default MyComponent;\n\nconst styles = StyleSheet.create({\n surface: {\n height: 80,\n width: 80,\n padding: 8,\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n```", - "displayName": "Surface", - "methods": [], - "statics": [], - "props": { - "backgroundColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Background color of the Surface. Overrides the color derived from\n`elevation`." - }, - "borderRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of every corner of the Surface." - }, - "borderBottomEndRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the bottom-end corner of the Surface." - }, - "borderBottomLeftRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the bottom-left corner of the Surface." - }, - "borderBottomRightRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the bottom-right corner of the Surface." - }, - "borderBottomStartRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the bottom-start corner of the Surface." - }, - "borderEndEndRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the end-end corner of the Surface." - }, - "borderEndStartRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the end-start corner of the Surface." - }, - "borderStartEndRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the start-end corner of the Surface." - }, - "borderStartStartRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the start-start corner of the Surface." - }, - "borderTopEndRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the top-end corner of the Surface." - }, - "borderTopLeftRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the top-left corner of the Surface." - }, - "borderTopRightRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the top-right corner of the Surface." - }, - "borderTopStartRadius": { - "required": false, - "tsType": { - "name": "Extract['borderRadius']", - "raw": "Extract<\n AnimatedStyle>>,\n Record\n>[Key]" - }, - "description": "Radius of the top-start corner of the Surface." - }, - "borderCurve": { - "required": false, - "tsType": { - "name": "ViewStyle['borderCurve']", - "raw": "ViewStyle['borderCurve']" - }, - "description": "Corner curve of the Surface on iOS.", - "defaultValue": { - "value": "'continuous'", - "computed": false - } - }, - "transitionDuration": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Duration of the background, elevation, and shadow transitions in\nmilliseconds." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "Omit", - "elements": [ - { - "name": "ViewStyle" - }, - { - "name": "union", - "raw": "keyof SurfaceVisualProps | 'elevation'", - "elements": [ - { - "name": "unknown" - }, - { - "name": "literal", - "value": "'elevation'" - } - ] - } - ], - "raw": "Omit" - } - ], - "raw": "AnimatedStyle<\n Omit\n>" - } - ], - "raw": "StyleProp" - }, - "description": "Style of the Surface.\n\nThis doesn't support all View style properties:\n- Background color and border radius should be specified via props instead.\n- `overflow: 'hidden'` is not supported with `elevation` as it can clip the shadow.\n To achieve the same effect, wrap the content in a child View with the overflow style." - }, - "elevation": { - "required": false, - "tsType": { - "name": "Elevation" - }, - "description": "@supported Available in v5.x with theme version 3\nChanges shadows and background on iOS and Android.\nUsed to create UI hierarchy between components.\n\nNote: If `mode` is set to `flat`, Surface doesn't have a shadow.\n\nNote: In version 2 the `elevation` prop was accepted via `style` prop i.e. `style={{ elevation: 4 }}`.\nIt's no longer supported with theme version 3 and you should use `elevation` property instead.", - "defaultValue": { - "value": "1", - "computed": false - } - }, - "mode": { - "required": false, - "tsType": { - "name": "union", - "raw": "'flat' | 'elevated'", - "elements": [ - { - "name": "literal", - "value": "'flat'" - }, - { - "name": "literal", - "value": "'elevated'" - } - ] - }, - "description": "@supported Available in v5.x with theme version 3\nMode of the Surface.\n- `elevated` - Surface with a shadow and background color corresponding to set `elevation` value.\n- `flat` - Surface without a shadow, with the background color corresponding to set `elevation` value.", - "defaultValue": { - "value": "'elevated'", - "computed": false - } - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "Content of the `Surface`." - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "TestID used for testing purposes" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Surface.tsx" - ] - }, - "Switch/Switch": { - "filepath": "Switch/Switch.tsx", - "title": "Switch", - "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track + icon / selected handle\n- `primaryContainer`: selected handle on hover, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", - "link": "switch", - "data": { - "description": "Material 3 toggle between two mutually exclusive states (on / off).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Switch } from 'react-native-paper';\n\nconst Example = () => {\n const [on, setOn] = React.useState(false);\n return ;\n};\n```\n\n## Theming\nCustomize by overriding these `theme.colors` roles:\n- `primary` / `onPrimary`: selected track + icon / selected handle\n- `primaryContainer`: selected handle on hover, press\n- `surfaceContainerHighest`: unselected track + icon\n- `outline`: unselected resting handle, unselected track outline\n- `onSurfaceVariant`: unselected handle on hover, press\n- `onSurface`: disabled track, handle, and icon fills\n- `surface`: disabled selected handle\n- `secondary`: focus indicator", - "displayName": "Switch", - "methods": [], - "statics": [], - "props": { - "value": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the switch is on." - }, - "onValueChange": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: boolean) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "boolean" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Called with the new value when the user toggles the switch." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Disables interaction and renders the disabled visual state." - }, - "checkedIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon shown inside the handle when checked" - }, - "uncheckedIcon": { - "required": false, - "tsType": { - "name": "IconSource" - }, - "description": "Icon shown inside the handle when unchecked." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the switch. This is read by the screen reader when the user focuses the switch." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Switch/Switch.tsx" - ] - }, - "TextInput/TextInput": { - "filepath": "TextInput/TextInput.tsx", - "title": "TextInput", - "description": "A text input lets users enter and edit text. It shows an optional floating label,\nsupports `filled` and `outlined` variants, optional supporting text (including\nerror state), and start/end accessories.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (accessoryProps) => (\n \n );\n\n const clearAccessory = ({ style, disabled }) => (\n setText('')}\n role=\"button\"\n aria-label=\"Clear text\"\n >\n \n \n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", - "link": "text-input", - "data": { - "description": "A text input lets users enter and edit text. It shows an optional floating label,\nsupports `filled` and `outlined` variants, optional supporting text (including\nerror state), and start/end accessories.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (accessoryProps) => (\n \n );\n\n const clearAccessory = ({ style, disabled }) => (\n setText('')}\n role=\"button\"\n aria-label=\"Clear text\"\n >\n \n \n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends TextInput props https://reactnative.dev/docs/textinput#props", - "displayName": "TextInput", - "methods": [], - "statics": [], - "props": { - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "Pick", - "elements": [ - { - "name": "NativeTextInput" - }, - { - "name": "union", - "raw": "'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps' | 'setSelection'", - "elements": [ - { - "name": "literal", - "value": "'focus'" - }, - { - "name": "literal", - "value": "'clear'" - }, - { - "name": "literal", - "value": "'blur'" - }, - { - "name": "literal", - "value": "'isFocused'" - }, - { - "name": "literal", - "value": "'setNativeProps'" - }, - { - "name": "literal", - "value": "'setSelection'" - } - ] - } - ], - "raw": "Pick<\n NativeTextInput,\n 'focus' | 'clear' | 'blur' | 'isFocused' | 'setNativeProps' | 'setSelection'\n>" - } - ] - }, - "description": "Imperative handle exposing a subset of native `TextInput` methods\nwith side-effect handling (e.g. `clear()` syncs internal state and animations)." - }, - "variant": { - "required": false, - "tsType": { - "name": "union", - "raw": "'filled' | 'outlined'", - "elements": [ - { - "name": "literal", - "value": "'filled'" - }, - { - "name": "literal", - "value": "'outlined'" - } - ] - }, - "description": "Determines the visual style of the text input.\n\n- `filled` — filled background with an animated underline; higher visual emphasis.\n- `outlined` — stroke outline only; lower visual emphasis." - }, - "error": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "When `true`, the field uses error styling and replaces the trailing accessory\nwith an error indicator when no `endAccessory` is provided." - }, - "label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "The label text to display above the input." - }, - "supportingText": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Supporting text to display below the input (Material Design 3)." - }, - "counter": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "When `true`, displays a character counter below the input on the trailing\nside, showing `currentLength/maxLength`. Requires `maxLength` to be set." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "This is separate from `editable={false}`, which makes the text read-only while the\ninput can still be focused and text selected." - }, - "prefix": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "A short text string displayed at the start of the input (e.g. `\"$\"`)." - }, - "suffix": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "A short text string displayed at the end of the input (e.g. `\"/100\"`)." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "startAccessory": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: TextInputAccessoryProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "TextInputAccessoryProps" - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "An optional component to render on the start side of the input (leading in LTR).\nCan be a custom component or `TextInput.Icon`." - }, - "endAccessory": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: TextInputAccessoryProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "TextInputAccessoryProps" - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "An optional component to render on the end side of the input (trailing in LTR).\nCan be a custom component or `TextInput.Icon`." - }, - "render": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(props: TextInputRenderProps) => React.ReactNode", - "signature": { - "arguments": [ - { - "name": "props", - "type": { - "name": "intersection", - "raw": "React.ComponentPropsWithoutRef<\n typeof NativeTextInput\n> & {\n ref?: React.RefObject;\n}", - "elements": [ - { - "name": "ReactComponentPropsWithoutRef", - "raw": "React.ComponentPropsWithoutRef<\n typeof NativeTextInput\n>", - "elements": [ - { - "name": "NativeTextInput" - } - ] - }, - { - "name": "signature", - "type": "object", - "raw": "{\n ref?: React.RefObject;\n}", - "signature": { - "properties": [ - { - "key": "ref", - "value": { - "name": "ReactRefObject", - "raw": "React.RefObject", - "elements": [ - { - "name": "union", - "raw": "NativeTextInput | null", - "elements": [ - { - "name": "NativeTextInput" - }, - { - "name": "null" - } - ] - } - ], - "required": false - } - } - ] - } - } - ] - } - } - ], - "return": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - } - }, - "description": "Callback to render a custom input component in place of the native `TextInput`.\nReceives all props that would be passed to `TextInput`, allowing integration\nwith third-party inputs such as masked inputs." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/TextInput/TextInput.tsx" - ] - }, - "TextInput/TextInputIcon": { - "filepath": "TextInput/TextInputIcon.tsx", - "title": "TextInput.Icon", - "description": "A component to render a leading / trailing icon in the TextInput\n(return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as\n`TextInputAccessoryProps`, which TextInput passes into those render props.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (props) => (\n \n );\n\n const clearAccessory = (props) => (\n setText('')} />\n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends IconButton props https://callstack.github.io/react-native-paper/docs/components/IconButton", - "link": "text-input-icon", - "data": { - "description": "A component to render a leading / trailing icon in the TextInput\n(return it from `startAccessory` or `endAccessory`). Accepts icon-specific props as well as\n`TextInputAccessoryProps`, which TextInput passes into those render props.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { TextInput } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [text, setText] = React.useState('');\n\n const searchAccessory = (props) => (\n \n );\n\n const clearAccessory = (props) => (\n setText('')} />\n );\n\n return (\n \n );\n};\n\nexport default MyComponent;\n```\n\n@extends IconButton props https://callstack.github.io/react-native-paper/docs/components/IconButton", - "displayName": "TextInput.Icon", - "methods": [], - "statics": [], - "props": { - "style": { - "required": true, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "multiline": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "" - }, - "disabled": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "" - }, - "error": { - "required": true, - "tsType": { - "name": "boolean" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/TextInput/TextInputIcon.tsx" - ], - "group": "TextInput" - }, - "ToggleButton/ToggleButton": { - "filepath": "ToggleButton/ToggleButton.tsx", - "title": "ToggleButton", - "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", - "link": "toggle-button", - "data": { - "description": "Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons,\na group should share a common container.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst ToggleButtonExample = () => {\n const [status, setStatus] = React.useState('checked');\n\n const onButtonToggle = value => {\n setStatus(status === 'checked' ? 'unchecked' : 'checked');\n };\n\n return (\n \n );\n};\n\nexport default ToggleButtonExample;\n\n```", - "displayName": "ToggleButton", - "methods": [], - "statics": [], - "props": { - "icon": { - "required": true, - "tsType": { - "name": "IconSource" - }, - "description": "Icon to display for the `ToggleButton`." - }, - "size": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Size of the icon." - }, - "iconColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Custom text color for button." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether the button is disabled." - }, - "aria-label": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value?: GestureResponderEvent | string) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "union", - "raw": "GestureResponderEvent | string", - "elements": [ - { - "name": "GestureResponderEvent" - }, - { - "name": "string" - } - ] - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press." - }, - "value": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Value of button." - }, - "status": { - "required": false, - "tsType": { - "name": "union", - "raw": "'checked' | 'unchecked'", - "elements": [ - { - "name": "literal", - "value": "'checked'" - }, - { - "name": "literal", - "value": "'unchecked'" - } - ] - }, - "description": "Status of button." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "AnimatedStyle", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "AnimatedStyle" - } - ], - "raw": "StyleProp>" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "testID": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "testID to be used on tests." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButton.tsx" - ] - }, - "ToggleButton/ToggleButtonGroup": { - "filepath": "ToggleButton/ToggleButtonGroup.tsx", - "title": "ToggleButton.Group", - "description": "Toggle group allows to control a group of toggle buttons.
    \nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "link": "toggle-button-group", - "data": { - "description": "Toggle group allows to control a group of toggle buttons.
    \nIt doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow).\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)}\n value={value}\n >\n \n \n \n );\n};\n\nexport default MyComponent;\n```", - "displayName": "ToggleButton.Group", - "methods": [], - "statics": [], - "props": { - "onValueChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: Value) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "Value" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "value": { - "required": true, - "tsType": { - "name": "union", - "raw": "Value | null", - "elements": [ - { - "name": "Value" - }, - { - "name": "null" - } - ] - }, - "description": "Value of the currently selected toggle button." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing toggle buttons." - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButtonGroup.tsx" - ], - "group": "ToggleButton" - }, - "ToggleButton/ToggleButtonRow": { - "filepath": "ToggleButton/ToggleButtonRow.tsx", - "title": "ToggleButton.Row", - "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", - "link": "toggle-button-row", - "data": { - "description": "Toggle button row renders a group of toggle buttons in a row.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { ToggleButton } from 'react-native-paper';\n\nconst MyComponent = () => {\n const [value, setValue] = React.useState('left');\n\n return (\n setValue(value)} value={value}>\n \n \n \n );\n};\n\nexport default MyComponent;\n\n```", - "displayName": "ToggleButton.Row", - "methods": [], - "statics": [], - "props": { - "onValueChange": { - "required": true, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(value: string) => void", - "signature": { - "arguments": [ - { - "name": "value", - "type": { - "name": "string" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on selection change." - }, - "value": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Value of the currently selected toggle button." - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "React elements containing toggle buttons." - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/ToggleButton/ToggleButtonRow.tsx" - ], - "group": "ToggleButton" - }, - "Tooltip/Tooltip": { - "filepath": "Tooltip/Tooltip.tsx", - "title": "Tooltip", - "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "link": "tooltip", - "data": { - "description": "Tooltips display informative text when users hover over, focus on, or tap an element.\n\nPlain tooltips, when activated, display a text label identifying an element, such as a description of its function. Tooltips should include only short, descriptive text and avoid restating visible UI text.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { IconButton, Tooltip } from 'react-native-paper';\n\nconst MyComponent = () => (\n \n {}} />\n \n);\n\nexport default MyComponent;\n```", - "displayName": "Tooltip", - "methods": [], - "statics": [], - "props": { - "children": { - "required": true, - "tsType": { - "name": "ReactReactElement", - "raw": "React.ReactElement" - }, - "description": "Tooltip reference element. Needs to be able to hold a ref." - }, - "enterTouchDelay": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds a user must touch the element before showing the tooltip.", - "defaultValue": { - "value": "500", - "computed": false - } - }, - "leaveTouchDelay": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "The number of milliseconds after the user stops touching an element before hiding the tooltip.", - "defaultValue": { - "value": "1500", - "computed": false - } - }, - "title": { - "required": true, - "tsType": { - "name": "string" - }, - "description": "Tooltip title" - }, - "titleMaxFontSizeMultiplier": { - "required": false, - "tsType": { - "name": "number" - }, - "description": "Specifies the largest possible scale a title font can reach." - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Tooltip/Tooltip.tsx" - ] - }, - "TouchableRipple/TouchableRipple": { - "filepath": "TouchableRipple/TouchableRipple.tsx", - "title": "TouchableRipple", - "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n \n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", - "link": "touchable-ripple", - "data": { - "description": "A wrapper for views that should respond to touches.\nProvides a material \"ink ripple\" interaction effect for supported platforms (>= Android Lollipop).\nOn unsupported platforms, it falls back to a highlight effect.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { View } from 'react-native';\nimport { Text, TouchableRipple } from 'react-native-paper';\n\nconst MyComponent = () => (\n console.log('Pressed')}\n rippleColor=\"rgba(0, 0, 0, .32)\"\n >\n Press anywhere\n \n);\n\nexport default MyComponent;\n```\n\n@extends Pressable props https://reactnative.dev/docs/Pressable#props", - "displayName": "TouchableRipple", - "methods": [], - "statics": [], - "props": { - "borderless": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to render the ripple outside the view bounds.", - "defaultValue": { - "value": "false", - "computed": false - } - }, - "background": { - "required": false, - "tsType": { - "name": "Object" - }, - "description": "Type of background drawabale to display the feedback (Android).\nhttps://reactnative.dev/docs/pressable#rippleconfig" - }, - "centered": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to start the ripple at the center (Web)." - }, - "disabled": { - "required": false, - "tsType": { - "name": "boolean" - }, - "description": "Whether to prevent interaction with the touchable." - }, - "onPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on press. If not set, will cause the touchable to be disabled." - }, - "onLongPress": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute on long press." - }, - "onPressIn": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute immediately when a touch is engaged, before `onPressOut` and `onPress`." - }, - "onPressOut": { - "required": false, - "tsType": { - "name": "signature", - "type": "function", - "raw": "(e: GestureResponderEvent) => void", - "signature": { - "arguments": [ - { - "name": "e", - "type": { - "name": "GestureResponderEvent" - } - } - ], - "return": { - "name": "void" - } - } - }, - "description": "Function to execute when a touch is released." - }, - "rippleColor": { - "required": false, - "tsType": { - "name": "ColorValue" - }, - "description": "Color of the ripple effect (Android >= 5.0 and Web)." - }, - "underlayColor": { - "required": false, - "tsType": { - "name": "string" - }, - "description": "Color of the underlay for the highlight effect (Android < 5.0 and iOS)." - }, - "children": { - "required": true, - "tsType": { - "name": "union", - "raw": "| ((state: PressableStateCallbackType) => React.ReactNode)\n| React.ReactNode", - "elements": [ - { - "name": "unknown" - }, - { - "name": "ReactReactNode", - "raw": "React.ReactNode" - } - ] - }, - "description": "Content of the `TouchableRipple`." - }, - "style": { - "required": false, - "tsType": { - "name": "union", - "raw": "| StyleProp\n| ((state: PressableStateCallbackType) => StyleProp)\n| undefined", - "elements": [ - { - "name": "StyleProp", - "elements": [ - { - "name": "ViewStyle" - } - ], - "raw": "StyleProp" - }, - { - "name": "unknown" - }, - { - "name": "undefined" - } - ] - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "View" - } - ] - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/TouchableRipple/TouchableRipple.tsx" - ] - }, - "Typography/Text": { - "filepath": "Typography/Text.tsx", - "title": "Text", - "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", - "link": "text", - "data": { - "description": "Typography component showing styles complied with passed `variant` prop and supported by the type system.\n\n## Usage\n```js\nimport * as React from 'react';\nimport { Text } from 'react-native-paper';\n\nconst MyComponent = () => (\n <>\n Display Large\n Display Medium\n Display small\n\n Headline Large\n Headline Medium\n Headline Small\n\n Title Large\n Title Medium\n Title Small\n\n Body Large\n Body Medium\n Body Small\n\n Label Large\n Label Medium\n Label Small\n \n);\n\nexport default MyComponent;\n```\n\n@extends Text props https://reactnative.dev/docs/text#props", - "displayName": "Text", - "methods": [], - "statics": [], - "props": { - "variant": { - "required": false, - "tsType": { - "name": "VariantProp", - "elements": [ - { - "name": "T" - } - ], - "raw": "VariantProp" - }, - "description": "@supported Available in v5.x with theme version 3\n\nVariant defines appropriate text styles for type role and its size.\nAvailable variants:\n\n Display: `displayLarge`, `displayMedium`, `displaySmall`\n\n Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`\n\n Title: `titleLarge`, `titleMedium`, `titleSmall`\n\n Label: `labelLarge`, `labelMedium`, `labelSmall`\n\n Body: `bodyLarge`, `bodyMedium`, `bodySmall`" - }, - "children": { - "required": true, - "tsType": { - "name": "ReactReactNode", - "raw": "React.ReactNode" - }, - "description": "" - }, - "theme": { - "required": false, - "tsType": { - "name": "ThemeProp" - }, - "description": "" - }, - "style": { - "required": false, - "tsType": { - "name": "StyleProp", - "elements": [ - { - "name": "TextStyle" - } - ], - "raw": "StyleProp" - }, - "description": "" - }, - "ref": { - "required": false, - "tsType": { - "name": "ReactRef", - "raw": "React.Ref", - "elements": [ - { - "name": "signature", - "type": "object", - "raw": "{\n setNativeProps(args: Object): void;\n}", - "signature": { - "properties": [ - { - "key": "setNativeProps", - "value": { - "name": "void", - "required": true - } - } - ] - } - } - ] - }, - "description": "" - } - } - }, - "type": "component", - "dependencies": [ - "src/components/Typography/Text.tsx" - ] - } - } -} diff --git a/docs/src/pages/index.mdx b/docs/src/pages/index.mdx deleted file mode 100644 index 91f12670f9..0000000000 --- a/docs/src/pages/index.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -pageType: home -hero: - name: Cross-platform Material Design for React Native - tagline: Paper is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. - actions: - - text: Get started - link: /docs/guides/getting-started - theme: brand - - text: Try on Snack - link: https://snack.expo.dev/@react-native-paper/react-native-paper-example_v5 - theme: alt -features: - - title: Production-ready components - details: Buttons, inputs, lists, cards, menus, dialogs, navigation, and feedback patterns built for day-to-day product work. - icon: /react-native-paper/images/home/components.svg - span: 4 - - title: Material You theming - details: Adopt Material 3 color, typography, elevation, and state layers with a theme system designed for real apps. - icon: /react-native-paper/images/home/palette.svg - span: 4 - - title: Accessible by default - details: Common interactions, contrast, and state handling are already baked into the component surface instead of left to each screen. - icon: /react-native-paper/images/home/accessibility.svg - span: 4 - - title: Cross-platform parity - details: Keep design language and component behavior aligned across iOS, Android, and React Native Web from one codebase. - icon: /react-native-paper/images/home/devices.svg - span: 4 - - title: Dark mode support - details: Ship light and dark experiences with the same primitives, including adaptive surfaces and semantic color roles. - icon: /react-native-paper/images/home/moon.svg - span: 4 - - title: Migration-friendly docs - details: Browse stable 5.x, preview 6.x, and legacy static docs from one version selector without splitting the docs experience. - icon: /react-native-paper/images/home/versions.svg - span: 4 ---- diff --git a/eslint.config.mjs b/eslint.config.mjs index 7c2aeb73a3..ac6e65951f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -28,18 +28,12 @@ export default defineConfig( 'example/dist/', // generated docs + 'docs/5.x/component-docs.json', 'docs/5.x/_nav.json', - 'docs/5.x/index.mdx', 'docs/5.x/docs/_meta.json', 'docs/5.x/docs/guides/_meta.json', - 'docs/5.x/docs/components/_meta.json', - 'docs/5.x/docs/components/**/_meta.json', - 'docs/6.x/_nav.json', - 'docs/6.x/index.mdx', - 'docs/6.x/docs/_meta.json', - 'docs/6.x/docs/guides/_meta.json', + 'docs/5.x/docs/components/', 'docs/6.x/docs/components/', - 'docs/src/data/componentDocs6x.json', // generated by bob 'lib/', diff --git a/scripts/generate-component-docs.ts b/scripts/generate-component-docs.ts deleted file mode 100644 index a3f5a177dd..0000000000 --- a/scripts/generate-component-docs.ts +++ /dev/null @@ -1,276 +0,0 @@ -import * as childProcess from 'node:child_process'; -import * as fs from 'node:fs'; -import { createRequire } from 'node:module'; -import * as os from 'node:os'; -import * as path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import * as vm from 'node:vm'; - -type PluginOptions = { - docsRootDir: string; - libsRootDir: string; - pages: unknown; -}; - -type PluginFactory = ( - context: unknown, - options: PluginOptions -) => Promise<{ - loadContent: () => Promise; -}>; - -const isRecord = (value: unknown): value is { [key: string]: unknown } => - typeof value === 'object' && value !== null; - -const isPluginOptions = (value: unknown): value is PluginOptions => { - if (!isRecord(value)) { - return false; - } - - return ( - typeof value.docsRootDir === 'string' && - typeof value.libsRootDir === 'string' && - 'pages' in value - ); -}; - -const isPluginFactory = (value: unknown): value is PluginFactory => - typeof value === 'function'; - -const findObjectEnd = (source: string, start: number) => { - let depth = 0; - - for (let index = start; index < source.length; index++) { - const char = source[index]; - - if (char === '{') { - depth++; - } - - if (char === '}') { - depth--; - } - - if (depth === 0) { - return index + 1; - } - } - - return -1; -}; - -const loadLegacyPluginConfig = ( - docusaurusConfigPath: string -): PluginOptions => { - const source = fs.readFileSync(docusaurusConfigPath, 'utf8'); - const pluginPathIndex = source.indexOf("'./component-docs-plugin'"); - const optionsStart = source.indexOf('{', pluginPathIndex); - const optionsEnd = findObjectEnd(source, optionsStart); - - if (pluginPathIndex === -1 || optionsStart === -1 || optionsEnd === -1) { - throw new Error( - `Unable to read component docs plugin options from ${docusaurusConfigPath}` - ); - } - - const optionsSource = source.slice(optionsStart, optionsEnd); - const pluginOptions: unknown = vm.runInNewContext(`(${optionsSource})`, { - __dirname: path.dirname(docusaurusConfigPath), - path, - }); - - if (!isPluginOptions(pluginOptions)) { - throw new Error( - `Unable to read component docs plugin options from ${docusaurusConfigPath}` - ); - } - - return pluginOptions; -}; - -const loadPluginConfig = ( - sourceDir: string, - requireFromScript: ReturnType -): PluginOptions => { - const sharedConfigPath = path.join( - sourceDir, - 'docs', - 'component-docs.config.js' - ); - - if (fs.existsSync(sharedConfigPath)) { - const sharedConfig: unknown = requireFromScript(sharedConfigPath); - - if (!isPluginOptions(sharedConfig)) { - throw new Error( - `Unable to read component docs config from ${sharedConfigPath}` - ); - } - - return sharedConfig; - } - - const docusaurusConfigPath = path.join( - sourceDir, - 'docs', - 'docusaurus.config.js' - ); - - return loadLegacyPluginConfig(docusaurusConfigPath); -}; - -const writeDocusaurusConfigStub = (sourceDir: string) => { - fs.writeFileSync( - path.join(sourceDir, 'docs', 'docusaurus.config.js'), - `module.exports = { - baseUrl: '/react-native-paper/', - customFields: { - moreExamples: {}, - knownIssues: {}, - themeColors: {}, - screenshots: {}, - extendedExamples: {}, - }, -}; -` - ); -}; - -const normalizeDocs = (value: unknown, sourceDir: string): unknown => { - if (Array.isArray(value)) { - return value.map((item) => normalizeDocs(item, sourceDir)); - } - - if (!isRecord(value)) { - return value; - } - - return Object.fromEntries( - Object.entries(value).map(([key, item]) => { - if (key !== 'dependencies' || !Array.isArray(item)) { - return [key, normalizeDocs(item, sourceDir)]; - } - - return [ - key, - item.map((dependency) => { - if (typeof dependency !== 'string' || !path.isAbsolute(dependency)) { - return dependency; - } - - const realDependency = fs.existsSync(dependency) - ? fs.realpathSync(dependency) - : dependency; - const relativeDependency = path.relative(sourceDir, realDependency); - - if ( - relativeDependency.startsWith('..') || - path.isAbsolute(relativeDependency) - ) { - return dependency; - } - - return relativeDependency.split(path.sep).join('/'); - }), - ]; - }) - ); -}; - -const writeJson = (destination: string, value: unknown) => { - fs.mkdirSync(path.dirname(destination), { recursive: true }); - fs.writeFileSync(destination, `${JSON.stringify(value, null, 2)}\n`); -}; - -const main = async () => { - const [branchName, outputPath = 'docs/src/data/componentDocs5x.json'] = - process.argv.slice(2); - - if (!branchName) { - console.error( - 'Usage: node scripts/generate-component-docs.ts [output]' - ); - process.exitCode = 1; - return; - } - - const requireFromScript = createRequire(import.meta.url); - const rootDir = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - '..' - ); - const tempDir = fs.mkdtempSync( - path.join(os.tmpdir(), 'react-native-paper-docs-') - ); - - try { - const archivePath = path.join(tempDir, 'source.tar'); - const sourceDir = path.join(tempDir, 'source'); - - childProcess.execFileSync( - 'git', - ['rev-parse', '--verify', `${branchName}^{commit}`], - { - cwd: rootDir, - stdio: 'ignore', - } - ); - childProcess.execFileSync( - 'git', - ['archive', '--format=tar', '--output', archivePath, branchName], - { - cwd: rootDir, - stdio: 'inherit', - } - ); - - fs.mkdirSync(sourceDir); - childProcess.execFileSync('tar', ['-xf', archivePath, '-C', sourceDir], { - cwd: rootDir, - stdio: 'inherit', - }); - - const nodeModulesPath = path.join(rootDir, 'docs', 'node_modules'); - const archivedNodeModulesPath = path.join( - sourceDir, - 'docs', - 'node_modules' - ); - - if (fs.existsSync(nodeModulesPath)) { - fs.symlinkSync(nodeModulesPath, archivedNodeModulesPath, 'dir'); - } - - const pluginOptions = loadPluginConfig(sourceDir, requireFromScript); - - writeDocusaurusConfigStub(sourceDir); - - const pluginFactoryPath = path.join( - sourceDir, - 'docs', - 'component-docs-plugin' - ); - const pluginFactory: unknown = requireFromScript(pluginFactoryPath); - - if (!isPluginFactory(pluginFactory)) { - throw new Error( - `Unable to read component docs plugin from ${pluginFactoryPath}` - ); - } - - const plugin = await pluginFactory({}, pluginOptions); - const docs = await plugin.loadContent(); - const destination = path.resolve(rootDir, outputPath); - - writeJson(destination, { - docs: normalizeDocs(docs, fs.realpathSync(sourceDir)), - }); - } catch (error) { - console.error(error instanceof Error ? error.message : String(error)); - process.exitCode = 1; - } finally { - fs.rmSync(tempDir, { recursive: true, force: true }); - } -}; - -void main(); diff --git a/src/components/SegmentedButtons/SegmentedButtons.tsx b/src/components/SegmentedButtons/SegmentedButtons.tsx index c5fceeaeae..5a17f553ce 100644 --- a/src/components/SegmentedButtons/SegmentedButtons.tsx +++ b/src/components/SegmentedButtons/SegmentedButtons.tsx @@ -15,7 +15,7 @@ import type { IconSource } from '../Icon'; type ConditionalValue = | { /** - * Array of the currently selected segmented button values. + * Currently selected segmented button value or values. */ value: T[]; /** @@ -29,7 +29,7 @@ type ConditionalValue = } | { /** - * Value of the currently selected segmented button. + * Currently selected segmented button value or values. */ value: T; /** diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx index 5a7a65d974..10ac46a67a 100644 --- a/src/components/TextInput/TextInput.tsx +++ b/src/components/TextInput/TextInput.tsx @@ -292,25 +292,22 @@ const defaultRenderer = (props: TextInputRenderProps) => ( * * @extends TextInput props https://reactnative.dev/docs/textinput#props */ -function TextInput(props: TextInputProps) { - /* eslint-disable @typescript-eslint/no-unused-vars -- peel TextInput-only props before NativeTextInput spread */ - const { - ref, - error, - label, - supportingText, - variant, - theme, - prefix, - suffix, - counter, - disabled, - startAccessory, - endAccessory, - render = defaultRenderer, - ...textInputProps - } = props; - +function TextInput({ + ref, + error, + label, + supportingText, + variant, + theme, + prefix, + suffix, + counter, + disabled, + startAccessory, + endAccessory, + render = defaultRenderer, + ...rest +}: TextInputProps) { const { input, isDisabled, @@ -346,7 +343,21 @@ function TextInput(props: TextInputProps) { onChangeText, onFocus, onBlur, - } = useTextInput(props); + } = useTextInput({ + ref, + error, + label, + supportingText, + variant, + theme, + prefix, + suffix, + counter, + disabled, + startAccessory, + endAccessory, + ...rest, + }); return ( @@ -383,7 +394,7 @@ function TextInput(props: TextInputProps) { style: leadingAccessoryStyles, error: hasError, disabled: isDisabled, - multiline: !!textInputProps.multiline, + multiline: !!rest.multiline, }) : null} @@ -396,7 +407,7 @@ function TextInput(props: TextInputProps) { cursorColor, placeholderTextColor, ...accessibilityProps.input, - ...textInputProps, + ...rest, editable: isEditable, placeholder, style: inputStyles, @@ -413,7 +424,7 @@ function TextInput(props: TextInputProps) { style: trailingAccessoryStyles, error: hasError, disabled: isDisabled, - multiline: !!textInputProps.multiline, + multiline: !!rest.multiline, }) ) : hasError ? ( diff --git a/yarn.lock b/yarn.lock index 100597d848..dcc0fa4cac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7003,21 +7003,12 @@ __metadata: languageName: node linkType: hard -"baseline-browser-mapping@npm:^2.10.12": - version: 2.10.29 - resolution: "baseline-browser-mapping@npm:2.10.29" +"baseline-browser-mapping@npm:^2.10.12, baseline-browser-mapping@npm:^2.9.0": + version: 2.11.20 + resolution: "baseline-browser-mapping@npm:2.11.20" bin: baseline-browser-mapping: dist/cli.cjs - checksum: 10c0/d629a6d87f1aa0585b85ec8bf686bf58d706836fe7827d7bd11f646db2292f2089d8dff33fdd9a1ffe4c07424ae5b08743f57d9405d45cd8ceeb145e8dfb58e5 - languageName: node - linkType: hard - -"baseline-browser-mapping@npm:^2.9.0": - version: 2.9.13 - resolution: "baseline-browser-mapping@npm:2.9.13" - bin: - baseline-browser-mapping: dist/cli.js - checksum: 10c0/0881e813472865ae5f7f19d48c9525fabf358acb43547d57b294b9b62e28821562877627f5c7818595fa583b76e01967c1395ff9525164e5905e1d8be2c71318 + checksum: 10c0/67588b7edafa4e6c52996ec4b96b6e007312961cefc5a179cb49232f6681a38ba68d4d43990081e9d0e1dcadcbd28f13105ee7dc3e43b267385d0beb14824ecc languageName: node linkType: hard @@ -7451,17 +7442,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001759": - version: 1.0.30001763 - resolution: "caniuse-lite@npm:1.0.30001763" - checksum: 10c0/4524f6ae15a18c8510849e43baed01441940c50cab29687cba62dde6354a49549d5bcb7f79b864dadf3f0c8c342038c63131cdb64af382a501593106b1d0028b - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001782": - version: 1.0.30001792 - resolution: "caniuse-lite@npm:1.0.30001792" - checksum: 10c0/03bab64b123453e18e7c6747c32bb820be6d82ac78536c5c3704988aaeec411715a2045069fb569f872d73acf1ea64bf83508c9fdfa87b6d012236cde738e1be +"caniuse-lite@npm:^1.0.30001759, caniuse-lite@npm:^1.0.30001782": + version: 1.0.30001810 + resolution: "caniuse-lite@npm:1.0.30001810" + checksum: 10c0/d29bc73f33c888025d54c7ff2984372d3b350b15a6a9e174884fb40976175e9177f202e2a72c2d428dddfc032784bc8ba635a31d168fcab9fac65756d7b3d6b4 languageName: node linkType: hard From 8fea1f00100e11af4112444db8453cdc0be1226f Mon Sep 17 00:00:00 2001 From: Kallinikos Milonakis <95851621+KallinikosMil@users.noreply.github.com> Date: Thu, 3 Sep 2026 12:07:51 +0300 Subject: [PATCH 60/94] fix: emit forward slashes in generated import paths in babel plugin on Windows (#5054) --- .gitattributes | 1 + .github/workflows/ci.yml | 14 ++++++++++---- scripts/generate-mappings.ts | 13 +++++++++++-- 3 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..6313b56c57 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 852bb6bd97..81a3255b83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,13 @@ jobs: unit-tests: name: Unit tests - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -47,8 +53,8 @@ jobs: uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ./cache/jest - key: jest-cache-${{ github.ref_name }} - restore-keys: jest-cache- + key: jest-cache-${{ runner.os }}-${{ github.ref_name }} + restore-keys: jest-cache-${{ runner.os }}- - name: Run unit tests run: yarn test --maxWorkers=2 --coverage @@ -57,7 +63,7 @@ jobs: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() with: - name: coverage + name: coverage-${{ runner.os }} path: coverage build-package: diff --git a/scripts/generate-mappings.ts b/scripts/generate-mappings.ts index b6a172e7c6..7919c2f7b0 100644 --- a/scripts/generate-mappings.ts +++ b/scripts/generate-mappings.ts @@ -2,7 +2,13 @@ import { parse } from '@babel/parser'; import * as types from '@babel/types'; import type { Identifier, StringLiteral } from '@babel/types'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; -import { dirname, join, relative as relativePath, resolve } from 'node:path'; +import { + dirname, + join, + relative as relativePath, + resolve, + sep, +} from 'node:path'; import { fileURLToPath } from 'node:url'; type PackageJson = { @@ -49,8 +55,11 @@ const ast = parse(source, { }); const index = packageJson.main; + const relative = (value: string) => - relativePath(root, resolve(root, dirname(index), value)); + relativePath(root, resolve(root, dirname(index), value)) + .split(sep) + .join('/'); const mappings = ast.program.body.reduce<{ [key: string]: Mapping }>( (acc, declaration, _index, self) => { From 526af2b260149a659e92cd993c902fb495d35c01 Mon Sep 17 00:00:00 2001 From: Bao Nguyen <39545125+giaBaoJS@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:49:32 +0700 Subject: [PATCH 61/94] fix(theme): keep flat font properties in a mixed configureFonts config (#5066) --- src/theme/__tests__/fonts.test.js | 44 ++++++++++++++++++++++++++ src/theme/fonts.tsx | 52 +++++++++++++++++++------------ 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/theme/__tests__/fonts.test.js b/src/theme/__tests__/fonts.test.js index 23e0f794ae..86d1430405 100644 --- a/src/theme/__tests__/fonts.test.js +++ b/src/theme/__tests__/fonts.test.js @@ -340,6 +340,50 @@ describe('configureFonts', () => { }); }); + it('applies flat properties to every variant when the config also has per-variant entries', () => { + mockPlatform('ios'); + const { configureFonts, typescale } = loadFonts(); + + const fonts = configureFonts({ + config: { + fontFamily: 'NotoSans', + bodyLarge: { + fontSize: 18, + }, + }, + }); + + expect(fonts).toEqual({ + ...Object.fromEntries( + Object.entries(typescale).map(([variantName, variantProperties]) => [ + variantName, + { ...variantProperties, fontFamily: 'NotoSans' }, + ]) + ), + bodyLarge: { + ...typescale.bodyLarge, + fontFamily: 'NotoSans', + fontSize: 18, + }, + }); + }); + + it('does not add flat properties of a mixed config as typescale variants', () => { + mockPlatform('ios'); + const { configureFonts } = loadFonts(); + + const fonts = configureFonts({ + config: { + fontFamily: 'NotoSans', + bodyLarge: { + fontSize: 18, + }, + }, + }); + + expect(fonts.fontFamily).toBeUndefined(); + }); + it('should be deterministic', () => { mockPlatform('ios'); const { configureFonts } = loadFonts(); diff --git a/src/theme/fonts.tsx b/src/theme/fonts.tsx index 2fe7b55626..268f4dde02 100644 --- a/src/theme/fonts.tsx +++ b/src/theme/fonts.tsx @@ -13,34 +13,41 @@ function configureFontsConfig( return typescale; } - const isFlatConfig = Object.values(config).every( - (value) => typeof value !== 'object' - ); - - if (isFlatConfig) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - return Object.fromEntries( - Object.entries(typescale).map(([variantName, variantProperties]) => [ - variantName, - { ...variantProperties, ...config }, - ]) - ) as Typescale; + // A config entry is either a whole variant (an object, e.g. `bodyLarge: { fontSize: 18 }`) + // or a single font property shared by every variant (e.g. `fontFamily: 'NotoSans'`). + // Both may appear in the same config, so they are collected separately instead of + // classifying the config as a whole. + const sharedProperties: Record = {}; + const variantOverrides: Record = {}; + + for (const [key, value] of Object.entries(config)) { + if (typeof value === 'object' && value !== null) { + variantOverrides[key] = value; + } else { + sharedProperties[key] = value; + } } const typescaleByVariant: Partial< Record > = typescale; - return Object.assign( - {}, - typescale, - ...Object.entries(config).map(([variantName, variantProperties]) => ({ - [variantName]: { + const variantNames = new Set([ + ...Object.keys(typescale), + ...Object.keys(variantOverrides), + ]); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + return Object.fromEntries( + Array.from(variantNames, (variantName) => [ + variantName, + { ...typescaleByVariant[variantName], - ...variantProperties, + ...sharedProperties, + ...variantOverrides[variantName], }, - })) - ); + ]) + ) as Typescale; } export default function configureFonts(params?: { @@ -51,6 +58,11 @@ export default function configureFonts(params?: { config?: Partial>>; }): Typescale; // eslint-disable-next-line no-redeclare +export default function configureFonts(params: { + config: Partial & + Partial>>; +}): Typescale; +// eslint-disable-next-line no-redeclare export default function configureFonts(params: { config: Record; }): Typescale & { [key: string]: TypescaleStyle }; From fedf202004ce56771b27b17a8dd90396e363aa48 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 4 Sep 2026 13:12:25 +0200 Subject: [PATCH 62/94] chore: add contributor label for callstack-internal org --- .github/workflows/contributor-label.yml | 58 ++++++++++++++++++------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/.github/workflows/contributor-label.yml b/.github/workflows/contributor-label.yml index 39e69bf3c9..7edb303d97 100644 --- a/.github/workflows/contributor-label.yml +++ b/.github/workflows/contributor-label.yml @@ -6,46 +6,74 @@ on: permissions: issues: write - pull-requests: write jobs: label: name: Add contributor label runs-on: ubuntu-latest steps: + - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + id: app-token + with: + app-id: ${{ secrets.ORG_MEMBERSHIP_APP_ID }} + private-key: ${{ secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }} + owner: callstack-internal + permission-members: read + - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + ORG_MEMBERSHIP_TOKEN: ${{ steps.app-token.outputs.token }} with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const owner = context.repo.owner; const repo = context.repo.repo; - let permission = 'none'; + const label = 'Contributor: Internal'; + + const pullRequest = context.payload.pull_request; + + if (pullRequest.labels.some(({ name }) => name === label)) { + return; + } + + const username = pullRequest.user.login; - try { - const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + const { data } = + await github.rest.repos.getCollaboratorPermissionLevel({ owner, repo, - username: context.payload.pull_request.user.login, + username, }); - permission = data.permission; - } catch (error) { - if (error.status !== 404) { - throw error; + let isInternal = + ['MEMBER', 'OWNER'].includes(pullRequest.author_association) || + ['admin', 'maintain', 'write'].includes(data.permission); + + if (!isInternal) { + try { + const internalGithub = getOctokit(process.env.ORG_MEMBERSHIP_TOKEN); + + await internalGithub.rest.orgs.checkMembershipForUser({ + org: 'callstack-internal', + username, + }); + + isInternal = true; + } catch (error) { + if (error.status !== 404) { + throw error; + } } } - if ( - !['MEMBER', 'OWNER'].includes(context.payload.pull_request.author_association) && - !['admin', 'maintain', 'write'].includes(permission) - ) { + if (!isInternal) { return; } await github.rest.issues.addLabels({ owner, repo, - issue_number: context.issue.number, - labels: ['Contributor: Internal'], + issue_number: pullRequest.number, + labels: [label], }); From 6fa2a561ba20a1cf3c81f92e1c713db06373c8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Fedyna?= Date: Fri, 4 Sep 2026 13:24:56 +0200 Subject: [PATCH 63/94] refactor: inline Platform.OS checks (#5087) --- example/src/DrawerItems.tsx | 7 +++---- example/src/Examples/CardExample.tsx | 7 +++---- .../Examples/Dialogs/DialogWithLoadingIndicator.tsx | 4 +--- example/src/Examples/TooltipExample.tsx | 3 +-- example/utils/index.ts | 2 -- src/components/ProgressBar.tsx | 7 +++---- src/components/Tooltip/Tooltip.tsx | 10 ++++------ 7 files changed, 15 insertions(+), 25 deletions(-) diff --git a/example/src/DrawerItems.tsx b/example/src/DrawerItems.tsx index 94afa3136c..49bdd5f99d 100644 --- a/example/src/DrawerItems.tsx +++ b/example/src/DrawerItems.tsx @@ -17,7 +17,7 @@ import { useTheme, } from 'react-native-paper'; -import { dynamicThemeSupported, isWeb } from '../utils'; +import { dynamicThemeSupported } from '../utils'; import { PreferencesContext } from './PreferencesContext'; const DrawerItemsData = [ @@ -92,7 +92,6 @@ function DrawerItems() { const _setDrawerItem = (index: number) => setDrawerItemIndex(index); const { colors } = useTheme(); - const isIOS = Platform.OS === 'ios'; const expoGoExecution = Constants.executionEnvironment === ExecutionEnvironment.StoreClient; @@ -114,7 +113,7 @@ function DrawerItems() { } = preferences; const _handleToggleRTL = () => { - if (!isWeb && expoGoExecution) { + if (Platform.OS !== 'web' && expoGoExecution) { setShowRTLDialog(true); return; } @@ -222,7 +221,7 @@ function DrawerItems() { - {isIOS ? 'Highlight' : 'Ripple'} effect * + {Platform.OS === 'ios' ? 'Highlight' : 'Ripple'} effect * diff --git a/example/src/Examples/CardExample.tsx b/example/src/Examples/CardExample.tsx index 1061d4f904..016bef3e21 100644 --- a/example/src/Examples/CardExample.tsx +++ b/example/src/Examples/CardExample.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Alert, ScrollView, StyleSheet, View } from 'react-native'; +import { Alert, Platform, ScrollView, StyleSheet, View } from 'react-native'; import { Avatar, @@ -11,7 +11,6 @@ import { useTheme, } from 'react-native-paper'; -import { isWeb } from '../../utils'; import { PreferencesContext } from '../PreferencesContext'; import ScreenWrapper from '../ScreenWrapper'; @@ -151,7 +150,7 @@ const CardExample = () => { { - isWeb + Platform.OS === 'web' ? alert('The Chameleon is Pressed') : Alert.alert('The Chameleon is Pressed'); }} @@ -168,7 +167,7 @@ const CardExample = () => { { - isWeb + Platform.OS === 'web' ? alert('The City is Long Pressed') : Alert.alert('The City is Long Pressed'); }} diff --git a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx index a28620e79c..d854dcb587 100644 --- a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx +++ b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx @@ -4,8 +4,6 @@ import { Dialog, Palette, Portal } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; -const isIOS = Platform.OS === 'ios'; - const DialogWithLoadingIndicator = ({ visible, close, @@ -21,7 +19,7 @@ const DialogWithLoadingIndicator = ({ Loading..... diff --git a/example/src/Examples/TooltipExample.tsx b/example/src/Examples/TooltipExample.tsx index 8e0802d4a4..325e0f0113 100644 --- a/example/src/Examples/TooltipExample.tsx +++ b/example/src/Examples/TooltipExample.tsx @@ -15,7 +15,6 @@ import { Card, } from 'react-native-paper'; -import { isWeb } from '../../utils'; import ScreenWrapper from '../ScreenWrapper'; const MORE_ICON = Platform.OS === 'ios' ? 'dots-horizontal' : 'dots-vertical'; @@ -68,7 +67,7 @@ const TooltipExample = () => { A tooltip is displayed upon - {!isWeb + {Platform.OS !== 'web' ? ' tapping and holding a screen element or component' : ' hovering over a screen element or component'} . Continuously display the tooltip as long as the user long-presses or diff --git a/example/utils/index.ts b/example/utils/index.ts index 00f9f22bd6..f8c9675e41 100644 --- a/example/utils/index.ts +++ b/example/utils/index.ts @@ -60,8 +60,6 @@ export function inputReducer( } } -export const isWeb = Platform.OS === 'web'; - export const animatedFABExampleData = [ { id: '1', diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index 34b69bba3d..54c62038b4 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -80,7 +80,6 @@ const ProgressBar = ({ testID = 'progress-bar', ...rest }: Props) => { - const isWeb = Platform.OS === 'web'; const theme = useInternalTheme(themeOverrides); const { direction } = useLocale(); const isRTL = direction === 'rtl'; @@ -132,7 +131,7 @@ const ProgressBar = ({ duration: INDETERMINATE_DURATION, toValue: 1, // Animated.loop does not work if useNativeDriver is true on web - useNativeDriver: !isWeb, + useNativeDriver: Platform.OS !== 'web', isInteraction: false, }); } @@ -149,7 +148,7 @@ const ProgressBar = ({ isInteraction: false, }).start(); } - }, [fade, scale, indeterminate, timer, progress, isWeb]); + }, [fade, scale, indeterminate, timer, progress]); const stopAnimation = React.useCallback(() => { // Stop indeterminate animation @@ -201,7 +200,7 @@ const ProgressBar = ({ aria-valuemin={indeterminate ? undefined : 0} aria-valuemax={indeterminate ? undefined : 100} aria-valuenow={indeterminate ? undefined : Math.round(progress * 100)} - style={isWeb && styles.webContainer} + style={Platform.OS === 'web' && styles.webContainer} testID={testID} > { - const isWeb = Platform.OS === 'web'; - const theme = useInternalTheme(themeOverrides); const [visible, setVisible] = React.useState(false); @@ -120,7 +118,7 @@ const Tooltip = ({ hideTooltipTimer.current = []; } - if (isWeb) { + if (Platform.OS === 'web') { const id = setTimeout(() => { touched.current = true; setVisible(true); @@ -130,7 +128,7 @@ const Tooltip = ({ touched.current = true; setVisible(true); } - }, [isWeb, enterTouchDelay]); + }, [enterTouchDelay]); const handleTouchEnd = React.useCallback(() => { touched.current = false; @@ -236,11 +234,11 @@ const Tooltip = ({ {React.cloneElement(children, { ...rest, - ...(isWeb ? webPressProps : mobilePressProps), + ...(Platform.OS === 'web' ? webPressProps : mobilePressProps), })} From f938c789e71c2b8f04c65b38980e32da9a2a261b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Fedyna?= Date: Fri, 4 Sep 2026 13:31:40 +0200 Subject: [PATCH 64/94] refactor: cleanup utility types (#5098) --- src/components/Appbar/AppbarAction.tsx | 3 ++- src/components/Appbar/AppbarBackAction.tsx | 7 ++----- src/components/Appbar/AppbarContent.tsx | 4 ++-- src/components/Appbar/AppbarHeader.tsx | 7 ++----- src/components/Banner.tsx | 5 +++-- src/components/Checkbox/Checkbox.tsx | 8 ++++++-- src/components/Chip/Chip.tsx | 5 +++-- src/components/DataTable/DataTableCell.tsx | 7 +++++-- src/components/DataTable/DataTableRow.tsx | 8 ++++++-- src/components/Divider.tsx | 7 ++++--- src/components/IconButton/IconButton.tsx | 8 ++++++-- src/components/List/ListItem.tsx | 17 +++++++++++------ src/components/List/utils.ts | 6 +++--- .../RadioButton/RadioButtonAndroid.tsx | 8 ++++++-- src/components/RadioButton/RadioButtonIOS.tsx | 9 +++++++-- src/components/Snackbar.tsx | 5 +++-- src/components/TextInput/TextInputIcon.tsx | 5 ++--- src/index.tsx | 2 +- src/types.tsx | 11 ----------- 19 files changed, 74 insertions(+), 58 deletions(-) delete mode 100644 src/types.tsx diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index c6d68a362e..dd481eb0cd 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -7,8 +7,9 @@ import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; +import type { Props as IconButtonProps } from '../IconButton/IconButton'; -export type Props = React.ComponentPropsWithoutRef & { +export type Props = React.PropsWithoutRef & { /** * Custom color for action icon. */ diff --git a/src/components/Appbar/AppbarBackAction.tsx b/src/components/Appbar/AppbarBackAction.tsx index c4249bd18a..4f23dfa542 100644 --- a/src/components/Appbar/AppbarBackAction.tsx +++ b/src/components/Appbar/AppbarBackAction.tsx @@ -9,14 +9,11 @@ import type { import type { AnimatedStyle } from 'react-native-reanimated'; -import type { $Omit } from './../../types'; import AppbarAction from './AppbarAction'; +import type { Props as AppbarActionProps } from './AppbarAction'; import AppbarBackIcon from './AppbarBackIcon'; -export type Props = $Omit< - React.ComponentPropsWithoutRef, - 'icon' -> & { +export type Props = Omit, 'icon'> & { /** * Custom color for back icon. */ diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index 5270aa271d..0717499fe4 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -10,7 +10,7 @@ import type { import { modeTextVariant } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import Text from '../Typography/Text'; import type { TextRef } from '../Typography/Text'; @@ -21,7 +21,7 @@ type TitleString = { type TitleElement = { title: React.ReactNode; titleStyle?: never }; -export type Props = $RemoveChildren & { +export type Props = Omit, 'children'> & { // For `title` and `titleStyle` props their types are duplicated due to the generation of documentation. // Appropriate type for them are either `TitleString` or `TitleElement`, depends on `title` type. /** diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index 1c486f9ccd..f111e1699e 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -5,15 +5,12 @@ import type { ColorValue, StyleProp } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Appbar } from './Appbar'; -import type { AppbarStyle } from './Appbar'; +import type { AppbarStyle, Props as AppbarProps } from './Appbar'; import { getAppbarBackgroundColor, modeAppbarHeight } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../types'; -export type Props = Omit< - React.ComponentProps, - 'safeAreaInsets' | 'style' -> & { +export type Props = Omit & { /** * Whether the background color is a dark color. A dark header will render light text and vice-versa. */ diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index dddcf330c0..0dce8acae8 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -19,13 +19,14 @@ import { scheduleOnRN } from 'react-native-worklets'; import useLatestCallback from 'use-latest-callback'; import Button from './Button/Button'; +import type { Props as ButtonProps } from './Button/Button'; import Icon from './Icon'; import type { IconSource } from './Icon'; import Surface from './Surface'; import type { SurfaceStyle } from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; -import type { $RemoveChildren, Elevation, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../types'; const DEFAULT_MAX_WIDTH = 960; @@ -56,7 +57,7 @@ export type Props = Omit & { actions?: Array< { label: string; - } & $RemoveChildren + } & Omit, 'children'> >; /** * Style of banner's inner content. diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 02df81b0b4..5cb529104d 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -17,11 +17,15 @@ import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { tokens } from '../../theme/tokens'; -import type { $RemoveChildren, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import { isKeyboardFocusEvent } from '../../utils/isKeyboardFocusEvent'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; -export type Props = $RemoveChildren & { +export type Props = Omit< + React.PropsWithoutRef, + 'children' +> & { /** * Status of checkbox. */ diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 3bbbe32cf5..35904c2b27 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -5,6 +5,7 @@ import type { GestureResponderEvent, PressableAndroidRippleConfig, StyleProp, + TextProps, TextStyle, ViewProps, } from 'react-native'; @@ -15,7 +16,7 @@ import { getChipColors } from './helpers'; import type { ChipAvatarProps } from './helpers'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; -import type { EllipsizeProp, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; @@ -140,7 +141,7 @@ export type Props = Omit & { /** * Ellipsize Mode for the children text */ - ellipsizeMode?: EllipsizeProp; + ellipsizeMode?: TextProps['ellipsizeMode']; /** * Specifies the largest possible scale a text font can reach. */ diff --git a/src/components/DataTable/DataTableCell.tsx b/src/components/DataTable/DataTableCell.tsx index ce59206af6..b5842a07c9 100644 --- a/src/components/DataTable/DataTableCell.tsx +++ b/src/components/DataTable/DataTableCell.tsx @@ -7,11 +7,14 @@ import type { GestureResponderEvent, } from 'react-native'; -import type { $RemoveChildren } from '../../types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; -export type Props = $RemoveChildren & { +export type Props = Omit< + React.PropsWithoutRef, + 'children' +> & { /** * Content of the `DataTableCell`. */ diff --git a/src/components/DataTable/DataTableRow.tsx b/src/components/DataTable/DataTableRow.tsx index 51964a5f34..4fa776b3db 100644 --- a/src/components/DataTable/DataTableRow.tsx +++ b/src/components/DataTable/DataTableRow.tsx @@ -8,10 +8,14 @@ import type { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; -export type Props = $RemoveChildren & { +export type Props = Omit< + React.PropsWithoutRef, + 'children' +> & { /** * Content of the `DataTableRow`. */ diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index aadaed4eb3..f06389d2c3 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -1,10 +1,11 @@ +import type * as React from 'react'; import { StyleSheet, View } from 'react-native'; -import type { StyleProp, ViewStyle } from 'react-native'; +import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../types'; +import type { ThemeProp } from '../types'; -export type Props = $RemoveChildren & { +export type Props = Omit, 'children'> & { /** * @renamed Renamed from 'inset' to 'leftInset` in v5.x * Whether divider has a left inset. diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 52cb5fa3b2..39b49e40f4 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -11,18 +11,22 @@ import Animated, { type AnimatedStyle } from 'react-native-reanimated'; import { getIconButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import ActivityIndicator from '../ActivityIndicator'; import CrossFadeIcon from '../CrossFadeIcon'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; const PADDING = 8; type IconButtonMode = 'outlined' | 'contained' | 'contained-tonal'; -export type Props = Omit<$RemoveChildren, 'style'> & { +export type Props = Omit< + React.PropsWithoutRef, + 'children' | 'style' +> & { /** * Icon to display. */ diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index a6f0181f02..2a54cf97c3 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -6,6 +6,7 @@ import type { NativeSyntheticEvent, StyleProp, TextLayoutEventData, + TextProps, TextStyle, ViewStyle, } from 'react-native'; @@ -13,15 +14,16 @@ import type { import { getLeftStyles, getRightStyles } from './utils'; import type { Style } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, EllipsizeProp, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; type Title = | React.ReactNode | ((props: { selectable: boolean; - ellipsizeMode: EllipsizeProp | undefined; + ellipsizeMode: TextProps['ellipsizeMode']; color: ColorValue; fontSize: number; }) => React.ReactNode); @@ -30,12 +32,15 @@ type Description = | React.ReactNode | ((props: { selectable: boolean; - ellipsizeMode: EllipsizeProp | undefined; + ellipsizeMode: TextProps['ellipsizeMode']; color: ColorValue; fontSize: number; }) => React.ReactNode); -export type Props = $RemoveChildren & { +export type Props = Omit< + React.PropsWithoutRef, + 'children' +> & { /** * Title text for the list item. */ @@ -96,13 +101,13 @@ export type Props = $RemoveChildren & { * * See [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode) */ - titleEllipsizeMode?: EllipsizeProp; + titleEllipsizeMode?: TextProps['ellipsizeMode']; /** * Ellipsize Mode for the Description. One of `'head'`, `'middle'`, `'tail'`, `'clip'`. * * See [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode) */ - descriptionEllipsizeMode?: EllipsizeProp; + descriptionEllipsizeMode?: TextProps['ellipsizeMode']; /** * Specifies the largest possible scale a title font can reach. */ diff --git a/src/components/List/utils.ts b/src/components/List/utils.ts index 7b7a868de0..5bce566f90 100644 --- a/src/components/List/utils.ts +++ b/src/components/List/utils.ts @@ -1,13 +1,13 @@ import { StyleSheet } from 'react-native'; -import type { StyleProp, ViewStyle } from 'react-native'; +import type { StyleProp, TextProps, ViewStyle } from 'react-native'; -import type { EllipsizeProp, InternalTheme, ThemeProp } from '../../types'; +import type { InternalTheme, ThemeProp } from '../../types'; type Description = | React.ReactNode | ((props: { selectable: boolean; - ellipsizeMode: EllipsizeProp | undefined; + ellipsizeMode: TextProps['ellipsizeMode']; color: string; fontSize: number; }) => React.ReactNode); diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index b82d793a1e..0add107cf6 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -6,10 +6,14 @@ import { RadioButtonContext } from './RadioButtonGroup'; import type { RadioButtonContextType } from './RadioButtonGroup'; import { getSelectionControlColor, handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; -export type Props = $RemoveChildren & { +export type Props = Omit< + React.PropsWithoutRef, + 'children' +> & { /** * Value of the radio button */ diff --git a/src/components/RadioButton/RadioButtonIOS.tsx b/src/components/RadioButton/RadioButtonIOS.tsx index 131e5ce7c7..3dae55684d 100644 --- a/src/components/RadioButton/RadioButtonIOS.tsx +++ b/src/components/RadioButton/RadioButtonIOS.tsx @@ -1,3 +1,4 @@ +import type * as React from 'react'; import { StyleSheet, View } from 'react-native'; import type { GestureResponderEvent } from 'react-native'; @@ -6,11 +7,15 @@ import type { RadioButtonContextType } from './RadioButtonGroup'; import { handlePress, isChecked } from './utils'; import { getSelectionControlIOSColor } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $RemoveChildren, ThemeProp } from '../../types'; +import type { ThemeProp } from '../../types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; +import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; -export type Props = $RemoveChildren & { +export type Props = Omit< + React.PropsWithoutRef, + 'children' +> & { /** * Value of the radio button */ diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index 297825c130..de27e5b603 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -15,6 +15,7 @@ import { scheduleOnRN } from 'react-native-worklets'; import useLatestCallback from 'use-latest-callback'; import Button from './Button/Button'; +import type { Props as ButtonProps } from './Button/Button'; import type { IconSource } from './Icon'; import IconButton from './IconButton/IconButton'; import MaterialCommunityIcon from './MaterialCommunityIcon'; @@ -23,7 +24,7 @@ import type { SurfaceStyle } from './Surface'; import Text from './Typography/Text'; import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; -import type { $RemoveChildren, Elevation, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../types'; export type Props = Omit & { /** @@ -35,7 +36,7 @@ export type Props = Omit & { * - `label` - Label of the action button * - `onPress` - Callback that is called when action button is pressed. */ - action?: $RemoveChildren & { + action?: Omit, 'children'> & { label: string; }; /** diff --git a/src/components/TextInput/TextInputIcon.tsx b/src/components/TextInput/TextInputIcon.tsx index 4fcd7b6e18..012e2f45f9 100644 --- a/src/components/TextInput/TextInputIcon.tsx +++ b/src/components/TextInput/TextInputIcon.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { View } from 'react-native'; import type { StyleProp, ViewStyle } from 'react-native'; @@ -6,8 +5,8 @@ import { ACCESSORY_SIZE } from './constants'; import { styles } from './styles'; import { getIconColor } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { $Omit } from '../../types'; import IconButton from '../IconButton/IconButton'; +import type { Props as IconButtonProps } from '../IconButton/IconButton'; export type TextInputAccessoryProps = { style: StyleProp; @@ -17,7 +16,7 @@ export type TextInputAccessoryProps = { }; export type TextInputIconProps = TextInputAccessoryProps & - $Omit, keyof TextInputAccessoryProps>; + Omit; /** * A component to render a leading / trailing icon in the TextInput diff --git a/src/index.tsx b/src/index.tsx index 8863e2fa20..f46d8e22d8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -147,4 +147,4 @@ export type { Props as SegmentedButtonsProps } from './components/SegmentedButto export type { Props as ListImageProps } from './components/List/ListImage'; export type { Props as TooltipProps } from './components/Tooltip/Tooltip'; -export { type TypescaleKey, type Theme, type Elevation } from './types'; +export { type TypescaleKey, type Theme, type Elevation } from './theme/types'; diff --git a/src/types.tsx b/src/types.tsx deleted file mode 100644 index c84e3915b4..0000000000 --- a/src/types.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type * as React from 'react'; - -export * from './theme/types'; - -export type $Omit = Pick>; -export type $RemoveChildren> = $Omit< - React.ComponentPropsWithoutRef, - 'children' ->; - -export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip'; From 98e4a8484f2947b4414be39d528cb82838191c13 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 4 Sep 2026 13:32:26 +0200 Subject: [PATCH 65/94] refactor: fix type imports --- src/components/ActivityIndicator.tsx | 2 +- src/components/Appbar/Appbar.tsx | 2 +- src/components/Appbar/AppbarAction.tsx | 2 +- src/components/Appbar/AppbarContent.tsx | 2 +- src/components/Appbar/AppbarHeader.tsx | 2 +- src/components/Appbar/utils.ts | 2 +- src/components/Avatar/AvatarIcon.tsx | 2 +- src/components/Avatar/AvatarImage.tsx | 2 +- src/components/Avatar/AvatarText.tsx | 2 +- src/components/Badge.tsx | 2 +- src/components/Banner.tsx | 2 +- src/components/BottomNavigation/BottomNavigation.tsx | 2 +- src/components/BottomNavigation/BottomNavigationBar.tsx | 2 +- src/components/BottomNavigation/utils.ts | 2 +- src/components/Button/Button.tsx | 2 +- src/components/Button/utils.tsx | 2 +- src/components/Card/Card.tsx | 2 +- src/components/Card/CardActions.tsx | 2 +- src/components/Card/CardCover.tsx | 2 +- src/components/Card/CardTitle.tsx | 2 +- src/components/Card/utils.tsx | 2 +- src/components/Checkbox/Checkbox.tsx | 2 +- src/components/Checkbox/CheckboxItem.tsx | 2 +- src/components/Checkbox/utils.ts | 2 +- src/components/Chip/Chip.tsx | 2 +- src/components/Chip/helpers.tsx | 2 +- src/components/CrossFadeIcon.tsx | 2 +- src/components/DataTable/DataTableHeader.tsx | 2 +- src/components/DataTable/DataTablePagination.tsx | 2 +- src/components/DataTable/DataTableRow.tsx | 2 +- src/components/DataTable/DataTableTitle.tsx | 2 +- src/components/Dialog/Dialog.tsx | 2 +- src/components/Dialog/DialogActions.tsx | 2 +- src/components/Dialog/DialogIcon.tsx | 2 +- src/components/Dialog/DialogScrollArea.tsx | 2 +- src/components/Dialog/DialogTitle.tsx | 2 +- src/components/Divider.tsx | 2 +- src/components/Drawer/DrawerCollapsedItem.tsx | 2 +- src/components/Drawer/DrawerItem.tsx | 2 +- src/components/Drawer/DrawerSection.tsx | 2 +- src/components/FAB/Extended.tsx | 2 +- src/components/FAB/FAB.tsx | 2 +- src/components/FAB/Menu.tsx | 2 +- src/components/FAB/Shell.tsx | 2 +- src/components/FAB/utils.ts | 3 +-- src/components/Icon.tsx | 2 +- src/components/IconButton/IconButton.tsx | 2 +- src/components/IconButton/utils.ts | 2 +- src/components/List/ListAccordion.tsx | 2 +- src/components/List/ListIcon.tsx | 2 +- src/components/List/ListImage.tsx | 2 +- src/components/List/ListItem.tsx | 2 +- src/components/List/ListSection.tsx | 2 +- src/components/List/ListSubheader.tsx | 2 +- src/components/List/utils.ts | 2 +- src/components/Menu/Menu.tsx | 2 +- src/components/Menu/MenuItem.tsx | 2 +- src/components/Menu/utils.ts | 2 +- src/components/Modal.tsx | 2 +- src/components/Portal/Portal.tsx | 2 +- src/components/ProgressBar.tsx | 2 +- src/components/RadioButton/RadioButton.tsx | 2 +- src/components/RadioButton/RadioButtonAndroid.tsx | 2 +- src/components/RadioButton/RadioButtonIOS.tsx | 2 +- src/components/RadioButton/RadioButtonItem.tsx | 2 +- src/components/RadioButton/utils.ts | 2 +- src/components/Searchbar.tsx | 2 +- src/components/SegmentedButtons/SegmentedButtonItem.tsx | 2 +- src/components/SegmentedButtons/SegmentedButtons.tsx | 2 +- src/components/SegmentedButtons/utils.ts | 2 +- src/components/Snackbar.tsx | 2 +- src/components/Surface.tsx | 2 +- src/components/Switch/Switch.tsx | 2 +- src/components/Switch/utils.ts | 2 +- src/components/TextInput/TextInput.tsx | 2 +- src/components/TextInput/TextInputErrorIcon.tsx | 2 +- src/components/TextInput/hooks.ts | 2 +- src/components/TextInput/utils.ts | 2 +- src/components/ToggleButton/ToggleButton.tsx | 2 +- src/components/ToggleButton/utils.ts | 2 +- src/components/Tooltip/Tooltip.tsx | 2 +- src/components/TouchableRipple/TouchableRipple.native.tsx | 2 +- src/components/TouchableRipple/TouchableRipple.tsx | 2 +- src/components/TouchableRipple/utils.ts | 2 +- src/components/Typography/AnimatedText.tsx | 2 +- src/components/Typography/Text.tsx | 2 +- src/components/Typography/types.tsx | 2 +- src/components/__tests__/Menu.test.tsx | 2 +- src/core/PaperProvider.tsx | 2 +- src/core/__tests__/PaperProvider.test.tsx | 2 +- 90 files changed, 90 insertions(+), 91 deletions(-) diff --git a/src/components/ActivityIndicator.tsx b/src/components/ActivityIndicator.tsx index 7ab61dfaa9..294f7e986e 100644 --- a/src/components/ActivityIndicator.tsx +++ b/src/components/ActivityIndicator.tsx @@ -4,7 +4,7 @@ import { Animated, Easing, Platform, StyleSheet, View } from 'react-native'; import type { ColorValue, StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import type { ThemeProp } from '../types'; +import type { ThemeProp } from '../theme/types'; export type Props = ViewProps & { /** diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index 7e62fd4df9..a324a61d5c 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -12,7 +12,7 @@ import { } from './utils'; import type { AppbarModes, AppbarChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Surface from '../Surface'; const APPBAR_HORIZONTAL_PADDING = 4; diff --git a/src/components/Appbar/AppbarAction.tsx b/src/components/Appbar/AppbarAction.tsx index dd481eb0cd..1c358397c9 100644 --- a/src/components/Appbar/AppbarAction.tsx +++ b/src/components/Appbar/AppbarAction.tsx @@ -4,7 +4,7 @@ import type { ColorValue, StyleProp, View, ViewStyle } from 'react-native'; import type { AnimatedStyle } from 'react-native-reanimated'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; import type { Props as IconButtonProps } from '../IconButton/IconButton'; diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index 0717499fe4..fa1cad5ebc 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -10,7 +10,7 @@ import type { import { modeTextVariant } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Text from '../Typography/Text'; import type { TextRef } from '../Typography/Text'; diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index f111e1699e..1a7c7c04dc 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -8,7 +8,7 @@ import { Appbar } from './Appbar'; import type { AppbarStyle, Props as AppbarProps } from './Appbar'; import { getAppbarBackgroundColor, modeAppbarHeight } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = Omit & { /** diff --git a/src/components/Appbar/utils.ts b/src/components/Appbar/utils.ts index 29978636b9..36b2fd2f09 100644 --- a/src/components/Appbar/utils.ts +++ b/src/components/Appbar/utils.ts @@ -3,7 +3,7 @@ import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import { StyleSheet } from 'react-native'; import { white } from '../../theme/colors'; -import type { InternalTheme, ThemeProp } from '../../types'; +import type { InternalTheme, ThemeProp } from '../../theme/types'; export type AppbarModes = 'small' | 'medium' | 'large' | 'center-aligned'; diff --git a/src/components/Avatar/AvatarIcon.tsx b/src/components/Avatar/AvatarIcon.tsx index 9c3b813e59..2d2f08760b 100644 --- a/src/components/Avatar/AvatarIcon.tsx +++ b/src/components/Avatar/AvatarIcon.tsx @@ -3,7 +3,7 @@ import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import getContrastingColor from '../../utils/getContrastingColor'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; diff --git a/src/components/Avatar/AvatarImage.tsx b/src/components/Avatar/AvatarImage.tsx index 51330dd70c..e8b6e501b1 100644 --- a/src/components/Avatar/AvatarImage.tsx +++ b/src/components/Avatar/AvatarImage.tsx @@ -9,7 +9,7 @@ import type { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; const defaultSize = 64; diff --git a/src/components/Avatar/AvatarText.tsx b/src/components/Avatar/AvatarText.tsx index d9e16c8090..4d3ffe87a0 100644 --- a/src/components/Avatar/AvatarText.tsx +++ b/src/components/Avatar/AvatarText.tsx @@ -3,7 +3,7 @@ import type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import getContrastingColor from '../../utils/getContrastingColor'; import Text from '../Typography/Text'; diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 23a8cd973e..cbe7e8d0b5 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -5,7 +5,7 @@ import Animated, { type AnimatedStyle } from 'react-native-reanimated'; import { useInternalTheme } from '../core/theming'; import { cornerFull } from '../theme/tokens/sys/shape'; -import type { ThemeProp } from '../types'; +import type { ThemeProp } from '../theme/types'; const SMALL_SIZE = 6; const LARGE_SIZE = 16; diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index 0dce8acae8..909f75bf3f 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -26,7 +26,7 @@ import Surface from './Surface'; import type { SurfaceStyle } from './Surface'; import Text from './Typography/Text'; import { useInternalTheme } from '../core/theming'; -import type { Elevation, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../theme/types'; const DEFAULT_MAX_WIDTH = 960; diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index 7b0cd12111..b513748508 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -13,7 +13,7 @@ import useLatestCallback from 'use-latest-callback'; import BottomNavigationBar from './BottomNavigationBar'; import BottomNavigationRouteScreen from './BottomNavigationRouteScreen'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import useAnimatedValueArray from '../../utils/useAnimatedValueArray'; import type { IconSource } from '../Icon'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index b5cd6da15a..04e72da648 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -16,7 +16,7 @@ import { getLabelColor, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import useAnimatedValue from '../../utils/useAnimatedValue'; import useAnimatedValueArray from '../../utils/useAnimatedValueArray'; import useIsKeyboardShown from '../../utils/useIsKeyboardShown'; diff --git a/src/components/BottomNavigation/utils.ts b/src/components/BottomNavigation/utils.ts index e5d42940d8..1f882cd256 100644 --- a/src/components/BottomNavigation/utils.ts +++ b/src/components/BottomNavigation/utils.ts @@ -1,6 +1,6 @@ import type { ColorValue } from 'react-native'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; export const getActiveTintColor = ({ activeColor, diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 00f0242f0f..458e324228 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -14,7 +14,7 @@ import type { import { getButtonColors, getButtonTouchableRippleStyle } from './utils'; import type { ButtonMode } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import ActivityIndicator from '../ActivityIndicator'; import Icon from '../Icon'; diff --git a/src/components/Button/utils.tsx b/src/components/Button/utils.tsx index 058342b142..ec3503c82b 100644 --- a/src/components/Button/utils.tsx +++ b/src/components/Button/utils.tsx @@ -2,7 +2,7 @@ import type { ColorValue, ViewStyle } from 'react-native'; import { black, white } from '../../theme/colors'; import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; import { splitStyles } from '../../utils/splitStyles'; const stateOpacity = tokens.md.sys.state.opacity; diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index bb668736a8..5a6bbd1dcd 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -15,7 +15,7 @@ import CardCover from './CardCover'; import CardTitle from './CardTitle'; import { getCardColors } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { Elevation, ThemeProp } from '../../types'; +import type { Elevation, ThemeProp } from '../../theme/types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import Surface from '../Surface'; import type { SurfaceStyle } from '../Surface'; diff --git a/src/components/Card/CardActions.tsx b/src/components/Card/CardActions.tsx index 529db00f52..d541c691bc 100644 --- a/src/components/Card/CardActions.tsx +++ b/src/components/Card/CardActions.tsx @@ -4,7 +4,7 @@ import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import type { CardActionChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = ViewProps & { /** diff --git a/src/components/Card/CardCover.tsx b/src/components/Card/CardCover.tsx index dc348dbdd8..4542aa7c99 100644 --- a/src/components/Card/CardCover.tsx +++ b/src/components/Card/CardCover.tsx @@ -4,7 +4,7 @@ import type { ImageProps, StyleProp, ViewStyle } from 'react-native'; import { getCardCoverStyle } from './utils'; import { useInternalTheme } from '../../core/theming'; import { grey200 } from '../../theme/colors'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import { splitStyles } from '../../utils/splitStyles'; export type Props = ImageProps & { diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index 2737fe6430..7332c23014 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native'; import type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { TypescaleKey, ThemeProp } from '../../types'; +import type { TypescaleKey, ThemeProp } from '../../theme/types'; import Text from '../Typography/Text'; export type Props = ViewProps & { diff --git a/src/components/Card/utils.tsx b/src/components/Card/utils.tsx index 644c8f4c27..fc0faa945a 100644 --- a/src/components/Card/utils.tsx +++ b/src/components/Card/utils.tsx @@ -1,6 +1,6 @@ import type { StyleProp, ViewStyle } from 'react-native'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; type CardMode = 'elevated' | 'outlined' | 'contained'; diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 5cb529104d..3bccccf33b 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -17,7 +17,7 @@ import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { tokens } from '../../theme/tokens'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import { isKeyboardFocusEvent } from '../../utils/isKeyboardFocusEvent'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index fae1a44c85..79fa6da325 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -9,8 +9,8 @@ import type { import Checkbox from './Checkbox'; import { useInternalTheme } from '../../core/theming'; +import type { ThemeProp, TypescaleKey } from '../../theme/types'; import { getStateLayer } from '../../theme/utils/state'; -import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; diff --git a/src/components/Checkbox/utils.ts b/src/components/Checkbox/utils.ts index 75b7398a77..80e7c31750 100644 --- a/src/components/Checkbox/utils.ts +++ b/src/components/Checkbox/utils.ts @@ -2,7 +2,7 @@ import type { ColorValue } from 'react-native'; import { CheckboxTokens } from './tokens'; import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; // MD3 Checkbox spec: https://m3.material.io/components/checkbox/specs diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 35904c2b27..c5cf09244e 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -16,7 +16,7 @@ import { getChipColors } from './helpers'; import type { ChipAvatarProps } from './helpers'; import { useInternalTheme } from '../../core/theming'; import { white } from '../../theme/colors'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import hasTouchHandler from '../../utils/hasTouchHandler'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; diff --git a/src/components/Chip/helpers.tsx b/src/components/Chip/helpers.tsx index 1990bc59a7..3091d36e31 100644 --- a/src/components/Chip/helpers.tsx +++ b/src/components/Chip/helpers.tsx @@ -3,7 +3,7 @@ import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import color from 'color'; import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; const md3 = (theme: InternalTheme) => theme; diff --git a/src/components/CrossFadeIcon.tsx b/src/components/CrossFadeIcon.tsx index 686109a501..cc66313fab 100644 --- a/src/components/CrossFadeIcon.tsx +++ b/src/components/CrossFadeIcon.tsx @@ -14,7 +14,7 @@ import Animated, { import Icon, { isEqualIcon, isValidIcon } from './Icon'; import type { IconSource } from './Icon'; import { useInternalTheme } from '../core/theming'; -import type { ThemeProp } from '../types'; +import type { ThemeProp } from '../theme/types'; type Props = { /** diff --git a/src/components/DataTable/DataTableHeader.tsx b/src/components/DataTable/DataTableHeader.tsx index cacad34340..800f92b1a4 100644 --- a/src/components/DataTable/DataTableHeader.tsx +++ b/src/components/DataTable/DataTableHeader.tsx @@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = ViewProps & { /** diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 32f4b8b4fb..0e07e5e1c0 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -4,7 +4,7 @@ import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Button from '../Button/Button'; import IconButton from '../IconButton/IconButton'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; diff --git a/src/components/DataTable/DataTableRow.tsx b/src/components/DataTable/DataTableRow.tsx index 4fa776b3db..a833fda7b1 100644 --- a/src/components/DataTable/DataTableRow.tsx +++ b/src/components/DataTable/DataTableRow.tsx @@ -8,7 +8,7 @@ import type { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/DataTable/DataTableTitle.tsx b/src/components/DataTable/DataTableTitle.tsx index 0015c8753f..d6420ea41e 100644 --- a/src/components/DataTable/DataTableTitle.tsx +++ b/src/components/DataTable/DataTableTitle.tsx @@ -19,7 +19,7 @@ import Animated, { import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import Text from '../Typography/Text'; diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 89832fe658..1fe6f13700 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -10,7 +10,7 @@ import DialogIcon from './DialogIcon'; import DialogScrollArea from './DialogScrollArea'; import DialogTitle from './DialogTitle'; import { useInternalTheme } from '../../core/theming'; -import type { Elevation, ThemeProp } from '../../types'; +import type { Elevation, ThemeProp } from '../../theme/types'; import Modal from '../Modal'; import type { SurfaceStyle } from '../Surface'; import type { DialogChildProps } from './utils'; diff --git a/src/components/Dialog/DialogActions.tsx b/src/components/Dialog/DialogActions.tsx index 7e3799451e..0a11970077 100644 --- a/src/components/Dialog/DialogActions.tsx +++ b/src/components/Dialog/DialogActions.tsx @@ -4,7 +4,7 @@ import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import type { DialogActionChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = ViewProps & { /** diff --git a/src/components/Dialog/DialogIcon.tsx b/src/components/Dialog/DialogIcon.tsx index 3ced9e12b0..791544aefe 100644 --- a/src/components/Dialog/DialogIcon.tsx +++ b/src/components/Dialog/DialogIcon.tsx @@ -2,7 +2,7 @@ import { StyleSheet, View } from 'react-native'; import type { ColorValue } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; diff --git a/src/components/Dialog/DialogScrollArea.tsx b/src/components/Dialog/DialogScrollArea.tsx index a46824be45..c2446f6149 100644 --- a/src/components/Dialog/DialogScrollArea.tsx +++ b/src/components/Dialog/DialogScrollArea.tsx @@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = ViewProps & { /** diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index ae553daee2..beff3be7d9 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import type { StyleProp, TextStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Text from '../Typography/Text'; export type Props = React.ComponentPropsWithRef & { diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index f06389d2c3..b00c0ee895 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../core/theming'; -import type { ThemeProp } from '../types'; +import type { ThemeProp } from '../theme/types'; export type Props = Omit, 'children'> & { /** diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index 52decb7c23..4553ba9f95 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -18,7 +18,7 @@ import Animated, { } from 'react-native-reanimated'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Badge from '../Badge'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 88535ba162..5924f44e3b 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -10,7 +10,7 @@ import type { } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/Drawer/DrawerSection.tsx b/src/components/Drawer/DrawerSection.tsx index d267cf4c17..55773aac81 100644 --- a/src/components/Drawer/DrawerSection.tsx +++ b/src/components/Drawer/DrawerSection.tsx @@ -4,7 +4,7 @@ import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; import { Palette } from '../../theme/tokens'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Divider from '../Divider'; import Text from '../Typography/Text'; diff --git a/src/components/FAB/Extended.tsx b/src/components/FAB/Extended.tsx index ff3c500545..fb58e730e6 100644 --- a/src/components/FAB/Extended.tsx +++ b/src/components/FAB/Extended.tsx @@ -24,7 +24,7 @@ import { getDimensions } from './utils'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { toRawSpring } from '../../theme/tokens/sys/motion'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import type { IconSource } from '../Icon'; import AnimatedText from '../Typography/AnimatedText'; diff --git a/src/components/FAB/FAB.tsx b/src/components/FAB/FAB.tsx index fac6460c6a..036edd57d9 100644 --- a/src/components/FAB/FAB.tsx +++ b/src/components/FAB/FAB.tsx @@ -12,7 +12,7 @@ import type { AnimatedStyle } from 'react-native-reanimated'; import Shell from './Shell'; import type { Size, Variant } from './tokens'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import type { IconSource } from '../Icon'; export type Props = { diff --git a/src/components/FAB/Menu.tsx b/src/components/FAB/Menu.tsx index 58a75291cf..5a08323060 100644 --- a/src/components/FAB/Menu.tsx +++ b/src/components/FAB/Menu.tsx @@ -28,8 +28,8 @@ import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { toRawSpring } from '../../theme/tokens/sys/motion'; +import type { InternalTheme, ThemeProp } from '../../theme/types'; import { resolveCornerRadius } from '../../theme/utils/shape'; -import type { InternalTheme, ThemeProp } from '../../types'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index b9f6279194..5a7d3b81bf 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -29,8 +29,8 @@ import { getDimensions, resolveColors } from './utils'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { toRawSpring } from '../../theme/tokens/sys/motion'; +import type { Elevation, ThemeProp } from '../../theme/types'; import type { ShapeToken } from '../../theme/utils/shape'; -import type { Elevation, ThemeProp } from '../../types'; import type { IconSource } from '../Icon'; import Surface from '../Surface'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/FAB/utils.ts b/src/components/FAB/utils.ts index 75b7ddb940..cd894f048d 100644 --- a/src/components/FAB/utils.ts +++ b/src/components/FAB/utils.ts @@ -2,11 +2,10 @@ import type { ColorValue } from 'react-native'; import { Tokens } from './tokens'; import type { Size, Variant } from './tokens'; -import type { TypescaleKey } from '../../theme/types'; +import type { InternalTheme, TypescaleKey } from '../../theme/types'; import { contentColorFor } from '../../theme/utils/color'; import { resolveCornerRadius } from '../../theme/utils/shape'; import type { ShapeToken } from '../../theme/utils/shape'; -import type { InternalTheme } from '../../types'; export type ResolvedColors = { container: ColorValue; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index cb2c0b5e39..5714951fcb 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -7,7 +7,7 @@ import { accessibilityProps } from './MaterialCommunityIcon'; import { useLocale } from '../core/locale'; import { Consumer as SettingsConsumer } from '../core/settings'; import { useInternalTheme } from '../core/theming'; -import type { ThemeProp } from '../types'; +import type { ThemeProp } from '../theme/types'; type IconSourceBase = string | ImageSourcePropType; diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 39b49e40f4..62b86d3754 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -11,7 +11,7 @@ import Animated, { type AnimatedStyle } from 'react-native-reanimated'; import { getIconButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import ActivityIndicator from '../ActivityIndicator'; import CrossFadeIcon from '../CrossFadeIcon'; import Icon from '../Icon'; diff --git a/src/components/IconButton/utils.ts b/src/components/IconButton/utils.ts index cbcf5f1051..3b43fa2a4a 100644 --- a/src/components/IconButton/utils.ts +++ b/src/components/IconButton/utils.ts @@ -1,7 +1,7 @@ import type { ColorValue } from 'react-native'; import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; const stateOpacity = tokens.md.sys.state.opacity; diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 97730d58e6..97c930c52e 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -17,7 +17,7 @@ import type { ListChildProps, Style } from './utils'; import { getAccordionColors, getLeftStyles } from './utils'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/List/ListIcon.tsx b/src/components/List/ListIcon.tsx index 0851ed6055..ff02dea7f6 100644 --- a/src/components/List/ListIcon.tsx +++ b/src/components/List/ListIcon.tsx @@ -2,7 +2,7 @@ import { StyleSheet, View } from 'react-native'; import type { ColorValue, StyleProp, ViewStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; diff --git a/src/components/List/ListImage.tsx b/src/components/List/ListImage.tsx index 907c1b63b2..55e2a527db 100644 --- a/src/components/List/ListImage.tsx +++ b/src/components/List/ListImage.tsx @@ -1,7 +1,7 @@ import { StyleSheet, Image } from 'react-native'; import type { StyleProp, ImageSourcePropType, ImageStyle } from 'react-native'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = { source: ImageSourcePropType; diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 2a54cf97c3..97fb1ccc82 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -14,7 +14,7 @@ import type { import { getLeftStyles, getRightStyles } from './utils'; import type { Style } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; diff --git a/src/components/List/ListSection.tsx b/src/components/List/ListSection.tsx index c9dfc470b2..cd623992d6 100644 --- a/src/components/List/ListSection.tsx +++ b/src/components/List/ListSection.tsx @@ -4,7 +4,7 @@ import type { StyleProp, TextStyle, ViewProps, ViewStyle } from 'react-native'; import ListSubheader from './ListSubheader'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = ViewProps & { /** diff --git a/src/components/List/ListSubheader.tsx b/src/components/List/ListSubheader.tsx index ada0ff1e61..b7a804a57d 100644 --- a/src/components/List/ListSubheader.tsx +++ b/src/components/List/ListSubheader.tsx @@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native'; import type { StyleProp, TextStyle } from 'react-native'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Text from '../Typography/Text'; export type Props = React.ComponentProps & { diff --git a/src/components/List/utils.ts b/src/components/List/utils.ts index 5bce566f90..e01f0e55c6 100644 --- a/src/components/List/utils.ts +++ b/src/components/List/utils.ts @@ -1,7 +1,7 @@ import { StyleSheet } from 'react-native'; import type { StyleProp, TextProps, ViewStyle } from 'react-native'; -import type { InternalTheme, ThemeProp } from '../../types'; +import type { InternalTheme, ThemeProp } from '../../theme/types'; type Description = | React.ReactNode diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 63fb0faf1f..b17b3d2b9d 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -32,7 +32,7 @@ import useLatestCallback from 'use-latest-callback'; import MenuItem from './MenuItem'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; -import type { Elevation, ThemeProp } from '../../types'; +import type { Elevation, ThemeProp } from '../../theme/types'; import { addEventListener } from '../../utils/addEventListener'; import { BackHandler } from '../../utils/BackHandler/BackHandler'; import Portal from '../Portal/Portal'; diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 149a93a752..ec65303d30 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -15,7 +15,7 @@ import { MIN_WIDTH, } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Icon from '../Icon'; import type { IconSource } from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/Menu/utils.ts b/src/components/Menu/utils.ts index 2aae40d2e4..177c48feb6 100644 --- a/src/components/Menu/utils.ts +++ b/src/components/Menu/utils.ts @@ -1,5 +1,5 @@ import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; import type { IconSource } from '../Icon'; const stateOpacity = tokens.md.sys.state.opacity; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 22a3f7ba8e..0a34198d2e 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -13,7 +13,7 @@ import Surface from './Surface'; import type { Props as SurfaceProps, SurfaceStyle } from './Surface'; import { useInternalTheme } from '../core/theming'; import { tokens } from '../theme/tokens'; -import type { Elevation, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../theme/types'; import { addEventListener } from '../utils/addEventListener'; import { BackHandler } from '../utils/BackHandler/BackHandler'; diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index bfbe53c837..0dd218069b 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -8,7 +8,7 @@ import { Provider as SettingsProvider, } from '../../core/settings'; import { ThemeProvider, withInternalTheme } from '../../core/theming'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; export type Props = { /** diff --git a/src/components/ProgressBar.tsx b/src/components/ProgressBar.tsx index 54c62038b4..a917a05e8b 100644 --- a/src/components/ProgressBar.tsx +++ b/src/components/ProgressBar.tsx @@ -10,7 +10,7 @@ import type { import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; -import type { ThemeProp } from '../types'; +import type { ThemeProp } from '../theme/types'; export type Props = ViewProps & { /** diff --git a/src/components/RadioButton/RadioButton.tsx b/src/components/RadioButton/RadioButton.tsx index cb8a621e2d..af39325c33 100644 --- a/src/components/RadioButton/RadioButton.tsx +++ b/src/components/RadioButton/RadioButton.tsx @@ -4,7 +4,7 @@ import type { GestureResponderEvent } from 'react-native'; import RadioButtonAndroid from './RadioButtonAndroid'; import RadioButtonIOS from './RadioButtonIOS'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = { /** diff --git a/src/components/RadioButton/RadioButtonAndroid.tsx b/src/components/RadioButton/RadioButtonAndroid.tsx index 0add107cf6..91520db69c 100644 --- a/src/components/RadioButton/RadioButtonAndroid.tsx +++ b/src/components/RadioButton/RadioButtonAndroid.tsx @@ -6,7 +6,7 @@ import { RadioButtonContext } from './RadioButtonGroup'; import type { RadioButtonContextType } from './RadioButtonGroup'; import { getSelectionControlColor, handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/RadioButton/RadioButtonIOS.tsx b/src/components/RadioButton/RadioButtonIOS.tsx index 3dae55684d..9ca15a7e4a 100644 --- a/src/components/RadioButton/RadioButtonIOS.tsx +++ b/src/components/RadioButton/RadioButtonIOS.tsx @@ -7,7 +7,7 @@ import type { RadioButtonContextType } from './RadioButtonGroup'; import { handlePress, isChecked } from './utils'; import { getSelectionControlIOSColor } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index 849c9b637d..71f7aaec23 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -14,8 +14,8 @@ import type { RadioButtonContextType } from './RadioButtonGroup'; import RadioButtonIOS from './RadioButtonIOS'; import { handlePress, isChecked } from './utils'; import { useInternalTheme } from '../../core/theming'; +import type { ThemeProp, TypescaleKey } from '../../theme/types'; import { getStateLayer } from '../../theme/utils/state'; -import type { ThemeProp, TypescaleKey } from '../../types'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; import type { Props as TouchableRippleProps } from '../TouchableRipple/TouchableRipple'; import Text from '../Typography/Text'; diff --git a/src/components/RadioButton/utils.ts b/src/components/RadioButton/utils.ts index db2538eabc..f8190eb981 100644 --- a/src/components/RadioButton/utils.ts +++ b/src/components/RadioButton/utils.ts @@ -1,7 +1,7 @@ import type { ColorValue, GestureResponderEvent } from 'react-native'; import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; const stateOpacity = tokens.md.sys.state.opacity; diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index cae3ae4334..bd8c5293ae 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -18,7 +18,7 @@ import type { SurfaceStyle } from './Surface'; import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; import { cornerNone } from '../theme/tokens/sys/shape'; -import type { Elevation, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../theme/types'; interface Style { marginRight: number; diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 15e34dd170..9a68eeca3e 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -22,7 +22,7 @@ import { } from './utils'; import { useInternalTheme } from '../../core/theming'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import type { IconSource } from '../Icon'; import Icon from '../Icon'; import TouchableRipple from '../TouchableRipple/TouchableRipple'; diff --git a/src/components/SegmentedButtons/SegmentedButtons.tsx b/src/components/SegmentedButtons/SegmentedButtons.tsx index 5a17f553ce..486c8f1009 100644 --- a/src/components/SegmentedButtons/SegmentedButtons.tsx +++ b/src/components/SegmentedButtons/SegmentedButtons.tsx @@ -9,7 +9,7 @@ import type { import SegmentedButtonItem from './SegmentedButtonItem'; import { getDisabledSegmentedButtonStyle } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import type { IconSource } from '../Icon'; type ConditionalValue = diff --git a/src/components/SegmentedButtons/utils.ts b/src/components/SegmentedButtons/utils.ts index 1c74f5c49f..5698731f87 100644 --- a/src/components/SegmentedButtons/utils.ts +++ b/src/components/SegmentedButtons/utils.ts @@ -1,7 +1,7 @@ import type { ViewStyle } from 'react-native'; import { tokens } from '../../theme/tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; const stateOpacity = tokens.md.sys.state.opacity; diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index de27e5b603..da621369d2 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -24,7 +24,7 @@ import type { SurfaceStyle } from './Surface'; import Text from './Typography/Text'; import { useLocale } from '../core/locale'; import { useInternalTheme } from '../core/theming'; -import type { Elevation, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../theme/types'; export type Props = Omit & { /** diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index fa84f49051..48df1a4d4a 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -11,7 +11,7 @@ import Animated, { import { useInternalTheme } from '../core/theming'; import { androidElevationLevels, shadow } from '../theme/tokens/sys/elevation'; -import type { Elevation, ThemeProp } from '../types'; +import type { Elevation, ThemeProp } from '../theme/types'; type AnimatedStyleProp = Extract< AnimatedStyle>>, diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index b0ca4b4bd7..2c86f8d963 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -29,7 +29,7 @@ import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { tokens } from '../../theme/tokens'; import { toRawSpring } from '../../theme/tokens/sys/motion'; import { cornerFull } from '../../theme/tokens/sys/shape'; -import type { StateOpacityKey, ThemeProp } from '../../types'; +import type { StateOpacityKey, ThemeProp } from '../../theme/types'; import { isKeyboardFocusEvent } from '../../utils/isKeyboardFocusEvent'; import Icon, { type IconSource } from '../Icon'; diff --git a/src/components/Switch/utils.ts b/src/components/Switch/utils.ts index e0c7908ded..78ea4131bb 100644 --- a/src/components/Switch/utils.ts +++ b/src/components/Switch/utils.ts @@ -1,7 +1,7 @@ import type { ColorValue } from 'react-native'; import { SwitchTokens } from './tokens'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; export type SwitchColors = { // Enabled, base diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx index 10ac46a67a..b89e2b35d4 100644 --- a/src/components/TextInput/TextInput.tsx +++ b/src/components/TextInput/TextInput.tsx @@ -23,7 +23,7 @@ import { useTextInput } from './hooks'; import { styles } from './styles'; import TextInputErrorIcon from './TextInputErrorIcon'; import type { TextInputAccessoryProps } from './TextInputIcon'; -import type { InternalTheme, ThemeProp } from '../../types'; +import type { InternalTheme, ThemeProp } from '../../theme/types'; export type TextInputAnimationState = { animatedLabelWrapperStyle: StyleProp>>; diff --git a/src/components/TextInput/TextInputErrorIcon.tsx b/src/components/TextInput/TextInputErrorIcon.tsx index b477753259..b49e9d8db9 100644 --- a/src/components/TextInput/TextInputErrorIcon.tsx +++ b/src/components/TextInput/TextInputErrorIcon.tsx @@ -3,7 +3,7 @@ import type { StyleProp, ViewStyle } from 'react-native'; import { ACCESSORY_SIZE } from './constants'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import Icon from '../Icon'; interface TextInputErrorIconProps { diff --git a/src/components/TextInput/hooks.ts b/src/components/TextInput/hooks.ts index d07e1ed8c4..d00667550c 100644 --- a/src/components/TextInput/hooks.ts +++ b/src/components/TextInput/hooks.ts @@ -38,7 +38,7 @@ import { } from './utils'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; const useTextInputAnimation = ({ variant, diff --git a/src/components/TextInput/utils.ts b/src/components/TextInput/utils.ts index ad81f55301..3eed947f83 100644 --- a/src/components/TextInput/utils.ts +++ b/src/components/TextInput/utils.ts @@ -35,7 +35,7 @@ import type { GetAccessibilityDataProps, GetAccessibilityDataReturn, } from './TextInput'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; export const getTextInputAnimationLayout = ({ variant, diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index 50bb50547e..72f6dc3161 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -7,7 +7,7 @@ import type { AnimatedStyle } from 'react-native-reanimated'; import { ToggleButtonGroupContext } from './ToggleButtonGroup'; import { getToggleButtonColor } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import type { IconSource } from '../Icon'; import IconButton from '../IconButton/IconButton'; diff --git a/src/components/ToggleButton/utils.ts b/src/components/ToggleButton/utils.ts index e471a5b4b6..e5ef7d0c4d 100644 --- a/src/components/ToggleButton/utils.ts +++ b/src/components/ToggleButton/utils.ts @@ -1,4 +1,4 @@ -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; export const getToggleButtonColor = ({ theme, diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index c662d7db32..144acc3d32 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -11,7 +11,7 @@ import type { LayoutChangeEvent, ViewStyle } from 'react-native'; import { getTooltipPosition } from './utils'; import type { Measurement, TooltipChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import { addEventListener } from '../../utils/addEventListener'; import Portal from '../Portal/Portal'; import Text from '../Typography/Text'; diff --git a/src/components/TouchableRipple/TouchableRipple.native.tsx b/src/components/TouchableRipple/TouchableRipple.native.tsx index 513355afa5..e86ad49c23 100644 --- a/src/components/TouchableRipple/TouchableRipple.native.tsx +++ b/src/components/TouchableRipple/TouchableRipple.native.tsx @@ -14,7 +14,7 @@ import { getTouchableRippleColors } from './utils'; import { SettingsContext } from '../../core/settings'; import type { Settings } from '../../core/settings'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import hasTouchHandler from '../../utils/hasTouchHandler'; const ANDROID_VERSION_LOLLIPOP = 21; diff --git a/src/components/TouchableRipple/TouchableRipple.tsx b/src/components/TouchableRipple/TouchableRipple.tsx index d913b1ee3c..128e2c017e 100644 --- a/src/components/TouchableRipple/TouchableRipple.tsx +++ b/src/components/TouchableRipple/TouchableRipple.tsx @@ -15,7 +15,7 @@ import { getTouchableRippleColors } from './utils'; import { SettingsContext } from '../../core/settings'; import type { Settings } from '../../core/settings'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import hasTouchHandler from '../../utils/hasTouchHandler'; export type Props = PressableProps & { diff --git a/src/components/TouchableRipple/utils.ts b/src/components/TouchableRipple/utils.ts index f7471f2d87..2874eafc86 100644 --- a/src/components/TouchableRipple/utils.ts +++ b/src/components/TouchableRipple/utils.ts @@ -1,6 +1,6 @@ import type { ColorValue } from 'react-native'; -import type { InternalTheme } from '../../types'; +import type { InternalTheme } from '../../theme/types'; const getUnderlayColor = ({ calculatedRippleColor, diff --git a/src/components/Typography/AnimatedText.tsx b/src/components/Typography/AnimatedText.tsx index 2892d8d495..35bb781317 100644 --- a/src/components/Typography/AnimatedText.tsx +++ b/src/components/Typography/AnimatedText.tsx @@ -7,7 +7,7 @@ import Animated from 'react-native-reanimated'; import type { VariantProp } from './types'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; type AnimatedTextProps = React.ComponentProps; diff --git a/src/components/Typography/Text.tsx b/src/components/Typography/Text.tsx index 38a14fb423..a35934ac3a 100644 --- a/src/components/Typography/Text.tsx +++ b/src/components/Typography/Text.tsx @@ -7,7 +7,7 @@ import AnimatedText from './AnimatedText'; import type { VariantProp } from './types'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; export type Props = React.ComponentProps & { /** diff --git a/src/components/Typography/types.tsx b/src/components/Typography/types.tsx index ef7de9891a..cd6594aa5d 100644 --- a/src/components/Typography/types.tsx +++ b/src/components/Typography/types.tsx @@ -1,4 +1,4 @@ -import type { TypescaleKey } from '../../types'; +import type { TypescaleKey } from '../../theme/types'; export type VariantProp = | (T extends string ? (string extends T ? never : T) : never) diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 5fdc76f64d..fc6369599c 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -5,7 +5,7 @@ import { act, screen, waitFor } from '@testing-library/react-native'; import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; -import type { Elevation } from '../../types'; +import type { Elevation } from '../../theme/types'; import Button from '../Button/Button'; import Menu from '../Menu/Menu'; import Portal from '../Portal/Portal'; diff --git a/src/core/PaperProvider.tsx b/src/core/PaperProvider.tsx index 5149efcd6f..1525827f6a 100644 --- a/src/core/PaperProvider.tsx +++ b/src/core/PaperProvider.tsx @@ -13,7 +13,7 @@ import { useSystemColorScheme } from './useSystemColorScheme'; import MaterialCommunityIcon from '../components/MaterialCommunityIcon'; import PortalHost from '../components/Portal/PortalHost'; import { ReduceMotionContext } from '../theme/accessibility/ReduceMotionContext'; -import type { ThemeProp } from '../types'; +import type { ThemeProp } from '../theme/types'; export type Props = { children: React.ReactNode; diff --git a/src/core/__tests__/PaperProvider.test.tsx b/src/core/__tests__/PaperProvider.test.tsx index 28ac30e327..e1f5aa9cbd 100644 --- a/src/core/__tests__/PaperProvider.test.tsx +++ b/src/core/__tests__/PaperProvider.test.tsx @@ -12,7 +12,7 @@ import { act, render, screen } from '@testing-library/react-native'; import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import { DarkTheme, DynamicLightTheme, LightTheme } from '../../theme/schemes'; -import type { ThemeProp } from '../../types'; +import type { ThemeProp } from '../../theme/types'; import PaperProvider from '../PaperProvider'; import { useTheme } from '../theming'; From 1e7082b6160cbb1820a5d8d552a596fd9e0c3f87 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 7 Sep 2026 23:17:43 +0200 Subject: [PATCH 66/94] chore: update agent instructions --- .agents/README.md | 8 ----- .github/copilot-instructions.md | 7 ++++- .github/instructions/code.instructions.md | 36 +++++++++++++++++++--- .github/instructions/docs.instructions.md | 13 ++++---- .github/instructions/tests.instructions.md | 6 ++-- .github/pull_request_template.md | 8 +++-- AGENTS.md | 16 ++++++++++ CLAUDE.md | 1 + 8 files changed, 69 insertions(+), 26 deletions(-) delete mode 100644 .agents/README.md create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.agents/README.md b/.agents/README.md deleted file mode 100644 index 8a71d618a7..0000000000 --- a/.agents/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Agent Instructions - -For code review behavior, use: - -- `.github/copilot-instructions.md` -- `.github/instructions/*.instructions.md` - -These files are the source of truth because they are also consumed by GitHub Copilot code review. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d0482badea..1bc6cef610 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -12,6 +12,11 @@ - Review new or modified public APIs for visibility, naming, defaults, customizability, and extensibility. - Ensure public APIs and exported types do not leak implementation details. -## Tests +## Dependencies + +- For dependencies added for individual components, evaluate bundle size and platform impact. Prefer existing capabilities when they meet the requirements. + +## Validation - Call out missing tests when the changed behavior is risky. +- For visual changes, check before and after screenshots or videos for each affected platform and a nearby unaffected case. diff --git a/.github/instructions/code.instructions.md b/.github/instructions/code.instructions.md index a627454507..9f0d264fef 100644 --- a/.github/instructions/code.instructions.md +++ b/.github/instructions/code.instructions.md @@ -1,8 +1,8 @@ --- -applyTo: "**/*.{ts,tsx,js,jsx}" +applyTo: '**/*.{ts,tsx,js,jsx}' --- -# TypeScript And React Review Instructions +# TypeScript and React Review Instructions ## Code Quality @@ -18,6 +18,7 @@ applyTo: "**/*.{ts,tsx,js,jsx}" - Only accept `@ts-expect-error` when it has a comment explaining why the error is expected and cannot be resolved. - Prefer simple types over complex conditional types, overloads, or type logic when simpler approaches work. - Prefer `type` aliases over `interface` unless there is a specific reason to use `interface`. +- Reuse React Native prop and ref types where they describe the component's contract. ## React @@ -29,12 +30,37 @@ applyTo: "**/*.{ts,tsx,js,jsx}" - Avoid `React.Children`, `cloneElement`, and reading React elements directly unless there is a strong justification. - Treat memoization as something that needs a reason: expensive calculation, stable identity requirement, or measured performance need. - Check async effects for cancellation or race conditions when relevant. -- Check accessibility: labels, roles, focus order, keyboard interactions, disabled states, and loading states. -## Animations And Layout +## Component APIs and Styles + +- Prefer existing native props or `style` over new props when they express the required behavior. +- Verify that components forward relevant native props and events without changing their meaning. +- Ensure controlled values have callbacks to update them. Support uncontrolled input when controlled rendering causes a demonstrated performance or reliability problem. +- Merge consumer styles after defaults for supported overrides. Check that computed styles preserve supplied padding, colors, dimensions, and other supported values. +- Expose test IDs for public interaction or content. Avoid IDs on internal wrappers solely for tests and default IDs that can collide across component instances. + +## Design and Accessibility + +- Compare component states, dimensions, icons, shapes, ripple effects, and disabled appearance with the Material Design specification. +- Use theme defaults and named tokens for colors, spacing, sizing, and motion where available. Check spacing against the design scale. +- Check labels, roles, focus order, and loading states. Verify keyboard, mouse, and touch interaction where applicable. +- Ensure disabled controls expose their state to assistive technology and prevent activation. Preserve appropriate focus when a control becomes unavailable after activation. +- Check that dismissing temporary UI restores focus to an appropriate element. +- Ensure visual changes do not make noninteractive content pressable. + +## Platforms + +- Check affected behavior on iOS, Android, and web, including positioning and interaction within scrolling containers and safe areas. +- Use direct `Platform.OS` checks or `Platform.select` so build tools can remove unused branches. Use `.native` files for native/web splits. +- Use CSS for web behavior it already supports instead of recreating it with JavaScript or layout measurements. +- Keep browser APIs and layout measurements out of server rendering. Verify that server output and the initial client render match. + +## Animations and Layout - Check for unnecessary layout shifts. - Prefer animating only `transform` and `opacity` unless another property is required. -- For layout transitions, verify the approach handles rapid state changes, gestures, interruption, and cancellation. +- Verify that animations handle rapid state changes, gestures, interruption, and cancellation. Stale completion callbacks must not leave rendered state, focus, or component callbacks inconsistent. - Check that duration and easing feel appropriate for the platform and interaction. +- Keep continuous gesture work off the JavaScript thread when responsiveness requires it, using Gesture Handler and Reanimated where appropriate. +- Measure dimensions near animation start when this provides current values and avoids extra state. - Verify text and UI elements do not overlap or overflow at supported screen sizes. diff --git a/.github/instructions/docs.instructions.md b/.github/instructions/docs.instructions.md index d9b0627f52..e0c0fd307b 100644 --- a/.github/instructions/docs.instructions.md +++ b/.github/instructions/docs.instructions.md @@ -1,12 +1,11 @@ --- -applyTo: "**/*.{md,mdx}" +applyTo: '**/*.{md,mdx}' --- # Documentation Review Instructions -- Verify examples match the actual API. -- Check changed heading hierarchy for consistency. -- Check changed internal links and anchors for consistency. -- Check that documentation explains public behavior, not implementation details. -- Check that examples are self-contained and demonstrate only the relevant behavior instead of mixing multiple behaviors. -- Check that migration guide examples show diff or before/after code snippets when relevant. +- Verify that examples match the actual API, are self-contained, and focus on the relevant behavior. +- Check that changed headings follow the document hierarchy. +- Verify that changed internal links and anchors lead to the intended content. +- Ensure documentation explains public behavior. +- Use diffs or before and after code snippets in migration guides when they help explain the change. diff --git a/.github/instructions/tests.instructions.md b/.github/instructions/tests.instructions.md index 6424174362..caa914bd24 100644 --- a/.github/instructions/tests.instructions.md +++ b/.github/instructions/tests.instructions.md @@ -1,10 +1,12 @@ --- -applyTo: "**/*.{test,spec}.{ts,tsx,js,jsx}" +applyTo: '**/*.{test,spec}.{ts,tsx,js,jsx}' --- # Test Review Instructions -- Check that tests verify public behavior, not implementation details. +- Check that tests verify public behavior, including rendered output, interaction, callbacks, and warnings. Flag assertions about private properties, context internals, or exact internal style values. +- Test internal helpers directly only when their logic is complex enough to justify separate coverage. +- Prefer accessibility queries, then test IDs for public interaction or content. - Check that test titles describe user-facing behavior. - Check coverage of happy paths, edge cases, and error states. - Flag unnecessary mocking. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2168fac68d..81aae4c0a2 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -9,8 +9,10 @@ -### Test plan +### Screenshots / Videos + + - +### Test plan - + diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..4d94259957 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,16 @@ +# Project instructions + +Read and follow the relevant project guidance before making or reviewing changes: + +- [README.md](README.md) for the project overview and documentation links. +- [CONTRIBUTING.md](CONTRIBUTING.md) for setup, development workflow, validation, component guidelines, and commit conventions. +- [package.json](package.json) for the package manager version and available scripts. +- [CI workflow](.github/workflows/ci.yml) for build and validation checks. +- [Pull request template](.github/pull_request_template.md) when preparing a pull request. + +For implementation and review, apply the shared review instructions: + +- [General review standards](.github/copilot-instructions.md). +- [TypeScript and React rules](.github/instructions/code.instructions.md) for JavaScript and TypeScript files. +- [Documentation rules](.github/instructions/docs.instructions.md) for Markdown and MDX files. +- [Test rules](.github/instructions/tests.instructions.md) for test files. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..43c994c2d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md From cbcd52f7edd4d154fc8038146796ec92bbb16db3 Mon Sep 17 00:00:00 2001 From: k0ndee Date: Mon, 7 Sep 2026 23:20:48 +0200 Subject: [PATCH 67/94] fix: remove hardcoded default testIDs from components (#5088) --- docs/6.x/docs/guides/migration.md | 61 +++++++++++++++++-- src/components/Appbar/AppbarContent.tsx | 4 +- src/components/Appbar/AppbarHeader.tsx | 2 +- .../BottomNavigation/BottomNavigation.tsx | 4 +- .../BottomNavigation/BottomNavigationBar.tsx | 6 +- src/components/Button/Button.tsx | 11 ++-- src/components/Card/Card.tsx | 6 +- src/components/Checkbox/CheckboxItem.tsx | 2 +- src/components/Chip/Chip.tsx | 4 +- src/components/CrossFadeIcon.tsx | 6 +- src/components/DataTable/DataTableCell.tsx | 2 +- src/components/Drawer/DrawerCollapsedItem.tsx | 6 +- src/components/FAB/Extended.tsx | 2 +- src/components/FAB/FAB.tsx | 2 +- src/components/FAB/Menu.tsx | 4 +- src/components/FAB/Shell.tsx | 4 +- src/components/IconButton/IconButton.tsx | 11 +++- src/components/List/ListItem.tsx | 2 +- src/components/Menu/Menu.tsx | 6 +- src/components/Menu/MenuItem.tsx | 4 +- src/components/Modal.tsx | 8 +-- src/components/ProgressBar.tsx | 4 +- src/components/Searchbar.tsx | 16 ++--- .../SegmentedButtons/SegmentedButtonItem.tsx | 6 +- src/components/Snackbar.tsx | 2 +- .../__tests__/Appbar/Appbar.test.tsx | 19 +++--- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 15 ----- .../__tests__/BottomNavigation.test.tsx | 15 +++-- src/components/__tests__/Card/Card.test.tsx | 6 +- .../Card/__snapshots__/Card.test.tsx.snap | 3 - .../__snapshots__/CheckboxItem.test.tsx.snap | 2 - .../Drawer/DrawerCollapsedItem.test.tsx | 18 +++++- src/components/__tests__/FABMenu.test.tsx | 5 +- src/components/__tests__/ListItem.test.tsx | 4 +- src/components/__tests__/MenuItem.test.tsx | 1 + .../__snapshots__/Banner.test.tsx.snap | 12 ---- .../BottomNavigation.test.tsx.snap | 44 ------------- .../__snapshots__/Button.test.tsx.snap | 38 ------------ .../__snapshots__/Chip.test.tsx.snap | 12 ---- .../__snapshots__/DataTable.test.tsx.snap | 30 --------- .../__tests__/__snapshots__/FAB.test.tsx.snap | 26 -------- .../__snapshots__/FABExtended.test.tsx.snap | 18 ------ .../__snapshots__/FABMenu.test.tsx.snap | 21 ------- .../__snapshots__/IconButton.test.tsx.snap | 11 ---- .../__snapshots__/ListAccordion.test.tsx.snap | 1 - .../__snapshots__/ListItem.test.tsx.snap | 2 - .../__snapshots__/Menu.test.tsx.snap | 12 ---- .../__snapshots__/MenuItem.test.tsx.snap | 10 --- .../__snapshots__/ProgressBar.test.tsx.snap | 8 --- .../__snapshots__/Searchbar.test.tsx.snap | 18 ------ .../SegmentedButton.test.tsx.snap | 2 - .../__snapshots__/Snackbar.test.tsx.snap | 3 - .../__snapshots__/TextInput.test.tsx.snap | 16 ----- .../__snapshots__/ToggleButton.test.tsx.snap | 6 -- 54 files changed, 164 insertions(+), 399 deletions(-) diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index a4d7123a09..a401ad8de8 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -71,12 +71,61 @@ You can use the component's color prop where available, or override the correspo ### Test IDs -Some hardcoded and generated test IDs have been removed for the following components: - -- `Appbar.Header`: `${testID}-root-layer` -- `Surface`: `surface` and `${testID}-outer-layer` - -You can specify a `testID` explicitly and use that value to query the component. +Hardcoded default test IDs have been removed for the components listed below. Many of these components also derive test IDs for their internal parts by appending a suffix to the `testID` prop (e.g. `${testID}-container`). Since `testID` is no longer defaulted to a hardcoded value, none of these derived test IDs are set either unless you pass a `testID` explicitly — so all queries by the IDs below will stop matching: + +- `Appbar.Content`: `appbar-content` + - `appbar-content-title-text` +- `Appbar.Header`: `appbar-header` + - `appbar-header-root-layer` +- `BottomNavigation`: `bottom-navigation` + - `bottom-navigation-bar` +- `BottomNavigation.Bar`: `bottom-navigation-bar` + - `bottom-navigation-bar-content` + - `bottom-navigation-bar-content-wrapper` +- `Button`: `button` + - `button-container` + - `button-icon-container` + - `button-text` +- `Card`: `card` + - `card-container` + - `card-outline` +- `Chip`: `chip` + - `chip-container` +- `Drawer.CollapsedItem`: `drawer-collapsed-item` + - `drawer-collapsed-item-outline` + - `drawer-collapsed-item-container` +- `FAB`: `floating-action-button` + - `floating-action-button-container` + - `floating-action-button-text` +- `FAB.Extended`: `extended-floating-action-button` + - `extended-floating-action-button-container` + - `extended-floating-action-button-text` +- `FAB.Menu`: `floating-action-button-menu` +- `IconButton`: `icon-button` + - `icon-button-container` + - `icon-button-icon` (and `icon-button-icon-previous` / `icon-button-icon-current` when `animated`) +- `Menu`: `menu` + - `menu-view` + - `menu-surface` +- `Menu.Item`: `menu-item` + - `menu-item-title` +- `Modal`: `modal` + - `modal-backdrop` + - `modal-wrapper` + - `modal-surface` +- `ProgressBar`: `progress-bar` + - `progress-bar-fill` +- `Searchbar`: `search-bar` + - `search-bar-container` + - `search-bar-icon` + - `search-bar-icon-wrapper` + - `search-bar-clear-icon` + - `search-bar-trailering-icon` + - `search-bar-divider` +- `Surface`: `surface` + - `surface-outer-layer` + +You can specify a `testID` explicitly to restore both the component's own test ID and all of its derived test IDs above, using the same suffixes. ## Components diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index fa1cad5ebc..386f64158f 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -95,7 +95,7 @@ const AppbarContent = ({ titleMaxFontSizeMultiplier, mode = 'small', theme: themeOverrides, - testID = 'appbar-content', + testID, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); @@ -135,7 +135,7 @@ const AppbarContent = ({ numberOfLines={1} accessible role={onPress ? 'none' : 'heading'} - testID={`${testID}-title-text`} + testID={testID ? `${testID}-title-text` : undefined} maxFontSizeMultiplier={titleMaxFontSizeMultiplier} > {title} diff --git a/src/components/Appbar/AppbarHeader.tsx b/src/components/Appbar/AppbarHeader.tsx index 1a7c7c04dc..9845238299 100644 --- a/src/components/Appbar/AppbarHeader.tsx +++ b/src/components/Appbar/AppbarHeader.tsx @@ -85,7 +85,7 @@ const AppbarHeader = ({ mode = Platform.OS === 'ios' ? 'center-aligned' : 'small', elevated = false, theme: themeOverrides, - testID = 'appbar-header', + testID, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index b513748508..4556b2bd1b 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -335,7 +335,7 @@ const BottomNavigation = ({ safeAreaInsets, labelMaxFontSizeMultiplier = 1, compact: compactProp, - testID = 'bottom-navigation', + testID, theme: themeOverrides, getLazy = ({ route }: { route: Route }) => route.lazy, }: Props) => { @@ -579,7 +579,7 @@ const BottomNavigation = ({ safeAreaInsets={safeAreaInsets} labelMaxFontSizeMultiplier={labelMaxFontSizeMultiplier} compact={compact} - testID={`${testID}-bar`} + testID={testID ? `${testID}-bar` : undefined} theme={theme} /> diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index 04e72da648..eff4e9780b 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -323,7 +323,7 @@ const BottomNavigationBar = ({ safeAreaInsets, labelMaxFontSizeMultiplier = 1, compact: compactProp, - testID = 'bottom-navigation-bar', + testID, theme: themeOverrides, }: Props) => { const theme = useInternalTheme(themeOverrides); @@ -495,7 +495,7 @@ const BottomNavigationBar = ({ > ({ }, ]} role={'tablist'} - testID={`${testID}-content-wrapper`} + testID={testID ? `${testID}-content-wrapper` : undefined} > {routes.map((route, index) => { const focused = navigationState.index === index; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 458e324228..11cda00ccc 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -184,7 +184,7 @@ const Button = ({ uppercase: uppercaseProp, contentStyle, labelStyle, - testID = 'button', + testID, accessible, background, maxFontSizeMultiplier, @@ -292,7 +292,7 @@ const Button = ({ {icon && loading !== true ? ( - + {isMode('outlined') && ( diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index c5cf09244e..40cb460ffb 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -196,7 +196,7 @@ const Chip = ({ textStyle, style, theme: themeOverrides, - testID = 'chip', + testID, selectedColor, showSelectedCheck = true, ellipsizeMode, @@ -281,7 +281,7 @@ const Chip = ({ elevation={elevation} transitionDuration={elevationTransitionDuration} {...rest} - testID={`${testID}-container`} + testID={testID ? `${testID}-container` : undefined} theme={theme} > { const theme = useInternalTheme(themeOverrides); @@ -109,14 +109,14 @@ const CrossFadeIcon = ({ {hasPreviousIcon ? ( ) : null} diff --git a/src/components/DataTable/DataTableCell.tsx b/src/components/DataTable/DataTableCell.tsx index b5842a07c9..d1d730e0b3 100644 --- a/src/components/DataTable/DataTableCell.tsx +++ b/src/components/DataTable/DataTableCell.tsx @@ -111,7 +111,7 @@ const CellContent = ({ style={textStyle} numberOfLines={1} maxFontSizeMultiplier={maxFontSizeMultiplier} - testID={`${testID}-text-container`} + testID={testID ? `${testID}-text-container` : undefined} > {children} diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index 4553ba9f95..dccb8cc555 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -111,7 +111,7 @@ const DrawerCollapsedItem = ({ disabled, 'aria-label': ariaLabel, badge = false, - testID = 'drawer-collapsed-item', + testID, labelMaxFontSizeMultiplier, ...rest }: Props) => { @@ -195,12 +195,12 @@ const DrawerCollapsedItem = ({ style, animatedOutlineStyle, ]} - testID={`${testID}-outline`} + testID={testID ? `${testID}-outline` : undefined} /> {badge !== false && ( diff --git a/src/components/FAB/Extended.tsx b/src/components/FAB/Extended.tsx index fb58e730e6..17da09bd56 100644 --- a/src/components/FAB/Extended.tsx +++ b/src/components/FAB/Extended.tsx @@ -169,7 +169,7 @@ const Extended = ({ labelMaxFontSizeMultiplier, background, style, - testID = 'extended-floating-action-button', + testID, theme: themeOverrides, ref, }: Props) => { diff --git a/src/components/FAB/FAB.tsx b/src/components/FAB/FAB.tsx index 036edd57d9..fb30396939 100644 --- a/src/components/FAB/FAB.tsx +++ b/src/components/FAB/FAB.tsx @@ -131,7 +131,7 @@ const FAB = ({ 'aria-expanded': ariaExpanded, background, style, - testID = 'floating-action-button', + testID, theme, ref, }: Props) => ( diff --git a/src/components/FAB/Menu.tsx b/src/components/FAB/Menu.tsx index 5a08323060..438d7c2f1a 100644 --- a/src/components/FAB/Menu.tsx +++ b/src/components/FAB/Menu.tsx @@ -446,7 +446,6 @@ const MorphingTrigger = ({ }, visible ? styles.pointerEventsBoxNone : styles.pointerEventsNone, ]} - testID={testID} > { const theme = useInternalTheme(themeOverrides); diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index 5a7d3b81bf..6a07babb47 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -212,7 +212,7 @@ const Shell = ({ overlay, children, style, - testID = 'fab-shell', + testID, theme: themeOverrides, ref, }: ShellProps) => { @@ -319,7 +319,7 @@ const Shell = ({ visible ? styles.pointerEventsAuto : styles.pointerEventsNone, ]} elevation={elevation} - testID={`${testID}-container`} + testID={testID ? `${testID}-container` : undefined} theme={theme} > diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index 62b86d3754..a55c75e6c6 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -125,7 +125,7 @@ const IconButton = ({ mode, style, theme: themeOverrides, - testID = 'icon-button', + testID, loading = false, contentStyle, ref, @@ -161,7 +161,7 @@ const IconButton = ({ return ( ) : ( - + )} diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 97fb1ccc82..740b3df5d2 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -247,7 +247,7 @@ const ListItem = ({ : null} {renderTitle()} diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index b17b3d2b9d..12b3c536c7 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -186,7 +186,7 @@ const Menu = ({ visible, statusBarHeight, overlayAccessibilityLabel = 'Close menu', - testID = 'menu', + testID, anchor, onDismiss, anchorPosition, @@ -691,7 +691,7 @@ const Menu = ({ style={[styles.wrapper, positionStyle, style]} pointerEvents={pointerEvents} onAccessibilityEscape={onDismiss} - testID={`${testID}-view`} + testID={testID ? `${testID}-view` : undefined} > diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 0a34198d2e..da0975a44a 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -135,7 +135,7 @@ function Modal({ contentElevation, style, theme: themeOverrides, - testID = 'modal', + testID, }: Props) { const theme = useInternalTheme(themeOverrides); @@ -235,7 +235,7 @@ function Modal({ onPress={dismissable ? onDismissCallback : undefined} importantForAccessibility="no" style={[styles.backdrop, backdropStyle, backdropTransitionStyle]} - testID={`${testID}-backdrop`} + testID={testID ? `${testID}-backdrop` : undefined} /> { const theme = useInternalTheme(themeOverrides); @@ -212,7 +212,7 @@ const ProgressBar = ({ > {width ? ( & { right?: (props: { color: ColorValue; style: Style; - testID: string; + testID?: string; }) => React.ReactNode; /** * @supported Available in v5.x with theme version 3 @@ -183,7 +183,7 @@ const Searchbar = ({ theme: themeOverrides, value, loading = false, - testID = 'search-bar', + testID, ref, ...rest }: Props) => { @@ -235,7 +235,7 @@ const Searchbar = ({ backgroundColor={theme.colors.surfaceContainerHigh} borderRadius={isBarMode ? theme.shapes.corner.extraLarge : cornerNone} style={[styles.container, style]} - testID={`${testID}-container`} + testID={testID ? `${testID}-container` : undefined} elevation={elevation} theme={theme} > @@ -257,7 +257,7 @@ const Searchbar = ({ } theme={theme} aria-label={searchAccessibilityLabel} - testID={`${testID}-icon`} + testID={testID ? `${testID}-icon` : undefined} /> )) } - testID={`${testID}-clear-icon`} + testID={testID ? `${testID}-clear-icon` : undefined} role="button" theme={theme} /> @@ -331,7 +331,7 @@ const Searchbar = ({ iconColor={traileringIconColor || colors.onSurfaceVariant} icon={traileringIcon} aria-label={traileringIconAccessibilityLabel} - testID={`${testID}-trailering-icon`} + testID={testID ? `${testID}-trailering-icon` : undefined} /> ) : null} {isBarMode && @@ -345,7 +345,7 @@ const Searchbar = ({ backgroundColor: colors.outline, }, ]} - testID={`${testID}-divider`} + testID={testID ? `${testID}-divider` : undefined} /> )} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 9a68eeca3e..4b851de75a 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -224,7 +224,7 @@ const SegmentedButtonItem = ({ > {showCheckedIcon ? ( @@ -232,7 +232,7 @@ const SegmentedButtonItem = ({ ) : null} {showIcon ? ( @@ -244,7 +244,7 @@ const SegmentedButtonItem = ({ selectable={false} numberOfLines={1} maxFontSizeMultiplier={labelMaxFontSizeMultiplier} - testID={`${testID}-label`} + testID={testID ? `${testID}-label` : undefined} > {label} diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index da621369d2..fc43a08bf2 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -339,7 +339,7 @@ const Snackbar = ({ } aria-label={iconAccessibilityLabel} style={styles.icon} - testID={`${testID}-icon`} + testID={testID ? `${testID}-icon` : undefined} /> ) : null} diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 5bba9563f4..396ffd6734 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -196,8 +196,8 @@ describe('AppbarAction', () => { ); // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('cross-fade-icon-current').props - .children; + const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') + .props.children; // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(appbarActionIcon.props.color).toBe( getTheme().colors.onSurfaceVariant @@ -211,8 +211,8 @@ describe('AppbarAction', () => { ); // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('cross-fade-icon-current').props - .children; + const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') + .props.children; // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(appbarActionIcon.props.color).toBe(getTheme().colors.onSurface); }); @@ -224,8 +224,8 @@ describe('AppbarAction', () => { ); // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('cross-fade-icon-current').props - .children; + const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') + .props.children; // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(appbarActionIcon.props.color).toBe('purple'); }); @@ -237,8 +237,9 @@ describe('AppbarAction', () => { ); // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarBackActionIcon = screen.getByTestId('cross-fade-icon-current') - .props.children; + const appbarBackActionIcon = screen.getByTestId( + 'appbar-action-icon-current' + ).props.children; // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(appbarBackActionIcon.props.color).toBe('purple'); }); @@ -249,7 +250,7 @@ describe('AppbarContent', () => { it(`should render text component with appropriate variant for ${mode} mode`, async () => { await render( - + ); diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index 882652f92d..ad5805e1e6 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -140,7 +140,6 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` }, ] } - testID="search-bar-container" > @@ -357,7 +353,6 @@ exports[`Appbar does not pass any additional props to Searchbar 1`] = ` false, ] } - testID="search-bar-icon-wrapper" > Examples @@ -832,7 +820,6 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A undefined, ] } - testID="icon-button-container" > { const onIndexChange = jest.fn(); await render( { await render( { await render( { it('renders custom background color passed to barStyle property', async () => { await render( ); @@ -498,11 +501,11 @@ it('uses the rendered bar height when hiding it for the keyboard', async () => { it('renders a single tab', async () => { await render( ); @@ -527,12 +530,12 @@ it('renders bottom navigation with getLazy', async () => { it('applies maxTabBarWidth styling if compact prop is truthy', async () => { await render( route.key === 'key-2'} shifting={false} - testID="bottom-navigation" compact /> ); @@ -547,12 +550,12 @@ it('applies maxTabBarWidth styling if compact prop is truthy', async () => { it('does not apply maxTabBarWidth styling if compact prop is falsy', async () => { await render( route.key === 'key-2'} shifting={false} - testID="bottom-navigation" compact={false} /> ); @@ -567,11 +570,11 @@ it('does not apply maxTabBarWidth styling if compact prop is falsy', async () => it('renders bar content when shifting is enabled', async () => { await render( route.key === 'key-2'} - testID="bottom-navigation" shifting /> ); @@ -582,11 +585,11 @@ it('renders bar content when shifting is enabled', async () => { it('does not render legacy ripple overlay when shifting is disabled', async () => { await render( route.key === 'key-2'} - testID="bottom-navigation" shifting={false} /> ); diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index 74637a8a53..16163ba257 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -92,7 +92,7 @@ describe('Card', () => { it('renders with a content style', async () => { await render( - + Content ); @@ -101,13 +101,13 @@ describe('Card', () => { }); it('does not render a disabled accessibility state', async () => { - await render({null}); + await render({null}); expect(screen.getByTestId('card')).toBeEnabled(); }); it('does render a disabled accessibility state', async () => { await render( - {}} disabled> + {}} disabled> {null} ); diff --git a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap index 5c39f6bf05..0554292cee 100644 --- a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap +++ b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap @@ -41,7 +41,6 @@ exports[`Card renders an outlined card 1`] = ` }, ] } - testID="card-container" > `; diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index 9617ebefbd..bc5b884910 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -269,7 +269,6 @@ exports[`can render leading checkbox control 1`] = ` ], ] } - testID="undefined-text" > Default with leading control @@ -367,7 +366,6 @@ exports[`renders unchecked 1`] = ` ], ] } - testID="undefined-text" > Unchecked Button diff --git a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx index 3df20be862..fd0263b5e2 100644 --- a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx +++ b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx @@ -7,6 +7,7 @@ describe('DrawerCollapsedItem', () => { it('should have regular outline if label is specified', async () => { await render( { it('should have rounded outline if label is not specified', async () => { await render( - + ); expect(screen.getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ @@ -30,7 +35,11 @@ describe('DrawerCollapsedItem', () => { it('should display unfocused icon in inactive state, if unfocused icon is specified', async () => { await render( - + ); expect( @@ -41,7 +50,9 @@ describe('DrawerCollapsedItem', () => { }); it('should display focused icon in inactive state, if unfocused icon is not specified', async () => { - await render(); + await render( + + ); expect( // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. @@ -53,6 +64,7 @@ describe('DrawerCollapsedItem', () => { it('should display focused icon in active state', async () => { await render( { ); - // Shell's TouchableRipple uses the default testID 'fab-shell' await userEvent.press(screen.getByTestId('fab-shell')); expect(onTriggerPress).toHaveBeenCalledTimes(1); }); @@ -186,7 +185,7 @@ it('calls onDismiss when trigger is pressed while menu is open', async () => { ); diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index b50f4e7d3f..e0e1cb5303 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -155,7 +155,9 @@ it('calling onPress on ListItem right component', async () => { title="First Item" description="Item description" testID={testID} - right={() => } + right={() => ( + + )} /> ); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index ba66e705a7..4662ce968e 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -40,6 +40,7 @@ describe('Menu Item', () => { await render( {}} diff --git a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap index 94a5339bab..71e77e2936 100644 --- a/src/components/__tests__/__snapshots__/Banner.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Banner.test.tsx.snap @@ -228,7 +228,6 @@ exports[`render visible banner, with custom theme 1`] = ` }, ] } - testID="button-container" > first @@ -840,7 +837,6 @@ exports[`renders visible banner, with action buttons and with image 1`] = ` }, ] } - testID="button-container" > first @@ -1234,7 +1228,6 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` }, ] } - testID="button-container" > first @@ -1444,7 +1435,6 @@ exports[`renders visible banner, with action buttons and without image 1`] = ` }, ] } - testID="button-container" > second diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index 82f7a0cc74..7ffd6c9752 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -11,7 +11,6 @@ exports[`allows customizing Route's type via generics 1`] = ` undefined, ] } - testID="bottom-navigation" > Button with accessibility hint @@ -256,7 +253,6 @@ exports[`renders button with an accessibility label 1`] = ` }, ] } - testID="button-container" > Button with accessibility label @@ -463,7 +457,6 @@ exports[`renders button with button color 1`] = ` }, ] } - testID="button-container" > Custom Button @@ -670,7 +661,6 @@ exports[`renders button with color 1`] = ` }, ] } - testID="button-container" > Custom Button @@ -1084,7 +1072,6 @@ exports[`renders button with icon 1`] = ` }, ] } - testID="button-container" > Icon Button @@ -1340,7 +1324,6 @@ exports[`renders button with icon in reverse order 1`] = ` }, ] } - testID="button-container" > Right Icon @@ -1598,7 +1578,6 @@ exports[`renders contained contained with mode 1`] = ` }, ] } - testID="button-container" > Contained Button @@ -1806,7 +1783,6 @@ exports[`renders disabled button 1`] = ` }, ] } - testID="button-container" > Disabled Button @@ -2013,7 +1987,6 @@ exports[`renders loading button 1`] = ` }, ] } - testID="button-container" > Loading Button @@ -2424,7 +2395,6 @@ exports[`renders outlined button with mode 1`] = ` }, ] } - testID="button-container" > Outlined Button @@ -2632,7 +2600,6 @@ exports[`renders text button by default 1`] = ` }, ] } - testID="button-container" > Text Button @@ -2839,7 +2804,6 @@ exports[`renders text button with mode 1`] = ` }, ] } - testID="button-container" > Text Button diff --git a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap index 04359a41f9..04687e301f 100644 --- a/src/components/__tests__/__snapshots__/Chip.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Chip.test.tsx.snap @@ -49,7 +49,6 @@ exports[`renders chip with close button 1`] = ` }, ] } - testID="chip-container" > Cupcake @@ -139,7 +138,6 @@ exports[`DataTable.Cell renders right aligned data table cell 1`] = ` undefined, ] } - testID="undefined-text-container" > 356 @@ -400,7 +398,6 @@ exports[`DataTable.Pagination renders data table pagination 1`] = ` undefined, ] } - testID="icon-button-container" > 2 @@ -1838,7 +1816,6 @@ exports[`DataTable.Pagination renders data table pagination with options select undefined, ] } - testID="icon-button-container" > New message @@ -384,7 +381,6 @@ exports[`renders extended FAB expanded 1`] = ` }, ] } - testID="extended-floating-action-button-container" > New message @@ -713,7 +707,6 @@ exports[`renders extended FAB large size 1`] = ` }, ] } - testID="extended-floating-action-button-container" > New message @@ -1042,7 +1033,6 @@ exports[`renders extended FAB medium size 1`] = ` }, ] } - testID="extended-floating-action-button-container" > New message @@ -1371,7 +1359,6 @@ exports[`renders extended FAB not visible 1`] = ` }, ] } - testID="extended-floating-action-button-container" > New message @@ -1700,7 +1685,6 @@ exports[`renders extended FAB transitioning to collapsed 1`] = ` }, ] } - testID="extended-floating-action-button-container" > New message diff --git a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap index 059c17b9f4..23b577a775 100644 --- a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap @@ -21,7 +21,6 @@ exports[`renders FAB.Menu closed 1`] = ` }, ] } - testID="floating-action-button-menu" > Open menu @@ -282,7 +279,6 @@ exports[`renders visible menu 1`] = ` }, ] } - testID="button-container" > Open menu @@ -517,7 +511,6 @@ exports[`renders visible menu 1`] = ` undefined, ] } - testID="menu-view" > Undo @@ -825,7 +815,6 @@ exports[`renders visible menu 1`] = ` ], ] } - testID="menu-item" > Redo diff --git a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap index 6977abf932..c316a38599 100644 --- a/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/MenuItem.test.tsx.snap @@ -51,7 +51,6 @@ exports[`Menu Item renders menu item 1`] = ` ], ] } - testID="menu-item" > Redo @@ -215,7 +213,6 @@ exports[`Menu Item renders menu item 1`] = ` ], ] } - testID="menu-item" > Undo @@ -379,7 +375,6 @@ exports[`Menu Item renders menu item 1`] = ` ], ] } - testID="menu-item" > Cut @@ -543,7 +537,6 @@ exports[`Menu Item renders menu item 1`] = ` ], ] } - testID="menu-item" > Copy @@ -707,7 +699,6 @@ exports[`Menu Item renders menu item 1`] = ` ], ] } - testID="menu-item" > Paste diff --git a/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap b/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap index 22fa984fd8..b7fea2ca34 100644 --- a/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap @@ -10,7 +10,6 @@ exports[`renders colored progress bar 1`] = ` onLayout={[Function]} role="progressbar" style={false} - testID="progress-bar" > @@ -56,7 +54,6 @@ exports[`renders hidden progress bar 1`] = ` onLayout={[Function]} role="progressbar" style={false} - testID="progress-bar" > @@ -99,7 +95,6 @@ exports[`renders indeterminate progress bar 1`] = ` onLayout={[Function]} role="progressbar" style={false} - testID="progress-bar" > @@ -145,7 +139,6 @@ exports[`renders progress bar with specific progress 1`] = ` onLayout={[Function]} role="progressbar" style={false} - testID="progress-bar" > diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap index c324b1874c..27436d6599 100644 --- a/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Searchbar.test.tsx.snap @@ -42,7 +42,6 @@ exports[`activity indicator snapshot test 1`] = ` }, ] } - testID="search-bar-container" > @@ -486,7 +482,6 @@ exports[`renders with placeholder 1`] = ` }, ] } - testID="search-bar-container" > @@ -703,7 +695,6 @@ exports[`renders with placeholder 1`] = ` false, ] } - testID="search-bar-icon-wrapper" > @@ -1084,7 +1069,6 @@ exports[`renders with text 1`] = ` false, ] } - testID="search-bar-icon-wrapper" > @@ -260,7 +259,6 @@ exports[`renders segmented button 1`] = ` ], ] } - testID="undefined-label" /> diff --git a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap index 095b214bb2..353efa0fed 100644 --- a/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap @@ -528,7 +528,6 @@ exports[`renders snackbar with action button 1`] = ` }, ] } - testID="button-container" > Undo diff --git a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap index 9a4edcd5c1..6efc467066 100644 --- a/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/TextInput.test.tsx.snap @@ -176,7 +176,6 @@ exports[`renders filled TextInput with TextInput.Icon accessories 1`] = ` false, ] } - testID="icon-button-container" > Date: Wed, 9 Sep 2026 13:55:40 +0200 Subject: [PATCH 68/94] fix: support emphasized text variants in TypeScript and correct bold fonts on Android(#5068) --- docs/6.x/docs/guides/fonts.md | 24 +++++++- src/components/Typography/Text.tsx | 3 + src/theme/__tests__/fonts.test.js | 47 ++++++++++++++++ src/theme/tokens/ref/typeface.ts | 12 ++++ src/theme/tokens/sys/typography.ts | 90 ++++++++++++++++-------------- src/theme/types/typography.ts | 17 +++++- 6 files changed, 149 insertions(+), 44 deletions(-) diff --git a/docs/6.x/docs/guides/fonts.md b/docs/6.x/docs/guides/fonts.md index 94a8eb40fe..a72f42c83e 100644 --- a/docs/6.x/docs/guides/fonts.md +++ b/docs/6.x/docs/guides/fonts.md @@ -67,6 +67,13 @@ Platform.select({ }), ``` +Material Design 3 typescale uses two font families: + +- **Brand**: Display, Headline and Title Large +- **Plain**: Title Medium/Small, Label and Body + +The default theme uses the platform default for both font families. + ::: - #### Display @@ -291,7 +298,7 @@ Platform.select({ "fontFamily": "Font", "fontSize": 16, "fontWeight": "400", - "letterSpacing": 0.15, + "letterSpacing": 0.5, "lineHeight": 24, } ``` @@ -299,6 +306,21 @@ Platform.select({ +- #### Emphasized + +Each variant also has an `Emphasized` counterpart with a heavier font weight: `displayLargeEmphasized`, +`bodyMediumEmphasized` etc. + +```json +"bodyMediumEmphasized": { + "fontFamily": "Font", + "fontSize": 14, + "fontWeight": "500", + "letterSpacing": 0.25, + "lineHeight": 20, +} +``` + :::info If any component uses Paper's `Text` component, without specified variant, then `default` variant is applied: diff --git a/src/components/Typography/Text.tsx b/src/components/Typography/Text.tsx index a35934ac3a..73572d1601 100644 --- a/src/components/Typography/Text.tsx +++ b/src/components/Typography/Text.tsx @@ -25,6 +25,9 @@ export type Props = React.ComponentProps & { * Label: `labelLarge`, `labelMedium`, `labelSmall` * * Body: `bodyLarge`, `bodyMedium`, `bodySmall` + * + * Each variant also has an `Emphasized` counterpart with a heavier font weight. + * e.g. `displayLargeEmphasized`, `bodyMediumEmphasized` etc. */ variant?: VariantProp; children: React.ReactNode; diff --git a/src/theme/__tests__/fonts.test.js b/src/theme/__tests__/fonts.test.js index 86d1430405..6189b2ca96 100644 --- a/src/theme/__tests__/fonts.test.js +++ b/src/theme/__tests__/fonts.test.js @@ -340,6 +340,53 @@ describe('configureFonts', () => { }); }); + it('resolves the Android font families per MD3 family assignment', () => { + mockPlatform('android'); + const { typescale } = loadFonts(); + + for (const variant of ['bodyLarge', 'bodyMedium', 'bodySmall']) { + expect(typescale[variant]).toMatchObject({ + fontFamily: 'sans-serif', + fontWeight: '400', + }); + } + + for (const variant of [ + 'displayLargeEmphasized', + 'displayMediumEmphasized', + 'displaySmallEmphasized', + 'headlineLargeEmphasized', + 'headlineMediumEmphasized', + 'headlineSmallEmphasized', + 'titleLargeEmphasized', + ]) { + expect(typescale[variant]).toMatchObject({ + fontFamily: 'sans-serif-medium', + fontWeight: '500', + }); + } + + for (const variant of ['displayLarge', 'headlineLarge', 'titleLarge']) { + expect(typescale[variant]).toMatchObject({ + fontFamily: 'sans-serif', + fontWeight: '400', + }); + } + + for (const variant of [ + 'titleMediumEmphasized', + 'titleSmallEmphasized', + 'labelLargeEmphasized', + 'labelMediumEmphasized', + 'labelSmallEmphasized', + ]) { + expect(typescale[variant]).toMatchObject({ + fontFamily: 'sans-serif', + fontWeight: '700', + }); + } + }); + it('applies flat properties to every variant when the config also has per-variant entries', () => { mockPlatform('ios'); const { configureFonts, typescale } = loadFonts(); diff --git a/src/theme/tokens/ref/typeface.ts b/src/theme/tokens/ref/typeface.ts index fdf6993ae8..f62389b09b 100644 --- a/src/theme/tokens/ref/typeface.ts +++ b/src/theme/tokens/ref/typeface.ts @@ -9,8 +9,18 @@ export const typeface = { ios: 'System', default: 'sans-serif', }), + brandMedium: Platform.select({ + web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', + ios: 'System', + default: 'sans-serif-medium', + }), weightRegular: '400', + plainRegular: Platform.select({ + web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', + ios: 'System', + default: 'sans-serif', + }), plainMedium: Platform.select({ web: 'Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif', ios: 'System', @@ -21,7 +31,9 @@ export const typeface = { weightBold: '700', } satisfies { brandRegular?: string; + brandMedium?: string; weightRegular: Font['fontWeight']; + plainRegular?: string; plainMedium?: string; weightMedium: Font['fontWeight']; weightBold: Font['fontWeight']; diff --git a/src/theme/tokens/sys/typography.ts b/src/theme/tokens/sys/typography.ts index 289cbdedef..90b1a86aba 100644 --- a/src/theme/tokens/sys/typography.ts +++ b/src/theme/tokens/sys/typography.ts @@ -1,26 +1,38 @@ import type { Typescale } from '../../types'; import { typeface } from '../ref/typeface'; -const regularType = { +const brandRegularType = { fontFamily: typeface.brandRegular, letterSpacing: 0, fontWeight: typeface.weightRegular, }; -const mediumType = { +const brandMediumType = { + fontFamily: typeface.brandMedium, + letterSpacing: 0, + fontWeight: typeface.weightMedium, +}; + +const plainRegularType = { + fontFamily: typeface.plainRegular, + letterSpacing: 0, + fontWeight: typeface.weightRegular, +}; + +const plainMediumType = { fontFamily: typeface.plainMedium, letterSpacing: 0.15, fontWeight: typeface.weightMedium, }; -const emphasizedMediumType = { +const plainMediumEmphasizedType = { fontFamily: typeface.plainMedium, letterSpacing: 0, fontWeight: typeface.weightMedium, }; -const emphasizedBoldType = { - fontFamily: typeface.plainMedium, +const plainBoldType = { + fontFamily: typeface.plainRegular, letterSpacing: 0, fontWeight: typeface.weightBold, }; @@ -28,190 +40,184 @@ const emphasizedBoldType = { /** md.sys.typescale.* */ export const typescale = { displayLarge: { - ...regularType, + ...brandRegularType, letterSpacing: -0.25, lineHeight: 64, fontSize: 57, }, displayMedium: { - ...regularType, + ...brandRegularType, lineHeight: 52, fontSize: 45, }, displaySmall: { - ...regularType, + ...brandRegularType, lineHeight: 44, fontSize: 36, }, headlineLarge: { - ...regularType, + ...brandRegularType, lineHeight: 40, fontSize: 32, }, headlineMedium: { - ...regularType, + ...brandRegularType, lineHeight: 36, fontSize: 28, }, headlineSmall: { - ...regularType, + ...brandRegularType, lineHeight: 32, fontSize: 24, }, titleLarge: { - ...regularType, + ...brandRegularType, lineHeight: 28, fontSize: 22, }, titleMedium: { - ...mediumType, + ...plainMediumType, lineHeight: 24, fontSize: 16, }, titleSmall: { - ...mediumType, + ...plainMediumType, letterSpacing: 0.1, lineHeight: 20, fontSize: 14, }, labelLarge: { - ...mediumType, + ...plainMediumType, letterSpacing: 0.1, lineHeight: 20, fontSize: 14, }, labelMedium: { - ...mediumType, + ...plainMediumType, letterSpacing: 0.5, lineHeight: 16, fontSize: 12, }, labelSmall: { - ...mediumType, + ...plainMediumType, letterSpacing: 0.5, lineHeight: 16, fontSize: 11, }, bodyLarge: { - ...mediumType, - fontWeight: typeface.weightRegular, - fontFamily: typeface.brandRegular, + ...plainRegularType, letterSpacing: 0.5, lineHeight: 24, fontSize: 16, }, bodyMedium: { - ...mediumType, - fontWeight: typeface.weightRegular, - fontFamily: typeface.brandRegular, + ...plainRegularType, letterSpacing: 0.25, lineHeight: 20, fontSize: 14, }, bodySmall: { - ...mediumType, - fontWeight: typeface.weightRegular, - fontFamily: typeface.brandRegular, + ...plainRegularType, letterSpacing: 0.4, lineHeight: 16, fontSize: 12, }, displayLargeEmphasized: { - ...emphasizedMediumType, + ...brandMediumType, letterSpacing: -0.25, lineHeight: 64, fontSize: 57, }, displayMediumEmphasized: { - ...emphasizedMediumType, + ...brandMediumType, lineHeight: 52, fontSize: 45, }, displaySmallEmphasized: { - ...emphasizedMediumType, + ...brandMediumType, lineHeight: 44, fontSize: 36, }, headlineLargeEmphasized: { - ...emphasizedMediumType, + ...brandMediumType, lineHeight: 40, fontSize: 32, }, headlineMediumEmphasized: { - ...emphasizedMediumType, + ...brandMediumType, lineHeight: 36, fontSize: 28, }, headlineSmallEmphasized: { - ...emphasizedMediumType, + ...brandMediumType, lineHeight: 32, fontSize: 24, }, titleLargeEmphasized: { - ...emphasizedMediumType, + ...brandMediumType, lineHeight: 28, fontSize: 22, }, titleMediumEmphasized: { - ...emphasizedBoldType, + ...plainBoldType, letterSpacing: 0.15, lineHeight: 24, fontSize: 16, }, titleSmallEmphasized: { - ...emphasizedBoldType, + ...plainBoldType, letterSpacing: 0.1, lineHeight: 20, fontSize: 14, }, labelLargeEmphasized: { - ...emphasizedBoldType, + ...plainBoldType, letterSpacing: 0.1, lineHeight: 20, fontSize: 14, }, labelMediumEmphasized: { - ...emphasizedBoldType, + ...plainBoldType, letterSpacing: 0.5, lineHeight: 16, fontSize: 12, }, labelSmallEmphasized: { - ...emphasizedBoldType, + ...plainBoldType, letterSpacing: 0.5, lineHeight: 16, fontSize: 11, }, bodyLargeEmphasized: { - ...emphasizedMediumType, + ...plainMediumEmphasizedType, letterSpacing: 0.5, lineHeight: 24, fontSize: 16, }, bodyMediumEmphasized: { - ...emphasizedMediumType, + ...plainMediumEmphasizedType, letterSpacing: 0.25, lineHeight: 20, fontSize: 14, }, bodySmallEmphasized: { - ...emphasizedMediumType, + ...plainMediumEmphasizedType, letterSpacing: 0.4, lineHeight: 16, fontSize: 12, }, default: { - ...regularType, + ...brandRegularType, }, }; diff --git a/src/theme/types/typography.ts b/src/theme/types/typography.ts index 57fcf3f67e..f7ad73b80f 100644 --- a/src/theme/types/typography.ts +++ b/src/theme/types/typography.ts @@ -37,7 +37,22 @@ export type TypescaleKey = | 'labelSmall' | 'bodyLarge' | 'bodyMedium' - | 'bodySmall'; + | 'bodySmall' + | 'displayLargeEmphasized' + | 'displayMediumEmphasized' + | 'displaySmallEmphasized' + | 'headlineLargeEmphasized' + | 'headlineMediumEmphasized' + | 'headlineSmallEmphasized' + | 'titleLargeEmphasized' + | 'titleMediumEmphasized' + | 'titleSmallEmphasized' + | 'labelLargeEmphasized' + | 'labelMediumEmphasized' + | 'labelSmallEmphasized' + | 'bodyLargeEmphasized' + | 'bodyMediumEmphasized' + | 'bodySmallEmphasized'; export type TypescaleStyle = { fontFamily: string; From 46519b071dbf5579d216256c62b77b8949c2b201 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 9 Sep 2026 14:50:04 +0200 Subject: [PATCH 69/94] fix: preserve reference for theme if overrides didn't change --- package.json | 1 + src/components/Card/CardContent.tsx | 4 +- src/components/Portal/Portal.tsx | 58 ++++++------- src/components/__tests__/Portal.test.tsx | 36 ++++++++ src/theme/__tests__/provider.test.ts | 100 ++++++++++++++++++++++- src/theme/provider.tsx | 17 ++-- yarn.lock | 1 + 7 files changed, 171 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 6db11f9fef..c0c83323ee 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "dependencies": { "@callstack/react-theme-provider": "^3.0.9", "color": "^3.1.2", + "fast-deep-equal": "^3.1.3", "use-latest-callback": "^0.2.3" }, "devDependencies": { diff --git a/src/components/Card/CardContent.tsx b/src/components/Card/CardContent.tsx index f53ff034f5..bde25ddb25 100644 --- a/src/components/Card/CardContent.tsx +++ b/src/components/Card/CardContent.tsx @@ -43,8 +43,8 @@ export type Props = ViewProps & { * ``` */ const CardContent = ({ index, total, siblings, style, ...rest }: Props) => { - const cover = 'withInternalTheme(CardCover)'; - const title = 'withInternalTheme(CardTitle)'; + const cover = 'Card.Cover'; + const title = 'Card.Title'; let contentStyle, prev, next; diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index 0dd218069b..1326026913 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -2,13 +2,13 @@ import * as React from 'react'; import PortalConsumer from './PortalConsumer'; import PortalHost, { PortalContext } from './PortalHost'; -import { LocaleContext, LocaleProvider } from '../../core/locale'; +import { LocaleProvider, useLocale } from '../../core/locale'; import { - Consumer as SettingsConsumer, + SettingsContext, Provider as SettingsProvider, } from '../../core/settings'; -import { ThemeProvider, withInternalTheme } from '../../core/theming'; -import type { InternalTheme } from '../../theme/types'; +import { ThemeProvider, useInternalTheme } from '../../core/theming'; +import type { ThemeProp } from '../../theme/types'; export type Props = { /** @@ -18,7 +18,7 @@ export type Props = { /** * @optional */ - theme: InternalTheme; + theme?: ThemeProp; }; /** @@ -41,36 +41,24 @@ export type Props = { * export default MyComponent; * ``` */ -class Portal extends React.Component { - // @component ./PortalHost.tsx - static Host = PortalHost; +const Portal = ({ children, theme: themeOverrides }: Props) => { + const theme = useInternalTheme(themeOverrides); + const { direction } = useLocale(); + const settings = React.useContext(SettingsContext); + const manager = React.useContext(PortalContext); - render() { - const { children, theme } = this.props; + return ( + + + + {children} + + + + ); +}; - return ( - - {(locale) => ( - - {(settings) => ( - - {(manager) => ( - - - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */} - - {children} - - - - )} - - )} - - )} - - ); - } -} +// @component ./PortalHost.tsx +Portal.Host = PortalHost; -export default withInternalTheme(Portal); +export default Portal; diff --git a/src/components/__tests__/Portal.test.tsx b/src/components/__tests__/Portal.test.tsx index 14f07c141b..855d74b6ab 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -2,6 +2,8 @@ import { Text } from 'react-native'; import { expect, it, jest } from '@jest/globals'; +import { LocaleProvider, useLocale } from '../../core/locale'; +import { useInternalTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; import Portal from '../Portal/Portal'; @@ -21,3 +23,37 @@ it('renders portal with siblings', async () => { expect(toJSON()).toMatchSnapshot(); }); + +const PortalThemeContent = () => { + const theme = useInternalTheme(undefined); + const { direction } = useLocale(); + + return {`${theme.animation.scale} ${direction}`}; +}; + +it('passes local theme overrides and locale to portal content and updates them', async () => { + const { rerender } = await render( + + + + + + + + ); + + expect(screen.getByText('2 rtl')).toBeOnTheScreen(); + + await rerender( + + + + + + + + ); + + expect(screen.getByText('3 ltr')).toBeOnTheScreen(); + expect(screen.queryByText('2 rtl')).not.toBeOnTheScreen(); +}); diff --git a/src/theme/__tests__/provider.test.ts b/src/theme/__tests__/provider.test.ts index e8635dfe90..9ee299997e 100644 --- a/src/theme/__tests__/provider.test.ts +++ b/src/theme/__tests__/provider.test.ts @@ -1,6 +1,17 @@ +import * as React from 'react'; +import { PlatformColor } from 'react-native'; + import { describe, expect, it } from '@jest/globals'; +import { renderHook } from '@testing-library/react-native'; -import { isPlatformColorSentinel, safeMerge } from '../provider'; +import { + defaultThemes, + isPlatformColorSentinel, + safeMerge, + ThemeProvider, + useInternalTheme, +} from '../provider'; +import type { ThemeProp } from '../types'; describe('isPlatformColorSentinel', () => { it('detects iOS PlatformColor (semantic)', () => { @@ -114,3 +125,90 @@ describe('safeMerge', () => { expect(result.colors.tertiary).toBe('#222'); }); }); + +describe('useInternalTheme', () => { + it('returns the default theme without overrides', async () => { + const { result } = await renderHook(() => useInternalTheme(undefined)); + + expect(result.current).toBe(defaultThemes.light); + }); + + it('keeps the theme reference when nested overrides have equal values', async () => { + const { result, rerender } = await renderHook( + (overrides: ThemeProp) => useInternalTheme(overrides), + { initialProps: { colors: { primary: '#123456' } } } + ); + const theme = result.current; + + await rerender({ colors: { primary: '#123456' } }); + + expect(result.current).toBe(theme); + expect(result.current.colors.primary).toBe('#123456'); + expect(result.current.colors.secondary).toBe( + defaultThemes.light.colors.secondary + ); + }); + + it('updates changed overrides and restores defaults when overrides are removed', async () => { + const { result, rerender } = await renderHook( + (overrides: ThemeProp | undefined) => useInternalTheme(overrides), + { initialProps: { colors: { primary: '#123456', secondary: '#abcdef' } } } + ); + const theme = result.current; + + await rerender({ colors: { primary: '#654321' } }); + + expect(result.current).not.toBe(theme); + expect(result.current.colors.primary).toBe('#654321'); + expect(result.current.colors.secondary).toBe( + defaultThemes.light.colors.secondary + ); + + await rerender(undefined); + + expect(result.current).toBe(defaultThemes.light); + }); + + it('updates the provider theme while keeping local overrides', async () => { + let theme = defaultThemes.light; + const wrapper = (props: { children: React.ReactNode }) => + React.createElement(ThemeProvider, { ...props, theme }); + const { result, rerender } = await renderHook( + (overrides: ThemeProp) => useInternalTheme(overrides), + { wrapper, initialProps: { colors: { primary: '#123456' } } } + ); + const previous = result.current; + + theme = defaultThemes.dark; + await rerender({ colors: { primary: '#123456' } }); + + expect(result.current).not.toBe(previous); + expect(result.current.dark).toBe(true); + expect(result.current.colors.primary).toBe('#123456'); + expect(result.current.colors.secondary).toBe( + defaultThemes.dark.colors.secondary + ); + }); + + it('preserves platform colors and keeps equal platform color overrides stable', async () => { + const primary = PlatformColor('label'); + const { result, rerender } = await renderHook( + (overrides: ThemeProp) => useInternalTheme(overrides), + { initialProps: { colors: { primary } } } + ); + const theme = result.current; + + expect(result.current.colors.primary).toBe(primary); + + await rerender({ colors: { primary: PlatformColor('label') } }); + + expect(result.current).toBe(theme); + + await rerender({ colors: { primary: PlatformColor('secondaryLabel') } }); + + expect(result.current).not.toBe(theme); + expect(result.current.colors.primary).toEqual( + PlatformColor('secondaryLabel') + ); + }); +}); diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index 546a9dcf88..1a32c5f63c 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import type { ComponentType } from 'react'; import { createTheming } from '@callstack/react-theme-provider'; import type { $DeepPartial } from '@callstack/react-theme-provider'; +import isEqual from 'fast-deep-equal'; import { DarkTheme, LightTheme } from './schemes'; import type { Theme, NavigationTheme } from './types'; @@ -55,21 +55,22 @@ export const safeMerge = (base: T, overrides: unknown): T => { }; /* eslint-enable @typescript-eslint/no-unsafe-type-assertion */ -/** Memoize `themeOverrides` at the call site; inline object literals defeat the memo. */ export const useInternalTheme = ( themeOverrides: $DeepPartial | undefined ): Theme => { const theme = useThemeBase(); + const [overrides, setOverrides] = React.useState(themeOverrides); + + if (!isEqual(overrides, themeOverrides)) { + setOverrides(themeOverrides); + } + return React.useMemo( - () => (themeOverrides ? safeMerge(theme, themeOverrides) : theme), - [theme, themeOverrides] + () => (overrides ? safeMerge(theme, overrides) : theme), + [theme, overrides] ); }; -export const withInternalTheme = ( - WrappedComponent: ComponentType & C -) => withTheme(WrappedComponent); - export const defaultThemes = { light: LightTheme, dark: DarkTheme, diff --git a/yarn.lock b/yarn.lock index dcc0fa4cac..ee4558c745 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18219,6 +18219,7 @@ __metadata: eslint: "npm:9.39.4" eslint-plugin-flowtype: "npm:^8.0.3" eslint-plugin-testing-library: "npm:^7.16.2" + fast-deep-equal: "npm:^3.1.3" jest: "npm:^29.6.3" jest-file-snapshot: "npm:^0.3.2" lefthook: "npm:^2.1.9" From b3d0deab8f61c8ceb8dde5b6ba39618402701918 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 9 Sep 2026 14:56:21 +0200 Subject: [PATCH 70/94] refactor: remove unnecessary abstractions for theme --- .../__tests__/Appbar/Appbar.test.tsx | 14 +-- .../__tests__/BottomNavigation.test.tsx | 11 +- src/components/__tests__/Button.test.tsx | 104 +++++++++--------- src/components/__tests__/Card/Card.test.tsx | 22 ++-- .../__tests__/Checkbox/utils.test.tsx | 88 +++++++++------ src/components/__tests__/Chip.test.tsx | 58 +++++----- src/components/__tests__/FABUtils.test.tsx | 85 +++++++------- src/components/__tests__/IconButton.test.tsx | 76 ++++++------- .../__tests__/ListAccordion.test.tsx | 14 +-- src/components/__tests__/Menu.test.tsx | 5 +- src/components/__tests__/MenuItem.test.tsx | 18 +-- .../__tests__/RadioButton/utils.test.tsx | 26 ++--- .../__tests__/SegmentedButton.test.tsx | 48 ++++---- src/components/__tests__/Surface.test.tsx | 6 +- .../__tests__/ToggleButton.test.tsx | 14 +-- src/core/PaperProvider.tsx | 5 +- src/test-utils.tsx | 5 +- src/theme/__tests__/provider.test.ts | 22 ++-- src/theme/provider.tsx | 14 --- 19 files changed, 315 insertions(+), 320 deletions(-) diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 396ffd6734..4247243e77 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -1,8 +1,8 @@ import { describe, expect, it } from '@jest/globals'; import { SafeAreaProvider } from 'react-native-safe-area-context'; -import { getTheme } from '../../../core/theming'; import { render, screen } from '../../../test-utils'; +import { DarkTheme, LightTheme } from '../../../theme/schemes'; import { tokens } from '../../../theme/tokens'; import Appbar from '../../Appbar'; import { @@ -200,7 +200,7 @@ describe('AppbarAction', () => { .props.children; // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. expect(appbarActionIcon.props.color).toBe( - getTheme().colors.onSurfaceVariant + LightTheme.colors.onSurfaceVariant ); }); @@ -214,7 +214,7 @@ describe('AppbarAction', () => { const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') .props.children; // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarActionIcon.props.color).toBe(getTheme().colors.onSurface); + expect(appbarActionIcon.props.color).toBe(LightTheme.colors.onSurface); }); it('should be rendered with custom color', async () => { @@ -255,7 +255,7 @@ describe('AppbarContent', () => { ); expect(screen.getByTestId('appbar-content-title-text')).toHaveStyle( - getTheme().fonts[modeTextVariant[mode]] + LightTheme.fonts[modeTextVariant[mode]] ); }) ); @@ -283,18 +283,18 @@ describe('getAppbarColors', () => { it('should return custom color no matter what is the theme version', () => { expect( - getAppbarBackgroundColor(getTheme(), elevated, customBackground) + getAppbarBackgroundColor(LightTheme, elevated, customBackground) ).toBe(customBackground); }); it('returns the light surface container color for an elevated appbar', () => { - expect(getAppbarBackgroundColor(getTheme(), elevated)).toBe( + expect(getAppbarBackgroundColor(LightTheme, elevated)).toBe( tokens.md.ref.palette.neutral94 ); }); it('returns the dark surface container color for an elevated appbar', () => { - expect(getAppbarBackgroundColor(getTheme(true), elevated)).toBe( + expect(getAppbarBackgroundColor(DarkTheme, elevated)).toBe( tokens.md.ref.palette.neutral12 ); }); diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index 6a9f14c0a5..2f34c4c7c2 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -13,8 +13,8 @@ import type { KeyboardEvent } from 'react-native'; import { describe, expect, it, jest } from '@jest/globals'; import { act, fireEvent, userEvent } from '@testing-library/react-native'; -import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import { LightTheme } from '../../theme/schemes'; import { Palette } from '../../theme/tokens'; import BottomNavigation from '../BottomNavigation/BottomNavigation'; import BottomNavigationRouteScreen from '../BottomNavigation/BottomNavigationRouteScreen'; @@ -607,8 +607,7 @@ describe('getActiveTintColor', () => { `( 'returns $expected when activeColor: $activeColor', ({ activeColor, expected }) => { - const theme = getTheme(false); - const result = getActiveTintColor({ activeColor, theme }); + const result = getActiveTintColor({ activeColor, theme: LightTheme }); expect(result).toBe(expected); } ); @@ -622,10 +621,9 @@ describe('getInactiveTintColor', () => { `( 'returns $expected when inactiveColor: $inactiveColor', ({ inactiveColor, expected }) => { - const theme = getTheme(false); const result = getInactiveTintColor({ inactiveColor, - theme, + theme: LightTheme, }); expect(result).toBe(expected); } @@ -645,12 +643,11 @@ describe('getLabelColor', () => { ])( 'returns $expected when tintColor: $tintColor, focused: $focused', ({ tintColor, focused, expected }) => { - const theme = getTheme(false); const result = getLabelColor({ tintColor: tintColor ?? '', hasColor: Boolean(tintColor), focused, - theme, + theme: LightTheme, }); expect(result).toBe(expected); } diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index be3d573d29..69329232e2 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -3,9 +3,9 @@ import { StyleSheet } from 'react-native'; import { describe, expect, it, jest } from '@jest/globals'; import { userEvent } from '@testing-library/react-native'; -import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; import { pink500, white } from '../../theme/colors'; +import { DarkTheme, LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import Button from '../Button/Button'; import { getButtonColors } from '../Button/utils'; @@ -265,7 +265,7 @@ describe('getButtonColors - background color', () => { expect( getButtonColors({ customButtonColor, - theme: getTheme(), + theme: LightTheme, disabled: false, mode: 'text', }) @@ -277,7 +277,7 @@ describe('getButtonColors - background color', () => { expect( getButtonColors({ customButtonColor, - theme: getTheme(), + theme: LightTheme, mode, disabled: true, }) @@ -290,7 +290,7 @@ describe('getButtonColors - background color', () => { expect( getButtonColors({ customButtonColor, - theme: getTheme(), + theme: LightTheme, mode, disabled: true, }) @@ -303,12 +303,12 @@ describe('getButtonColors - background color', () => { return expect( getButtonColors({ customButtonColor, - theme: getTheme(), + theme: LightTheme, mode, disabled: true, }) ).toMatchObject({ - backgroundColor: getTheme().colors.onSurface, + backgroundColor: LightTheme.colors.onSurface, backgroundOpacity: stateOpacity.pressed, }); }) @@ -319,12 +319,12 @@ describe('getButtonColors - background color', () => { return expect( getButtonColors({ customButtonColor, - theme: getTheme(true), + theme: DarkTheme, mode, disabled: true, }) ).toMatchObject({ - backgroundColor: getTheme(true).colors.onSurface, + backgroundColor: DarkTheme.colors.onSurface, backgroundOpacity: stateOpacity.pressed, }); }) @@ -333,66 +333,66 @@ describe('getButtonColors - background color', () => { it('should return correct theme color, for theme version 3, elevated mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode: 'elevated', }) ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceContainerLow, + backgroundColor: LightTheme.colors.surfaceContainerLow, }); }); it('should return correct theme color, for theme version 3, dark theme, elevated mode', () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode: 'elevated', }) ).toMatchObject({ - backgroundColor: getTheme(true).colors.surfaceContainerLow, + backgroundColor: DarkTheme.colors.surfaceContainerLow, }); }); it('should return correct theme color, for theme version 3, contained mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode: 'contained', }) ).toMatchObject({ - backgroundColor: getTheme().colors.primary, + backgroundColor: LightTheme.colors.primary, }); }); it('should return correct theme color, for theme version 3, dark theme, contained mode', () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode: 'contained', }) ).toMatchObject({ - backgroundColor: getTheme(true).colors.primary, + backgroundColor: DarkTheme.colors.primary, }); }); it('should return correct theme color, for theme version 3, contained-tonal mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode: 'contained-tonal', }) ).toMatchObject({ - backgroundColor: getTheme().colors.secondaryContainer, + backgroundColor: LightTheme.colors.secondaryContainer, }); }); it('should return correct theme color, for theme version 3, dark theme, contained-tonal mode', () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode: 'contained-tonal', }) ).toMatchObject({ - backgroundColor: getTheme(true).colors.secondaryContainer, + backgroundColor: DarkTheme.colors.secondaryContainer, }); }); @@ -400,7 +400,7 @@ describe('getButtonColors - background color', () => { it(`should return transparent color, for theme version 3, ${mode} mode`, () => { return expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode, }) ).toMatchObject({ @@ -413,7 +413,7 @@ describe('getButtonColors - background color', () => { it(`should return transparent color, for theme version 3, dark theme, ${mode} mode`, () => { return expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode, }) ).toMatchObject({ @@ -430,7 +430,7 @@ describe('getButtonColors - text color', () => { expect( getButtonColors({ customTextColor, - theme: getTheme(), + theme: LightTheme, disabled: false, mode: 'text', }) @@ -441,12 +441,12 @@ describe('getButtonColors - text color', () => { expect( getButtonColors({ customTextColor, - theme: getTheme(), + theme: LightTheme, disabled: true, mode: 'text', }) ).toMatchObject({ - textColor: getTheme().colors.onSurface, + textColor: LightTheme.colors.onSurface, textOpacity: stateOpacity.disabled, }); }); @@ -455,12 +455,12 @@ describe('getButtonColors - text color', () => { expect( getButtonColors({ customTextColor, - theme: getTheme(true), + theme: DarkTheme, disabled: true, mode: 'text', }) ).toMatchObject({ - textColor: getTheme(true).colors.onSurface, + textColor: DarkTheme.colors.onSurface, textOpacity: stateOpacity.disabled, }); }); @@ -469,7 +469,7 @@ describe('getButtonColors - text color', () => { it(`should return correct text color for dark prop, for theme version 3, ${mode} mode`, () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode, dark: true, }) @@ -483,11 +483,11 @@ describe('getButtonColors - text color', () => { it(`should return correct theme text color, for theme version 3, ${mode} mode`, () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode, }) ).toMatchObject({ - textColor: getTheme().colors.primary, + textColor: LightTheme.colors.primary, }); }) ); @@ -496,11 +496,11 @@ describe('getButtonColors - text color', () => { it(`should return correct theme text color, for theme version 3, dark theme, ${mode} mode`, () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode, }) ).toMatchObject({ - textColor: getTheme(true).colors.primary, + textColor: DarkTheme.colors.primary, }); }) ); @@ -508,44 +508,44 @@ describe('getButtonColors - text color', () => { it('should return correct theme text color, for theme version 3, contained mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode: 'contained', }) ).toMatchObject({ - textColor: getTheme().colors.onPrimary, + textColor: LightTheme.colors.onPrimary, }); }); it('should return correct theme text color, for theme version 3, dark theme, contained mode', () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode: 'contained', }) ).toMatchObject({ - textColor: getTheme(true).colors.onPrimary, + textColor: DarkTheme.colors.onPrimary, }); }); it('should return correct theme text color, for theme version 3, contained-tonal mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode: 'contained-tonal', }) ).toMatchObject({ - textColor: getTheme().colors.onSecondaryContainer, + textColor: LightTheme.colors.onSecondaryContainer, }); }); it('should return correct theme text color, for theme version 3, dark theme contained-tonal mode', () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode: 'contained-tonal', }) ).toMatchObject({ - textColor: getTheme(true).colors.onSecondaryContainer, + textColor: DarkTheme.colors.onSecondaryContainer, }); }); }); @@ -554,46 +554,46 @@ describe('getButtonColors - border color', () => { it('should return correct border color, for theme version 3, when disabled, outlined mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, disabled: true, mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme().colors.outlineVariant, + borderColor: LightTheme.colors.outlineVariant, }); }); it('should return correct border color, for theme version 3, when disabled, dark theme, outlined mode', () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, disabled: true, mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme(true).colors.outlineVariant, + borderColor: DarkTheme.colors.outlineVariant, }); }); it('should return correct border color, for theme version 3, outlined mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme().colors.outlineVariant, + borderColor: LightTheme.colors.outlineVariant, }); }); it('should return correct border color, for theme version 3, dark theme, outlined mode', () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode: 'outlined', }) ).toMatchObject({ - borderColor: getTheme(true).colors.outlineVariant, + borderColor: DarkTheme.colors.outlineVariant, }); }); @@ -602,7 +602,7 @@ describe('getButtonColors - border color', () => { it(`should return transparent border, for theme version 3, ${mode} mode`, () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode, }) ).toMatchObject({ @@ -616,7 +616,7 @@ describe('getButtonColors - border color', () => { it(`should return transparent border, for theme version 3, dark theme, ${mode} mode`, () => { expect( getButtonColors({ - theme: getTheme(true), + theme: DarkTheme, mode, }) ).toMatchObject({ @@ -630,7 +630,7 @@ describe('getButtonColors - border width', () => { it('should return correct border width, for theme version 3, outlined mode', () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode: 'outlined', }) ).toMatchObject({ @@ -643,7 +643,7 @@ describe('getButtonColors - border width', () => { it(`should return correct border width, for ${mode} mode`, () => { expect( getButtonColors({ - theme: getTheme(), + theme: LightTheme, mode, }) ).toMatchObject({ diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index 16163ba257..fab15f0a26 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -2,8 +2,8 @@ import { Platform, StyleSheet, Text } from 'react-native'; import { afterEach, describe, expect, it, jest } from '@jest/globals'; -import { getTheme } from '../../../core/theming'; import { render, screen } from '../../../test-utils'; +import { LightTheme } from '../../../theme/schemes'; import { Palette } from '../../../theme/tokens'; import Button from '../../Button/Button'; import Card from '../../Card/Card'; @@ -155,27 +155,27 @@ describe('getCardColors - background color', () => { it('should return correct theme color, for theme version 3, contained mode', () => { expect( getCardColors({ - theme: getTheme(), + theme: LightTheme, mode: 'contained', }) ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceVariant, + backgroundColor: LightTheme.colors.surfaceVariant, }); }); it('should return correct theme color, for theme version 3, outlined mode', () => { expect( getCardColors({ - theme: getTheme(), + theme: LightTheme, mode: 'outlined', }) - ).toMatchObject({ backgroundColor: getTheme().colors.surface }); + ).toMatchObject({ backgroundColor: LightTheme.colors.surface }); }); it('should return undefined, for theme version 3, elevated mode', () => { expect( getCardColors({ - theme: getTheme(), + theme: LightTheme, mode: 'elevated', }) ).toMatchObject({ backgroundColor: undefined }); @@ -186,11 +186,11 @@ describe('getCardColors - border color', () => { it('should return correct theme color, for theme version 3', () => { expect( getCardColors({ - theme: getTheme(), + theme: LightTheme, // @ts-expect-error: Verify the runtime fallback when mode is omitted. mode: undefined, }) - ).toMatchObject({ borderColor: getTheme().colors.outline }); + ).toMatchObject({ borderColor: LightTheme.colors.outline }); }); }); @@ -198,7 +198,7 @@ describe('getCardCoverStyle - border radius', () => { it('should return custom border radius', () => { expect( getCardCoverStyle({ - theme: getTheme(), + theme: LightTheme, borderRadiusStyles: styles.customCoverRadius, }) ).toMatchObject(styles.customCoverRadius); @@ -207,9 +207,9 @@ describe('getCardCoverStyle - border radius', () => { it('should return correct border radius based on roundness, for theme version 3', () => { expect( getCardCoverStyle({ - theme: getTheme(), + theme: LightTheme, borderRadiusStyles: {}, }) - ).toMatchObject({ borderRadius: getTheme().shapes.corner.medium }); + ).toMatchObject({ borderRadius: LightTheme.shapes.corner.medium }); }); }); diff --git a/src/components/__tests__/Checkbox/utils.test.tsx b/src/components/__tests__/Checkbox/utils.test.tsx index ee0b09f159..6184bdf329 100644 --- a/src/components/__tests__/Checkbox/utils.test.tsx +++ b/src/components/__tests__/Checkbox/utils.test.tsx @@ -1,31 +1,35 @@ import { describe, expect, it } from '@jest/globals'; -import { getTheme } from '../../../core/theming'; +import { DarkTheme, LightTheme } from '../../../theme/schemes'; import { tokens } from '../../../theme/tokens'; import { getSelectionVisualState } from '../../Checkbox/utils'; const stateOpacity = tokens.md.sys.state.opacity; -const theme = getTheme(); -const darkTheme = getTheme(true); describe('getSelectionVisualState', () => { describe('containerColor (the fill)', () => { it('uses theme.colors.primary when selected (default)', () => { - expect(getSelectionVisualState({ theme, selected: true })).toMatchObject({ - containerColor: theme.colors.primary, + expect( + getSelectionVisualState({ theme: LightTheme, selected: true }) + ).toMatchObject({ + containerColor: LightTheme.colors.primary, }); }); it('uses theme.colors.error when selected + error', () => { expect( - getSelectionVisualState({ theme, selected: true, error: true }) - ).toMatchObject({ containerColor: theme.colors.error }); + getSelectionVisualState({ + theme: LightTheme, + selected: true, + error: true, + }) + ).toMatchObject({ containerColor: LightTheme.colors.error }); }); it('uses customColor when provided (overrides error)', () => { expect( getSelectionVisualState({ - theme, + theme: LightTheme, selected: true, error: true, customColor: 'purple', @@ -36,32 +40,36 @@ describe('getSelectionVisualState', () => { it('falls back to onSurface when disabled (overrides customColor)', () => { expect( getSelectionVisualState({ - theme, + theme: LightTheme, selected: true, disabled: true, customColor: 'purple', }) - ).toMatchObject({ containerColor: theme.colors.onSurface }); + ).toMatchObject({ containerColor: LightTheme.colors.onSurface }); }); }); describe('outlineColor (unselected border)', () => { it('uses theme.colors.onSurfaceVariant by default', () => { - expect(getSelectionVisualState({ theme, selected: false })).toMatchObject( - { outlineColor: theme.colors.onSurfaceVariant } - ); + expect( + getSelectionVisualState({ theme: LightTheme, selected: false }) + ).toMatchObject({ outlineColor: LightTheme.colors.onSurfaceVariant }); }); it('uses theme.colors.error when error', () => { expect( - getSelectionVisualState({ theme, selected: false, error: true }) - ).toMatchObject({ outlineColor: theme.colors.error }); + getSelectionVisualState({ + theme: LightTheme, + selected: false, + error: true, + }) + ).toMatchObject({ outlineColor: LightTheme.colors.error }); }); it('uses customUncheckedColor when provided (overrides error)', () => { expect( getSelectionVisualState({ - theme, + theme: LightTheme, selected: false, error: true, customUncheckedColor: 'orange', @@ -72,60 +80,76 @@ describe('getSelectionVisualState', () => { it('falls back to onSurface when disabled', () => { expect( getSelectionVisualState({ - theme, + theme: LightTheme, selected: false, disabled: true, customUncheckedColor: 'orange', }) - ).toMatchObject({ outlineColor: theme.colors.onSurface }); + ).toMatchObject({ outlineColor: LightTheme.colors.onSurface }); }); }); describe('iconColor (checkmark / dash glyph)', () => { it('keeps its color when unselected while the mask hides it', () => { - expect(getSelectionVisualState({ theme, selected: false })).toMatchObject( - { iconColor: theme.colors.onPrimary } - ); + expect( + getSelectionVisualState({ theme: LightTheme, selected: false }) + ).toMatchObject({ iconColor: LightTheme.colors.onPrimary }); }); it('uses theme.colors.onPrimary when selected (default)', () => { - expect(getSelectionVisualState({ theme, selected: true })).toMatchObject({ - iconColor: theme.colors.onPrimary, + expect( + getSelectionVisualState({ theme: LightTheme, selected: true }) + ).toMatchObject({ + iconColor: LightTheme.colors.onPrimary, }); }); it('uses theme.colors.onError when selected + error', () => { expect( - getSelectionVisualState({ theme, selected: true, error: true }) - ).toMatchObject({ iconColor: theme.colors.onError }); + getSelectionVisualState({ + theme: LightTheme, + selected: true, + error: true, + }) + ).toMatchObject({ iconColor: LightTheme.colors.onError }); }); it('uses theme.colors.surface when selected + disabled', () => { expect( - getSelectionVisualState({ theme, selected: true, disabled: true }) - ).toMatchObject({ iconColor: theme.colors.surface }); + getSelectionVisualState({ + theme: LightTheme, + selected: true, + disabled: true, + }) + ).toMatchObject({ iconColor: LightTheme.colors.surface }); }); }); describe('containerOpacity', () => { it('is the enabled state opacity by default', () => { - expect(getSelectionVisualState({ theme, selected: true })).toMatchObject({ + expect( + getSelectionVisualState({ theme: LightTheme, selected: true }) + ).toMatchObject({ containerOpacity: stateOpacity.enabled, }); }); it('drops to the disabled state opacity when disabled', () => { expect( - getSelectionVisualState({ theme, selected: true, disabled: true }) + getSelectionVisualState({ + theme: LightTheme, + selected: true, + disabled: true, + }) ).toMatchObject({ containerOpacity: stateOpacity.disabled }); }); }); describe('dark theme', () => { it('respects the dark theme palette', () => { - const v = getSelectionVisualState({ theme: darkTheme, selected: true }); - expect(v.containerColor).toBe(darkTheme.colors.primary); - expect(v.iconColor).toBe(darkTheme.colors.onPrimary); + const v = getSelectionVisualState({ theme: DarkTheme, selected: true }); + expect(v.containerColor).toBe(DarkTheme.colors.primary); + expect(v.iconColor).toBe(DarkTheme.colors.onPrimary); }); }); }); diff --git a/src/components/__tests__/Chip.test.tsx b/src/components/__tests__/Chip.test.tsx index 87f54dba4e..a7d522e4f0 100644 --- a/src/components/__tests__/Chip.test.tsx +++ b/src/components/__tests__/Chip.test.tsx @@ -1,8 +1,8 @@ import { describe, expect, it } from '@jest/globals'; import color from 'color'; -import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import { DarkTheme, LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import Chip from '../Chip/Chip'; import { getChipColors } from '../Chip/helpers'; @@ -100,11 +100,11 @@ describe('getChipColors - text color', () => { expect( getChipColors({ disabled: true, - theme: getTheme(), + theme: LightTheme, isOutlined: false, }) ).toMatchObject({ - textColor: getTheme().colors.onSurface, + textColor: LightTheme.colors.onSurface, contentOpacity: stateOpacity.disabled, }); }); @@ -112,29 +112,29 @@ describe('getChipColors - text color', () => { it('should return correct theme color, for theme version 3, flat mode', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, isOutlined: false, }) ).toMatchObject({ - textColor: getTheme().colors.onSecondaryContainer, + textColor: LightTheme.colors.onSecondaryContainer, }); }); it('should return correct theme color, for theme version 3, outlined mode', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, isOutlined: true, }) ).toMatchObject({ - textColor: getTheme().colors.onSurfaceVariant, + textColor: LightTheme.colors.onSurfaceVariant, }); }); it('should return custom color, for theme version 3', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, selectedColor: 'purple', isOutlined: false, }) @@ -149,11 +149,11 @@ describe('getChipColors - icon color', () => { expect( getChipColors({ disabled: true, - theme: getTheme(), + theme: LightTheme, isOutlined: false, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurface, + iconColor: LightTheme.colors.onSurface, contentOpacity: stateOpacity.disabled, }); }); @@ -161,29 +161,29 @@ describe('getChipColors - icon color', () => { it('should return correct theme color, for theme version 3, flat mode', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, isOutlined: false, }) ).toMatchObject({ - iconColor: getTheme().colors.onSecondaryContainer, + iconColor: LightTheme.colors.onSecondaryContainer, }); }); it('should return correct theme color, for theme version 3, outlined mode', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, isOutlined: true, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceVariant, + iconColor: LightTheme.colors.onSurfaceVariant, }); }); it('should return custom color, for theme version 3', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, selectedColor: 'purple', isOutlined: false, }) @@ -197,22 +197,22 @@ describe('getChipColor - background color', () => { it('should return theme color, for theme version 3, outlined mode', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, isOutlined: true, }) ).toMatchObject({ - backgroundColor: getTheme().colors.surface, + backgroundColor: LightTheme.colors.surface, }); }); it('should return theme color, for theme version 3, flat mode', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, isOutlined: false, }) ).toMatchObject({ - backgroundColor: getTheme().colors.secondaryContainer, + backgroundColor: LightTheme.colors.secondaryContainer, }); }); }); @@ -221,7 +221,7 @@ describe('getChipColor - border color', () => { it('should return correct disabled color, for theme version 3', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, disabled: true, isOutlined: false, }) @@ -233,7 +233,7 @@ describe('getChipColor - border color', () => { it('should return custom color, for theme version 3', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, selectedColor: 'purple', isOutlined: false, }) @@ -245,7 +245,7 @@ describe('getChipColor - border color', () => { it('should return theme color, for theme version 3', () => { expect( getChipColors({ - theme: getTheme(), + theme: LightTheme, isOutlined: false, }) ).toMatchObject({ @@ -256,7 +256,7 @@ describe('getChipColor - border color', () => { it('should return custom color, outlined mode', () => { expect( getChipColors({ - theme: getTheme(false), + theme: LightTheme, selectedColor: 'purple', isOutlined: true, }) @@ -268,29 +268,29 @@ describe('getChipColor - border color', () => { it('should return theme color, light mode, outlined mode', () => { expect( getChipColors({ - theme: getTheme(false), + theme: LightTheme, isOutlined: true, }) ).toMatchObject({ - borderColor: getTheme(false).colors.outlineVariant, + borderColor: LightTheme.colors.outlineVariant, }); }); it('should return theme color, dark mode, outlined mode', () => { expect( getChipColors({ - theme: getTheme(true), + theme: DarkTheme, isOutlined: true, }) ).toMatchObject({ - borderColor: getTheme(true).colors.outlineVariant, + borderColor: DarkTheme.colors.outlineVariant, }); }); it('should return theme background color, light mode, flat mode', () => { expect( getChipColors({ - theme: getTheme(false), + theme: LightTheme, isOutlined: false, }) ).toMatchObject({ @@ -301,7 +301,7 @@ describe('getChipColor - border color', () => { it('should return theme background color, dark mode, flat mode', () => { expect( getChipColors({ - theme: getTheme(true), + theme: DarkTheme, isOutlined: false, }) ).toMatchObject({ diff --git a/src/components/__tests__/FABUtils.test.tsx b/src/components/__tests__/FABUtils.test.tsx index b72de366f7..9389ccd78d 100644 --- a/src/components/__tests__/FABUtils.test.tsx +++ b/src/components/__tests__/FABUtils.test.tsx @@ -1,67 +1,66 @@ import { describe, expect, it } from '@jest/globals'; -import { getTheme } from '../../core/theming'; +import { LightTheme } from '../../theme/schemes'; import { getDimensions, resolveColors } from '../FAB/utils'; describe('resolveColors', () => { it('returns theme colors for default variant (tonalPrimary)', () => { - const theme = getTheme(); - const colors = resolveColors({ theme }); + const colors = resolveColors({ theme: LightTheme }); expect(colors).toEqual({ - container: theme.colors.primaryContainer, - content: theme.colors.onPrimaryContainer, + container: LightTheme.colors.primaryContainer, + content: LightTheme.colors.onPrimaryContainer, }); }); it('returns theme colors for primary variant', () => { - const theme = getTheme(); - const colors = resolveColors({ theme, variant: 'primary' }); + const colors = resolveColors({ theme: LightTheme, variant: 'primary' }); expect(colors).toEqual({ - container: theme.colors.primary, - content: theme.colors.onPrimary, + container: LightTheme.colors.primary, + content: LightTheme.colors.onPrimary, }); }); it('returns theme colors for secondary variant', () => { - const theme = getTheme(); - const colors = resolveColors({ theme, variant: 'secondary' }); + const colors = resolveColors({ theme: LightTheme, variant: 'secondary' }); expect(colors).toEqual({ - container: theme.colors.secondary, - content: theme.colors.onSecondary, + container: LightTheme.colors.secondary, + content: LightTheme.colors.onSecondary, }); }); it('returns theme colors for tertiary variant', () => { - const theme = getTheme(); - const colors = resolveColors({ theme, variant: 'tertiary' }); + const colors = resolveColors({ theme: LightTheme, variant: 'tertiary' }); expect(colors).toEqual({ - container: theme.colors.tertiary, - content: theme.colors.onTertiary, + container: LightTheme.colors.tertiary, + content: LightTheme.colors.onTertiary, }); }); it('returns theme colors for tonalSecondary variant', () => { - const theme = getTheme(); - const colors = resolveColors({ theme, variant: 'tonalSecondary' }); + const colors = resolveColors({ + theme: LightTheme, + variant: 'tonalSecondary', + }); expect(colors).toEqual({ - container: theme.colors.secondaryContainer, - content: theme.colors.onSecondaryContainer, + container: LightTheme.colors.secondaryContainer, + content: LightTheme.colors.onSecondaryContainer, }); }); it('returns theme colors for tonalTertiary variant', () => { - const theme = getTheme(); - const colors = resolveColors({ theme, variant: 'tonalTertiary' }); + const colors = resolveColors({ + theme: LightTheme, + variant: 'tonalTertiary', + }); expect(colors).toEqual({ - container: theme.colors.tertiaryContainer, - content: theme.colors.onTertiaryContainer, + container: LightTheme.colors.tertiaryContainer, + content: LightTheme.colors.onTertiaryContainer, }); }); it('containerColor override wins over variant', () => { - const theme = getTheme(); const colors = resolveColors({ - theme, + theme: LightTheme, variant: 'primary', containerColor: '#custom', }); @@ -69,20 +68,18 @@ describe('resolveColors', () => { }); it('derives content color from containerColor when contentColor is not set', () => { - const theme = getTheme(); // Use a known theme color so contentColorFor can derive the on-color pairing const colors = resolveColors({ - theme, - containerColor: theme.colors.primary, + theme: LightTheme, + containerColor: LightTheme.colors.primary, }); - expect(colors.container).toBe(theme.colors.primary); - expect(colors.content).toBe(theme.colors.onPrimary); + expect(colors.container).toBe(LightTheme.colors.primary); + expect(colors.content).toBe(LightTheme.colors.onPrimary); }); it('both containerColor and contentColor overrides win', () => { - const theme = getTheme(); const colors = resolveColors({ - theme, + theme: LightTheme, containerColor: '#bg', contentColor: '#fg', }); @@ -90,20 +87,19 @@ describe('resolveColors', () => { }); it('contentColor-only override uses variant container', () => { - const theme = getTheme(); const colors = resolveColors({ - theme, + theme: LightTheme, variant: 'primary', contentColor: '#custom', }); - expect(colors.container).toBe(theme.colors.primary); + expect(colors.container).toBe(LightTheme.colors.primary); expect(colors.content).toBe('#custom'); }); }); describe('getDimensions', () => { it('returns correct dimensions for default size', () => { - const dims = getDimensions({ theme: getTheme() }); + const dims = getDimensions({ theme: LightTheme }); expect(dims.height).toBe(56); expect(dims.width).toBe(56); expect(dims.iconSize).toBe(24); @@ -114,7 +110,7 @@ describe('getDimensions', () => { }); it('returns correct dimensions for medium size', () => { - const dims = getDimensions({ theme: getTheme(), size: 'medium' }); + const dims = getDimensions({ theme: LightTheme, size: 'medium' }); expect(dims.height).toBe(80); expect(dims.width).toBe(80); expect(dims.iconSize).toBe(28); @@ -125,7 +121,7 @@ describe('getDimensions', () => { }); it('returns correct dimensions for large size', () => { - const dims = getDimensions({ theme: getTheme(), size: 'large' }); + const dims = getDimensions({ theme: LightTheme, size: 'large' }); expect(dims.height).toBe(96); expect(dims.width).toBe(96); expect(dims.iconSize).toBe(36); @@ -136,20 +132,19 @@ describe('getDimensions', () => { }); it('shape override changes borderRadius compared to default', () => { - const theme = getTheme(); - const defaultDims = getDimensions({ theme }); - const fullDims = getDimensions({ theme, shape: 'full' }); + const defaultDims = getDimensions({ theme: LightTheme }); + const fullDims = getDimensions({ theme: LightTheme, shape: 'full' }); expect(fullDims.borderRadius).not.toBe(defaultDims.borderRadius); }); it('iconSize override wins over size spec', () => { - const dims = getDimensions({ theme: getTheme(), iconSize: 32 }); + const dims = getDimensions({ theme: LightTheme, iconSize: 32 }); expect(dims.iconSize).toBe(32); }); it('leading and trailing overrides win over size spec', () => { const dims = getDimensions({ - theme: getTheme(), + theme: LightTheme, leading: 20, trailing: 24, }); diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index a8bc540aa9..d804629670 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -2,9 +2,9 @@ import { StyleSheet } from 'react-native'; import { describe, expect, it } from '@jest/globals'; -import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; import { pink500 } from '../../theme/colors'; +import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import IconButton from '../IconButton/IconButton'; import { getIconButtonColor } from '../IconButton/utils'; @@ -88,7 +88,7 @@ describe('getIconButtonColor - icon color', () => { it('should return custom icon color', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, customIconColor: 'purple', }) ).toMatchObject({ @@ -99,11 +99,11 @@ describe('getIconButtonColor - icon color', () => { it('should return correct disabled color, for theme version 3', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, disabled: true, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurface, + iconColor: LightTheme.colors.onSurface, iconOpacity: stateOpacity.disabled, }); }); @@ -111,90 +111,90 @@ describe('getIconButtonColor - icon color', () => { it('should return theme icon color, for theme version 3, mode contained', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained', }) ).toMatchObject({ - iconColor: getTheme().colors.primary, + iconColor: LightTheme.colors.primary, }); }); it('should return theme icon color, for theme version 3, mode contained, selected', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained', selected: true, }) ).toMatchObject({ - iconColor: getTheme().colors.onPrimary, + iconColor: LightTheme.colors.onPrimary, }); }); it('should return theme icon color, for theme version 3, mode contained-tonal', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained-tonal', }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceVariant, + iconColor: LightTheme.colors.onSurfaceVariant, }); }); it('should return theme icon color, for theme version 3, mode contained-tonal, selected', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained-tonal', selected: true, }) ).toMatchObject({ - iconColor: getTheme().colors.onSecondaryContainer, + iconColor: LightTheme.colors.onSecondaryContainer, }); }); it('should return theme icon color, for theme version 3, mode outlined', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'outlined', }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceVariant, + iconColor: LightTheme.colors.onSurfaceVariant, }); }); it('should return theme icon color, for theme version 3, mode outlined, selected', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'outlined', selected: true, }) ).toMatchObject({ - iconColor: getTheme().colors.inverseOnSurface, + iconColor: LightTheme.colors.inverseOnSurface, }); }); it('should return theme icon color, for theme version 3', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceVariant, + iconColor: LightTheme.colors.onSurfaceVariant, }); }); it('should return theme icon color, for theme version 3, selected', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, selected: true, }) ).toMatchObject({ - iconColor: getTheme().colors.primary, + iconColor: LightTheme.colors.primary, }); }); }); @@ -203,7 +203,7 @@ describe('getIconButtonColor - background color', () => { it('should return custom background color', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, customContainerColor: 'purple', }) ).toMatchObject({ @@ -215,12 +215,12 @@ describe('getIconButtonColor - background color', () => { it(`should return correct disabled color, for theme version 3, ${mode} mode`, () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode, disabled: true, }) ).toMatchObject({ - backgroundColor: getTheme().colors.onSurface, + backgroundColor: LightTheme.colors.onSurface, backgroundOpacity: stateOpacity.disabled, }); }) @@ -229,65 +229,65 @@ describe('getIconButtonColor - background color', () => { it('should return theme icon color, for theme version 3, mode contained', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained', }) ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceVariant, + backgroundColor: LightTheme.colors.surfaceVariant, }); }); it('should return theme icon color, for theme version 3, mode contained, selected', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained', selected: true, }) ).toMatchObject({ - backgroundColor: getTheme().colors.primary, + backgroundColor: LightTheme.colors.primary, }); }); it('should return theme icon color, for theme version 3, mode contained-tonal', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained-tonal', }) ).toMatchObject({ - backgroundColor: getTheme().colors.surfaceVariant, + backgroundColor: LightTheme.colors.surfaceVariant, }); }); it('should return theme icon color, for theme version 3, mode contained-tonal, selected', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'contained-tonal', selected: true, }) ).toMatchObject({ - backgroundColor: getTheme().colors.secondaryContainer, + backgroundColor: LightTheme.colors.secondaryContainer, }); }); it('should return theme icon color, for theme version 3, mode outlined, selected', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, mode: 'outlined', selected: true, }) ).toMatchObject({ - backgroundColor: getTheme().colors.inverseSurface, + backgroundColor: LightTheme.colors.inverseSurface, }); }); it('should return undefined, for theme version 3, if mode not specified', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ backgroundColor: undefined, @@ -299,21 +299,21 @@ describe('getIconButtonColor - border color', () => { it('should return correct disabled color, for theme version 3', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, disabled: true, }) ).toMatchObject({ - borderColor: getTheme().colors.outlineVariant, + borderColor: LightTheme.colors.outlineVariant, }); }); it('should return theme color, for theme version 3', () => { expect( getIconButtonColor({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ - borderColor: getTheme().colors.outlineVariant, + borderColor: LightTheme.colors.outlineVariant, }); }); }); diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index ad1783ec52..59d221f9dc 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -2,9 +2,9 @@ import { StyleSheet, View } from 'react-native'; import { describe, expect, it } from '@jest/globals'; -import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; import { red500 } from '../../theme/colors'; +import { LightTheme } from '../../theme/schemes'; import ListAccordion from '../List/ListAccordion'; import ListAccordionGroup from '../List/ListAccordionGroup'; import ListIcon from '../List/ListIcon'; @@ -126,10 +126,10 @@ describe('getAccordionColors - description color', () => { it('should return theme color, for theme version 3', () => { expect( getAccordionColors({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ - descriptionColor: getTheme().colors.onSurfaceVariant, + descriptionColor: LightTheme.colors.onSurfaceVariant, }); }); }); @@ -138,21 +138,21 @@ describe('getAccordionColors - title text color', () => { it('should return theme color, for theme version 3', () => { expect( getAccordionColors({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ - titleTextColor: getTheme().colors.onSurface, + titleTextColor: LightTheme.colors.onSurface, }); }); it('should return primary color if it is expanded', () => { expect( getAccordionColors({ - theme: getTheme(), + theme: LightTheme, isExpanded: true, }) ).toMatchObject({ - titleTextColor: getTheme().colors?.primary, + titleTextColor: LightTheme.colors?.primary, }); }); }); diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index fc6369599c..856e339e06 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -3,8 +3,8 @@ import { Dimensions, StyleSheet, View } from 'react-native'; import { expect, it, jest } from '@jest/globals'; import { act, screen, waitFor } from '@testing-library/react-native'; -import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; +import { LightTheme } from '../../theme/schemes'; import type { Elevation } from '../../theme/types'; import Button from '../Button/Button'; import Menu from '../Menu/Menu'; @@ -52,7 +52,6 @@ const elevations: Elevation[] = [0, 1, 2, 3, 4, 5]; elevations.forEach((elevation) => it(`renders menu with background color based on elevation value = ${elevation}`, async () => { - const theme = getTheme(); const testID = 'menu-with-elevation'; await render( @@ -72,7 +71,7 @@ elevations.forEach((elevation) => ); expect(screen.getByTestId(`${testID}-surface`)).toHaveStyle({ - backgroundColor: theme.colors.elevation[`level${elevation}`], + backgroundColor: LightTheme.colors.elevation[`level${elevation}`], }); }) ); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index 4662ce968e..fd05f64cd5 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -1,7 +1,7 @@ import { describe, expect, it } from '@jest/globals'; -import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import Menu from '../Menu/Menu'; import { getMenuItemColor } from '../Menu/utils'; @@ -68,11 +68,11 @@ describe('getMenuItemColor - title color', () => { it('should return disabled color if disabled, for theme version 3', () => { expect( getMenuItemColor({ - theme: getTheme(), + theme: LightTheme, disabled: true, }) ).toMatchObject({ - titleColor: getTheme().colors.onSurface, + titleColor: LightTheme.colors.onSurface, contentOpacity: stateOpacity.disabled, }); }); @@ -80,10 +80,10 @@ describe('getMenuItemColor - title color', () => { it('should return correct theme color, for theme version 3', () => { expect( getMenuItemColor({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ - titleColor: getTheme().colors.onSurface, + titleColor: LightTheme.colors.onSurface, }); }); }); @@ -92,11 +92,11 @@ describe('getMenuItemColor - icon color', () => { it('should return disabled color if disabled, for theme version 3', () => { expect( getMenuItemColor({ - theme: getTheme(), + theme: LightTheme, disabled: true, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceVariant, + iconColor: LightTheme.colors.onSurfaceVariant, contentOpacity: stateOpacity.disabled, }); }); @@ -104,10 +104,10 @@ describe('getMenuItemColor - icon color', () => { it('should return correct theme color, for theme version 3', () => { expect( getMenuItemColor({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ - iconColor: getTheme().colors.onSurfaceVariant, + iconColor: LightTheme.colors.onSurfaceVariant, }); }); }); diff --git a/src/components/__tests__/RadioButton/utils.test.tsx b/src/components/__tests__/RadioButton/utils.test.tsx index e0a6736bd6..be3cbc97d8 100644 --- a/src/components/__tests__/RadioButton/utils.test.tsx +++ b/src/components/__tests__/RadioButton/utils.test.tsx @@ -1,6 +1,6 @@ import { describe, expect, it } from '@jest/globals'; -import { getTheme } from '../../../core/theming'; +import { DarkTheme, LightTheme } from '../../../theme/schemes'; import { tokens } from '../../../theme/tokens'; import { getSelectionControlIOSColor } from '../../RadioButton/utils'; @@ -10,11 +10,11 @@ describe('getSelectionControlIOSColor - checked color', () => { it('should return correct disabled color, for theme version 3', () => { expect( getSelectionControlIOSColor({ - theme: getTheme(), + theme: LightTheme, disabled: true, }) ).toMatchObject({ - checkedColor: getTheme().colors.primary, + checkedColor: LightTheme.colors.primary, checkedColorOpacity: stateOpacity.disabled, }); }); @@ -22,7 +22,7 @@ describe('getSelectionControlIOSColor - checked color', () => { it('should return custom color, checked', () => { expect( getSelectionControlIOSColor({ - theme: getTheme(), + theme: LightTheme, customColor: 'purple', }) ).toMatchObject({ @@ -33,44 +33,44 @@ describe('getSelectionControlIOSColor - checked color', () => { it('should return theme color, for theme version 3, checked', () => { expect( getSelectionControlIOSColor({ - theme: getTheme(), + theme: LightTheme, }) ).toMatchObject({ - checkedColor: getTheme().colors.primary, + checkedColor: LightTheme.colors.primary, }); }); it('should return error color when error is true', () => { expect( getSelectionControlIOSColor({ - theme: getTheme(), + theme: LightTheme, error: true, }) ).toMatchObject({ - checkedColor: getTheme().colors.error, + checkedColor: LightTheme.colors.error, }); }); it('should return error color, dark mode, when error is true', () => { expect( getSelectionControlIOSColor({ - theme: getTheme(true), + theme: DarkTheme, error: true, }) ).toMatchObject({ - checkedColor: getTheme(true).colors.error, + checkedColor: DarkTheme.colors.error, }); }); it('should return disabled color when both disabled and error are true (disabled wins)', () => { expect( getSelectionControlIOSColor({ - theme: getTheme(), + theme: LightTheme, disabled: true, error: true, }) ).toMatchObject({ - checkedColor: getTheme().colors.primary, + checkedColor: LightTheme.colors.primary, checkedColorOpacity: stateOpacity.disabled, }); }); @@ -78,7 +78,7 @@ describe('getSelectionControlIOSColor - checked color', () => { it('should return custom color when both customColor and error are true (customColor wins)', () => { expect( getSelectionControlIOSColor({ - theme: getTheme(), + theme: LightTheme, customColor: 'purple', error: true, }) diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index ce950f671f..1e24072540 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -1,7 +1,7 @@ import { describe, expect, it, jest } from '@jest/globals'; -import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import SegmentedButtons from '../SegmentedButtons/SegmentedButtons'; import { @@ -61,36 +61,34 @@ it('renders checked segmented button with selected check', async () => { }); describe('getSegmentedButtonColors', () => { - const theme = getTheme(); - it.each([ { disabled: false, checked: true, checkedColor: undefined, uncheckedColor: undefined, - expected: theme.colors.onSecondaryContainer, + expected: LightTheme.colors.onSecondaryContainer, }, { disabled: false, checked: false, checkedColor: undefined, uncheckedColor: undefined, - expected: theme.colors.onSurface, + expected: LightTheme.colors.onSurface, }, { disabled: true, checked: true, checkedColor: undefined, uncheckedColor: undefined, - expected: theme.colors.onSurface, + expected: LightTheme.colors.onSurface, }, { disabled: true, checked: false, checkedColor: undefined, uncheckedColor: undefined, - expected: theme.colors.onSurface, + expected: LightTheme.colors.onSurface, }, { disabled: false, @@ -118,21 +116,21 @@ describe('getSegmentedButtonColors', () => { checked: false, checkedColor: 'a125f5', uncheckedColor: undefined, - expected: theme.colors.onSurface, + expected: LightTheme.colors.onSurface, }, { disabled: false, checked: true, checkedColor: undefined, uncheckedColor: '000', - expected: theme.colors.onSecondaryContainer, + expected: LightTheme.colors.onSecondaryContainer, }, ])( 'returns $expected when disabled: $disabled, checked: $checked, checkedColor is $checkedColor and uncheckedColor is $uncheckedColor', ({ disabled, checked, checkedColor, uncheckedColor, expected }) => { expect( getSegmentedButtonColors({ - theme, + theme: LightTheme, disabled, checked, checkedColor, @@ -145,17 +143,17 @@ describe('getSegmentedButtonColors', () => { it('should return correct background color when checked and theme version 3', () => { expect( getSegmentedButtonColors({ - theme: getTheme(), + theme: LightTheme, disabled: false, checked: true, }) - ).toMatchObject({ backgroundColor: getTheme().colors.secondaryContainer }); + ).toMatchObject({ backgroundColor: LightTheme.colors.secondaryContainer }); }); it('should return correct background color when uncheked (V3 & V2)', () => { expect( getSegmentedButtonColors({ - theme: getTheme(), + theme: LightTheme, disabled: false, checked: false, }) @@ -167,48 +165,48 @@ describe('getSegmentedButtonColors', () => { it('should return correct border color with theme version 3', () => { expect( getSegmentedButtonColors({ - theme: getTheme(), + theme: LightTheme, disabled: false, checked: false, }) ).toMatchObject({ - borderColor: getTheme().colors.outline, + borderColor: LightTheme.colors.outline, }); }); it('should return correct border color when disabled and theme version 3', () => { expect( getSegmentedButtonColors({ - theme: getTheme(), + theme: LightTheme, disabled: true, checked: false, }) ).toMatchObject({ - borderColor: getTheme().colors.outlineVariant, + borderColor: LightTheme.colors.outlineVariant, }); }); it('should return correct textColor with theme version 3', () => { expect( getSegmentedButtonColors({ - theme: getTheme(), + theme: LightTheme, disabled: false, checked: false, }) ).toMatchObject({ - textColor: getTheme().colors.onSurface, + textColor: LightTheme.colors.onSurface, }); }); it('should return correct textColor when disabled and theme version 3', () => { expect( getSegmentedButtonColors({ - theme: getTheme(), + theme: LightTheme, disabled: true, checked: false, }) ).toMatchObject({ - textColor: getTheme().colors.onSurface, + textColor: LightTheme.colors.onSurface, textOpacity: stateOpacity.disabled, }); }); @@ -219,7 +217,7 @@ describe('getDisabledSegmentedButtonBorderWidth', () => { [0, 1, 2].forEach((index) => { expect( getDisabledSegmentedButtonStyle({ - theme: getTheme(), + theme: LightTheme, buttons: [ { disabled: false }, { disabled: false }, @@ -235,7 +233,7 @@ describe('getDisabledSegmentedButtonBorderWidth', () => { [0, 1, 2].forEach((index) => { expect( getDisabledSegmentedButtonStyle({ - theme: getTheme(), + theme: LightTheme, buttons: [{ disabled: true }, { disabled: true }, { disabled: true }], index, }) @@ -246,7 +244,7 @@ describe('getDisabledSegmentedButtonBorderWidth', () => { it('Returns proper style object for one disabled button', () => { expect( getDisabledSegmentedButtonStyle({ - theme: getTheme(), + theme: LightTheme, buttons: [{ disabled: false }, { disabled: true }, { disabled: true }], index: 0, }) @@ -257,7 +255,7 @@ describe('getDisabledSegmentedButtonBorderWidth', () => { [0, 2].forEach((index) => { expect( getDisabledSegmentedButtonStyle({ - theme: getTheme(), + theme: LightTheme, buttons: [ { disabled: false }, { disabled: true }, diff --git a/src/components/__tests__/Surface.test.tsx b/src/components/__tests__/Surface.test.tsx index 889e51a25a..96ed707ae1 100644 --- a/src/components/__tests__/Surface.test.tsx +++ b/src/components/__tests__/Surface.test.tsx @@ -20,8 +20,8 @@ import { useSharedValue, } from 'react-native-reanimated'; -import { getTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import { LightTheme } from '../../theme/schemes'; import Surface from '../Surface'; const SPOT_SHADOW_OPACITY = 0.19; @@ -103,7 +103,7 @@ describe('Surface', () => { shadowOpacity: expect.any(Number), }); expect(screen.getByTestId('surface-test')).toHaveStyle({ - backgroundColor: getTheme().colors.surfaceContainerHighest, + backgroundColor: LightTheme.colors.surfaceContainerHighest, }); }); @@ -192,7 +192,7 @@ describe('Surface', () => { elevation: expect.any(Number), }); expect(screen.getByTestId('surface-container')).toHaveStyle({ - backgroundColor: getTheme().colors.surfaceContainerHighest, + backgroundColor: LightTheme.colors.surfaceContainerHighest, }); }); diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx index bd798e8180..006ae179c5 100644 --- a/src/components/__tests__/ToggleButton.test.tsx +++ b/src/components/__tests__/ToggleButton.test.tsx @@ -1,7 +1,7 @@ import { describe, expect, it } from '@jest/globals'; -import { getTheme } from '../../core/theming'; import { render } from '../../test-utils'; +import { DarkTheme, LightTheme } from '../../theme/schemes'; import ToggleButton from '../ToggleButton'; import { getToggleButtonColor } from '../ToggleButton/utils'; @@ -35,20 +35,20 @@ it('renders unchecked toggle button', async () => { describe('getToggleButtonColor', () => { it('should return correct color when checked and theme version 3', () => { - expect(getToggleButtonColor({ theme: getTheme(), checked: true })).toBe( - getTheme().colors.surfaceContainerHighest + expect(getToggleButtonColor({ theme: LightTheme, checked: true })).toBe( + LightTheme.colors.surfaceContainerHighest ); }); it('should return correct color when checked and theme version 3, dark theme', () => { - expect(getToggleButtonColor({ theme: getTheme(true), checked: true })).toBe( - getTheme(true).colors.surfaceContainerHighest + expect(getToggleButtonColor({ theme: DarkTheme, checked: true })).toBe( + DarkTheme.colors.surfaceContainerHighest ); }); it('should return correct color when not checked', () => { - expect(getToggleButtonColor({ theme: getTheme(), checked: false })).toBe( - getTheme().colors.surfaceContainer + expect(getToggleButtonColor({ theme: LightTheme, checked: false })).toBe( + LightTheme.colors.surfaceContainer ); }); }); diff --git a/src/core/PaperProvider.tsx b/src/core/PaperProvider.tsx index 1525827f6a..43e61453cc 100644 --- a/src/core/PaperProvider.tsx +++ b/src/core/PaperProvider.tsx @@ -4,7 +4,7 @@ import { getDefaultDirection, LocaleProvider, type Direction } from './locale'; import SafeAreaProviderCompat from './SafeAreaProviderCompat'; import { Provider as SettingsProvider } from './settings'; import type { Settings } from './settings'; -import { defaultThemes, ThemeProvider } from './theming'; +import { ThemeProvider } from './theming'; import { useResolvedReduceMotion, type ReduceMotionPreference, @@ -13,6 +13,7 @@ import { useSystemColorScheme } from './useSystemColorScheme'; import MaterialCommunityIcon from '../components/MaterialCommunityIcon'; import PortalHost from '../components/Portal/PortalHost'; import { ReduceMotionContext } from '../theme/accessibility/ReduceMotionContext'; +import { DarkTheme, LightTheme } from '../theme/schemes'; import type { ThemeProp } from '../theme/types'; export type Props = { @@ -31,7 +32,7 @@ const PaperProvider = (props: Props) => { const theme = React.useMemo(() => { const isDark = props.theme?.dark ?? colorScheme === 'dark'; - const base = defaultThemes[isDark ? 'dark' : 'light']; + const base = isDark ? DarkTheme : LightTheme; const scale = resolvedReduceMotion ? 0 : (props.theme?.animation?.scale ?? 1); diff --git a/src/test-utils.tsx b/src/test-utils.tsx index 9e1049e7ff..4eec1e548e 100644 --- a/src/test-utils.tsx +++ b/src/test-utils.tsx @@ -5,14 +5,15 @@ import { render, type RenderOptions } from '@testing-library/react-native'; import MaterialCommunityIcon from './components/MaterialCommunityIcon'; import { getDefaultDirection, LocaleProvider } from './core/locale'; import { Provider as SettingsProvider } from './core/settings'; -import { defaultThemes, ThemeProvider } from './core/theming'; +import { ThemeProvider } from './core/theming'; +import { LightTheme } from './theme/schemes'; const Wrapper = ({ children }: { children: React.ReactNode }) => ( - {children} + {children} ); diff --git a/src/theme/__tests__/provider.test.ts b/src/theme/__tests__/provider.test.ts index 9ee299997e..acac88b491 100644 --- a/src/theme/__tests__/provider.test.ts +++ b/src/theme/__tests__/provider.test.ts @@ -5,12 +5,12 @@ import { describe, expect, it } from '@jest/globals'; import { renderHook } from '@testing-library/react-native'; import { - defaultThemes, isPlatformColorSentinel, safeMerge, ThemeProvider, useInternalTheme, } from '../provider'; +import { DarkTheme, LightTheme } from '../schemes'; import type { ThemeProp } from '../types'; describe('isPlatformColorSentinel', () => { @@ -130,7 +130,7 @@ describe('useInternalTheme', () => { it('returns the default theme without overrides', async () => { const { result } = await renderHook(() => useInternalTheme(undefined)); - expect(result.current).toBe(defaultThemes.light); + expect(result.current).toBe(LightTheme); }); it('keeps the theme reference when nested overrides have equal values', async () => { @@ -144,9 +144,7 @@ describe('useInternalTheme', () => { expect(result.current).toBe(theme); expect(result.current.colors.primary).toBe('#123456'); - expect(result.current.colors.secondary).toBe( - defaultThemes.light.colors.secondary - ); + expect(result.current.colors.secondary).toBe(LightTheme.colors.secondary); }); it('updates changed overrides and restores defaults when overrides are removed', async () => { @@ -160,17 +158,15 @@ describe('useInternalTheme', () => { expect(result.current).not.toBe(theme); expect(result.current.colors.primary).toBe('#654321'); - expect(result.current.colors.secondary).toBe( - defaultThemes.light.colors.secondary - ); + expect(result.current.colors.secondary).toBe(LightTheme.colors.secondary); await rerender(undefined); - expect(result.current).toBe(defaultThemes.light); + expect(result.current).toBe(LightTheme); }); it('updates the provider theme while keeping local overrides', async () => { - let theme = defaultThemes.light; + let theme = LightTheme; const wrapper = (props: { children: React.ReactNode }) => React.createElement(ThemeProvider, { ...props, theme }); const { result, rerender } = await renderHook( @@ -179,15 +175,13 @@ describe('useInternalTheme', () => { ); const previous = result.current; - theme = defaultThemes.dark; + theme = DarkTheme; await rerender({ colors: { primary: '#123456' } }); expect(result.current).not.toBe(previous); expect(result.current.dark).toBe(true); expect(result.current.colors.primary).toBe('#123456'); - expect(result.current.colors.secondary).toBe( - defaultThemes.dark.colors.secondary - ); + expect(result.current.colors.secondary).toBe(DarkTheme.colors.secondary); }); it('preserves platform colors and keeps equal platform color overrides stable', async () => { diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index 1a32c5f63c..8f7e77681e 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -71,20 +71,6 @@ export const useInternalTheme = ( ); }; -export const defaultThemes = { - light: LightTheme, - dark: DarkTheme, -}; - -export const getTheme = ( - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - isDark: Scheme = false as Scheme -): (typeof defaultThemes)[Scheme extends true ? 'dark' : 'light'] => { - const scheme = isDark ? 'dark' : 'light'; - - return defaultThemes[scheme]; -}; - export function adaptNavigationTheme(themes: { reactNavigationLight: T; materialLight?: Theme; From 84fdd8e6dbb0346c76a11dd1e783ebefe4733546 Mon Sep 17 00:00:00 2001 From: k0ndee Date: Wed, 9 Sep 2026 16:19:58 +0200 Subject: [PATCH 71/94] feat: remove derived testIDs and add missing testIDs (#5099) --- docs/6.x/docs/guides/migration.md | 50 +- src/components/Appbar/AppbarContent.tsx | 1 - .../BottomNavigation/BottomNavigation.tsx | 7 +- .../BottomNavigation/BottomNavigationBar.tsx | 6 +- src/components/Button/Button.tsx | 7 +- src/components/Card/Card.tsx | 6 +- src/components/Card/CardTitle.tsx | 7 +- src/components/Checkbox/CheckboxItem.tsx | 1 - src/components/Chip/Chip.tsx | 7 +- src/components/CrossFadeIcon.tsx | 11 +- src/components/DataTable/DataTableCell.tsx | 8 +- .../DataTable/DataTablePagination.tsx | 7 +- src/components/Dialog/Dialog.tsx | 6 + src/components/Drawer/DrawerCollapsedItem.tsx | 6 +- src/components/Drawer/DrawerItem.tsx | 6 + src/components/FAB/Content.tsx | 3 - src/components/FAB/Menu.tsx | 1 - src/components/FAB/Shell.tsx | 2 - src/components/IconButton/IconButton.tsx | 8 +- src/components/List/ListImage.tsx | 1 - src/components/List/ListItem.tsx | 5 +- src/components/Menu/Menu.tsx | 9 +- src/components/Menu/MenuItem.tsx | 1 - src/components/Modal.tsx | 9 +- src/components/ProgressBar.tsx | 1 - src/components/Searchbar.tsx | 29 +- .../SegmentedButtons/SegmentedButtonItem.tsx | 11 +- src/components/Snackbar.tsx | 7 +- src/components/Tooltip/Tooltip.tsx | 1 - .../TouchableRipple.native.tsx | 1 - .../__tests__/Appbar/Appbar.test.tsx | 37 +- .../Appbar/__snapshots__/Appbar.test.tsx.snap | 1040 +++ .../__tests__/BottomNavigation.test.tsx | 56 +- src/components/__tests__/Button.test.tsx | 36 +- src/components/__tests__/Card/Card.test.tsx | 24 +- .../Card/__snapshots__/Card.test.tsx.snap | 248 + .../__tests__/Checkbox/CheckboxItem.test.tsx | 15 +- .../__snapshots__/CheckboxItem.test.tsx.snap | 275 + src/components/__tests__/DataTable.test.tsx | 31 +- src/components/__tests__/Dialog.test.tsx | 6 +- .../Drawer/DrawerCollapsedItem.test.tsx | 96 +- .../DrawerCollapsedItem.test.tsx.snap | 637 ++ src/components/__tests__/FABExtended.test.tsx | 16 +- src/components/__tests__/IconButton.test.tsx | 14 +- src/components/__tests__/ListImage.test.tsx | 45 +- src/components/__tests__/ListItem.test.tsx | 4 +- src/components/__tests__/Menu.test.tsx | 38 +- src/components/__tests__/MenuItem.test.tsx | 8 +- src/components/__tests__/Modal.test.tsx | 175 +- src/components/__tests__/ProgressBar.test.tsx | 6 +- src/components/__tests__/Searchbar.test.tsx | 81 +- .../__tests__/SegmentedButton.test.tsx | 265 +- src/components/__tests__/Tooltip.test.tsx | 56 +- .../__tests__/TouchableRipple.test.tsx | 10 +- .../BottomNavigation.test.tsx.snap | 7007 ++++++++++++++--- .../__snapshots__/Button.test.tsx.snap | 2548 +++++- .../__snapshots__/DataTable.test.tsx.snap | 306 +- .../__snapshots__/FABMenu.test.tsx.snap | 16 - .../__snapshots__/IconButton.test.tsx.snap | 246 + .../__snapshots__/ListImage.test.tsx.snap | 43 +- .../__snapshots__/ListItem.test.tsx.snap | 151 +- .../__snapshots__/ListSection.test.tsx.snap | 6 - .../__snapshots__/Menu.test.tsx.snap | 1358 ++++ .../__snapshots__/Modal.test.tsx.snap | 4576 +++++++++++ .../__snapshots__/ProgressBar.test.tsx.snap | 46 + .../__snapshots__/Searchbar.test.tsx.snap | 404 +- .../SegmentedButton.test.tsx.snap | 2170 +++++ .../TouchableRipple.test.tsx.snap | 123 + 68 files changed, 20598 insertions(+), 1846 deletions(-) create mode 100644 src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap create mode 100644 src/components/__tests__/__snapshots__/Modal.test.tsx.snap create mode 100644 src/components/__tests__/__snapshots__/TouchableRipple.test.tsx.snap diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index a401ad8de8..35a8f74830 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -71,61 +71,41 @@ You can use the component's color prop where available, or override the correspo ### Test IDs -Hardcoded default test IDs have been removed for the components listed below. Many of these components also derive test IDs for their internal parts by appending a suffix to the `testID` prop (e.g. `${testID}-container`). Since `testID` is no longer defaulted to a hardcoded value, none of these derived test IDs are set either unless you pass a `testID` explicitly — so all queries by the IDs below will stop matching: +Hardcoded default test IDs have been removed for the components listed below: - `Appbar.Content`: `appbar-content` - - `appbar-content-title-text` - `Appbar.Header`: `appbar-header` - - `appbar-header-root-layer` - `BottomNavigation`: `bottom-navigation` - - `bottom-navigation-bar` - `BottomNavigation.Bar`: `bottom-navigation-bar` - - `bottom-navigation-bar-content` - - `bottom-navigation-bar-content-wrapper` - `Button`: `button` - - `button-container` - - `button-icon-container` - - `button-text` - `Card`: `card` - - `card-container` - - `card-outline` - `Chip`: `chip` - - `chip-container` - `Drawer.CollapsedItem`: `drawer-collapsed-item` - - `drawer-collapsed-item-outline` - - `drawer-collapsed-item-container` - `FAB`: `floating-action-button` - - `floating-action-button-container` - - `floating-action-button-text` - `FAB.Extended`: `extended-floating-action-button` - - `extended-floating-action-button-container` - - `extended-floating-action-button-text` - `FAB.Menu`: `floating-action-button-menu` - `IconButton`: `icon-button` - - `icon-button-container` - - `icon-button-icon` (and `icon-button-icon-previous` / `icon-button-icon-current` when `animated`) - `Menu`: `menu` - - `menu-view` - - `menu-surface` - `Menu.Item`: `menu-item` - - `menu-item-title` - `Modal`: `modal` - - `modal-backdrop` - - `modal-wrapper` - - `modal-surface` - `ProgressBar`: `progress-bar` - - `progress-bar-fill` - `Searchbar`: `search-bar` - - `search-bar-container` - - `search-bar-icon` - - `search-bar-icon-wrapper` - - `search-bar-clear-icon` - - `search-bar-trailering-icon` - - `search-bar-divider` - `Surface`: `surface` - - `surface-outer-layer` -You can specify a `testID` explicitly to restore both the component's own test ID and all of its derived test IDs above, using the same suffixes. +You can specify a `testID` explicitly to restore each component's own test ID. + +These components used to also derive test IDs for internal, implementation-only elements by appending a suffix to the `testID` prop (e.g. `${testID}-container`, `${testID}-icon`, `${testID}-outline`). They have been removed entirely. + +If you were relying on internal test IDs, update your tests not to rely on internal implementation details and only interact with elements or assert content your users can reach, e.g.: query by role, label, text etc., or `testID` props accepted by the component. + +Some components now accept explicit `testID` props for their interactable elements: + +- `BottomNavigation`: `barTestID` for the internal `BottomNavigation.Bar`, replacing the previous `${testID}-bar` derivation. +- `Chip`: `closeIconTestID` for the close icon button. +- `Dialog` and `Modal`: `overlayTestID` for the overlay displayed behind the content. +- `Menu`: `overlayTestID` for the overlay displayed behind the menu. +- `Searchbar`: `searchTestID`, `clearTestID`, and `trailingTestID` for the search, clear, and trailing icon buttons. +- `Snackbar`: `iconTestID` for the icon button. ## Components diff --git a/src/components/Appbar/AppbarContent.tsx b/src/components/Appbar/AppbarContent.tsx index 386f64158f..37c3b28e2c 100644 --- a/src/components/Appbar/AppbarContent.tsx +++ b/src/components/Appbar/AppbarContent.tsx @@ -135,7 +135,6 @@ const AppbarContent = ({ numberOfLines={1} accessible role={onPress ? 'none' : 'heading'} - testID={testID ? `${testID}-title-text` : undefined} maxFontSizeMultiplier={titleMaxFontSizeMultiplier} > {title} diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index 4556b2bd1b..adecee59a3 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -252,6 +252,10 @@ export type Props = { * TestID used for testing purposes */ testID?: string; + /** + * testID for the underlying `BottomNavigation.Bar`. + */ + barTestID?: string; }; const FAR_FAR_AWAY = Platform.OS === 'web' ? 0 : 9999; @@ -336,6 +340,7 @@ const BottomNavigation = ({ labelMaxFontSizeMultiplier = 1, compact: compactProp, testID, + barTestID, theme: themeOverrides, getLazy = ({ route }: { route: Route }) => route.lazy, }: Props) => { @@ -579,7 +584,7 @@ const BottomNavigation = ({ safeAreaInsets={safeAreaInsets} labelMaxFontSizeMultiplier={labelMaxFontSizeMultiplier} compact={compact} - testID={testID ? `${testID}-bar` : undefined} + testID={barTestID} theme={theme} /> diff --git a/src/components/BottomNavigation/BottomNavigationBar.tsx b/src/components/BottomNavigation/BottomNavigationBar.tsx index eff4e9780b..c53545b39a 100644 --- a/src/components/BottomNavigation/BottomNavigationBar.tsx +++ b/src/components/BottomNavigation/BottomNavigationBar.tsx @@ -493,10 +493,7 @@ const BottomNavigationBar = ({ ]} onLayout={onLayout} > - + ({ }, ]} role={'tablist'} - testID={testID ? `${testID}-content-wrapper` : undefined} > {routes.map((route, index) => { const focused = navigationState.index === index; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 11cda00ccc..82b327dc74 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -292,7 +292,6 @@ const Button = ({ {icon && loading !== true ? ( - + + {React.Children.map(children, (child, index) => React.isValidElement(child) ? React.cloneElement(child as React.ReactElement, { @@ -225,13 +225,12 @@ const Card = ({ style={[{ borderColor }, style]} theme={theme} elevation={elevation} - testID={testID ? `${testID}-container` : undefined} + testID={hasPassedTouchHandler ? undefined : testID} {...rest} > {isMode('outlined') && ( {content} diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index 7332c23014..15477d3033 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -102,6 +102,10 @@ export type Props = ViewProps & { * @optional */ theme?: ThemeProp; + /** + * testID to be used on tests. + */ + testID?: string; }; const LEFT_SIZE = 40; @@ -143,6 +147,7 @@ const CardTitle = ({ rightStyle, style, theme: themeOverrides, + testID, }: Props) => { useInternalTheme(themeOverrides); @@ -150,7 +155,7 @@ const CardTitle = ({ const marginBottom = subtitle ? 0 : 2; return ( - + {left ? ( {left({ diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 9a6995384f..4cffd781fa 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -174,7 +174,6 @@ const CheckboxItem = ({ {isLeading && checkbox} diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 40cb460ffb..5a015fcf8d 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -87,6 +87,10 @@ export type Props = Omit & { * Accessibility label for the close icon. This is read by the screen reader when the user taps the close icon. */ closeIconAccessibilityLabel?: string; + /** + * testID for the close icon button. + */ + closeIconTestID?: string; /** * Function to execute on press. */ @@ -186,6 +190,7 @@ const Chip = ({ 'aria-label': ariaLabel, role = 'button', closeIconAccessibilityLabel = 'Close', + closeIconTestID, onPress, onLongPress, onPressOut, @@ -281,7 +286,6 @@ const Chip = ({ elevation={elevation} transitionDuration={elevationTransitionDuration} {...rest} - testID={testID ? `${testID}-container` : undefined} theme={theme} > {closeIcon ? ( diff --git a/src/components/CrossFadeIcon.tsx b/src/components/CrossFadeIcon.tsx index 696a0bd088..f4a87460ae 100644 --- a/src/components/CrossFadeIcon.tsx +++ b/src/components/CrossFadeIcon.tsx @@ -98,6 +98,7 @@ const CrossFadeIcon = ({ return ( {hasPreviousIcon ? ( - + ) : null} - + diff --git a/src/components/DataTable/DataTableCell.tsx b/src/components/DataTable/DataTableCell.tsx index d1d730e0b3..af332b3ae4 100644 --- a/src/components/DataTable/DataTableCell.tsx +++ b/src/components/DataTable/DataTableCell.tsx @@ -84,7 +84,6 @@ const DataTableCell = ({ > {children} @@ -97,11 +96,7 @@ const CellContent = ({ children, textStyle, maxFontSizeMultiplier, - testID, -}: Pick< - Props, - 'children' | 'textStyle' | 'testID' | 'maxFontSizeMultiplier' ->) => { +}: Pick) => { if (React.isValidElement(children)) { return children; } @@ -111,7 +106,6 @@ const CellContent = ({ style={textStyle} numberOfLines={1} maxFontSizeMultiplier={maxFontSizeMultiplier} - testID={testID ? `${testID}-text-container` : undefined} > {children} diff --git a/src/components/DataTable/DataTablePagination.tsx b/src/components/DataTable/DataTablePagination.tsx index 0e07e5e1c0..0fd2cb7a04 100644 --- a/src/components/DataTable/DataTablePagination.tsx +++ b/src/components/DataTable/DataTablePagination.tsx @@ -292,15 +292,10 @@ const DataTablePagination = ({ {numberOfItemsPerPageList && numberOfItemsPerPage && onItemsPerPageChange && ( - + { const { right, left } = useSafeAreaInsets(); @@ -124,6 +129,7 @@ const Dialog = ({ ]} theme={theme} testID={testID} + overlayTestID={overlayTestID} > {React.Children.toArray(children) .filter((child) => child != null && typeof child !== 'boolean') diff --git a/src/components/Drawer/DrawerCollapsedItem.tsx b/src/components/Drawer/DrawerCollapsedItem.tsx index dccb8cc555..9ffd121dca 100644 --- a/src/components/Drawer/DrawerCollapsedItem.tsx +++ b/src/components/Drawer/DrawerCollapsedItem.tsx @@ -195,13 +195,9 @@ const DrawerCollapsedItem = ({ style, animatedOutlineStyle, ]} - testID={testID ? `${testID}-outline` : undefined} /> - + {badge !== false && ( {typeof badge === 'boolean' ? ( diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index 5924f44e3b..dda753186b 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -64,6 +64,10 @@ export type Props = ViewProps & { * @optional */ theme?: ThemeProp; + /** + * testID to be used on tests. + */ + testID?: string; }; /** @@ -98,6 +102,7 @@ const DrawerItem = ({ right, labelMaxFontSizeMultiplier, hitSlop, + testID, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); @@ -129,6 +134,7 @@ const DrawerItem = ({ aria-label={ariaLabel} theme={theme} hitSlop={hitSlop} + testID={testID} > diff --git a/src/components/FAB/Content.tsx b/src/components/FAB/Content.tsx index 6114bcd62c..2055405688 100644 --- a/src/components/FAB/Content.tsx +++ b/src/components/FAB/Content.tsx @@ -27,7 +27,6 @@ export type ContentProps = { labelAnimatedStyle?: StyleProp>; labelNumberOfLines?: number; labelEllipsisMode?: 'clip' | 'tail' | 'head' | 'middle'; - testID?: string; }; /** @@ -50,7 +49,6 @@ const Content = ({ labelAnimatedStyle, labelNumberOfLines, labelEllipsisMode, - testID, }: ContentProps) => { const hasLabel = label !== undefined && label !== ''; const colorStyle = { color: contentColor }; @@ -86,7 +84,6 @@ const Content = ({ ellipsizeMode={labelEllipsisMode} maxFontSizeMultiplier={labelMaxFontSizeMultiplier} style={colorStyle} - testID={testID ? `${testID}-text` : undefined} > {label} diff --git a/src/components/FAB/Menu.tsx b/src/components/FAB/Menu.tsx index 438d7c2f1a..e4884647d6 100644 --- a/src/components/FAB/Menu.tsx +++ b/src/components/FAB/Menu.tsx @@ -284,7 +284,6 @@ const MenuItem = ({ leading={leading} trailing={trailing} iconLabelGap={iconLabelGap} - testID={testID} /> diff --git a/src/components/FAB/Shell.tsx b/src/components/FAB/Shell.tsx index 6a07babb47..83a71f72ed 100644 --- a/src/components/FAB/Shell.tsx +++ b/src/components/FAB/Shell.tsx @@ -319,7 +319,6 @@ const Shell = ({ visible ? styles.pointerEventsAuto : styles.pointerEventsNone, ]} elevation={elevation} - testID={testID ? `${testID}-container` : undefined} theme={theme} > @@ -357,7 +356,6 @@ const Shell = ({ labelAnimatedStyle={labelAnimatedStyle} labelNumberOfLines={labelAnimatedStyle ? 1 : undefined} labelEllipsisMode={labelAnimatedStyle ? 'clip' : undefined} - testID={testID} /> )} diff --git a/src/components/IconButton/IconButton.tsx b/src/components/IconButton/IconButton.tsx index a55c75e6c6..1d673a3d27 100644 --- a/src/components/IconButton/IconButton.tsx +++ b/src/components/IconButton/IconButton.tsx @@ -161,7 +161,6 @@ const IconButton = ({ return ( ) : ( - + )} diff --git a/src/components/List/ListImage.tsx b/src/components/List/ListImage.tsx index 55e2a527db..213d4254c7 100644 --- a/src/components/List/ListImage.tsx +++ b/src/components/List/ListImage.tsx @@ -50,7 +50,6 @@ const ListImage = ({ style={getStyles()} source={source} accessibilityIgnoresInvertColors - testID="list-image" /> ); }; diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 740b3df5d2..feb0f19f12 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -245,10 +245,7 @@ const ListItem = ({ style: getLeftStyles(alignToTop, description), }) : null} - + {renderTitle()} {description diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 12b3c536c7..96dd4096b7 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -68,6 +68,10 @@ export type Props = { * Accessibility label for the overlay. This is read by the screen reader when the user taps outside the menu. */ overlayAccessibilityLabel?: string; + /** + * testID for the overlay that is displayed behind the menu. + */ + overlayTestID?: string; /** * Content of the `Menu`. */ @@ -186,6 +190,7 @@ const Menu = ({ visible, statusBarHeight, overlayAccessibilityLabel = 'Close menu', + overlayTestID, testID, anchor, onDismiss, @@ -681,6 +686,7 @@ const Menu = ({ onPress={onDismiss} pointerEvents={visible ? 'auto' : 'none'} style={styles.pressableOverlay} + testID={overlayTestID} /> { @@ -691,7 +697,6 @@ const Menu = ({ style={[styles.wrapper, positionStyle, style]} pointerEvents={pointerEvents} onAccessibilityEscape={onDismiss} - testID={testID ? `${testID}-view` : undefined} > diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index da0975a44a..fbc1197630 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -36,6 +36,10 @@ export type Props = { * Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal. */ overlayAccessibilityLabel?: string; + /** + * testID for the overlay that is displayed behind the modal content. + */ + overlayTestID?: string; /** * Determines Whether the modal is visible. */ @@ -127,6 +131,7 @@ function Modal({ dismissableBackButton = dismissable, visible = false, overlayAccessibilityLabel = 'Close modal', + overlayTestID, onDismiss = () => {}, children, contentContainerStyle, @@ -235,7 +240,7 @@ function Modal({ onPress={dismissable ? onDismissCallback : undefined} importantForAccessibility="no" style={[styles.backdrop, backdropStyle, backdropTransitionStyle]} - testID={testID ? `${testID}-backdrop` : undefined} + testID={overlayTestID} /> {width ? ( & { * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ searchAccessibilityLabel?: string; + /** + * testID for the left icon button (see `onIconPress`). + */ + searchTestID?: string; /** * Custom icon for clear button, default will be icon close. It's visible when `loading` is set to `false`. * In v5.x with theme version 3, `clearIcon` is visible only if `right` prop is not defined. @@ -72,6 +76,10 @@ export type Props = Omit & { * Accessibility label for the button. This is read by the screen reader when the user taps the button. */ clearAccessibilityLabel?: string; + /** + * testID for the clear button. + */ + clearTestID?: string; /** * @supported Available in v5.x with theme version 3 * Icon name for the right trailering icon button. @@ -91,6 +99,10 @@ export type Props = Omit & { * Accessibility label for the right trailering icon button. This is read by the screen reader when the user taps the button. */ traileringIconAccessibilityLabel?: string; + /** + * testID for the right trailering icon button. + */ + trailingTestID?: string; /** * @supported Available in v5.x with theme version 3 * Callback which returns a React element to display on the right side. @@ -166,12 +178,15 @@ const Searchbar = ({ iconColor: customIconColor, onIconPress, searchAccessibilityLabel = 'search', + searchTestID, clearIcon, clearAccessibilityLabel = 'clear', + clearTestID, onClearIconPress, traileringIcon, traileringIconColor, traileringIconAccessibilityLabel, + trailingTestID, onTraileringIconPress, right, mode = 'bar', @@ -235,7 +250,6 @@ const Searchbar = ({ backgroundColor={theme.colors.surfaceContainerHigh} borderRadius={isBarMode ? theme.shapes.corner.extraLarge : cornerNone} style={[styles.container, style]} - testID={testID ? `${testID}-container` : undefined} elevation={elevation} theme={theme} > @@ -257,7 +271,7 @@ const Searchbar = ({ } theme={theme} aria-label={searchAccessibilityLabel} - testID={testID ? `${testID}-icon` : undefined} + testID={searchTestID} /> {loading ? ( - + ) : ( // Clear icon should be always rendered within Searchbar – it's transparent, // without touch events, when there is no value. It's done to avoid issues @@ -295,7 +306,6 @@ const Searchbar = ({ // when clearing the value. )) } - testID={testID ? `${testID}-clear-icon` : undefined} role="button" theme={theme} + testID={clearTestID} /> )} @@ -331,7 +341,7 @@ const Searchbar = ({ iconColor={traileringIconColor || colors.onSurfaceVariant} icon={traileringIcon} aria-label={traileringIconAccessibilityLabel} - testID={testID ? `${testID}-trailering-icon` : undefined} + testID={trailingTestID} /> ) : null} {isBarMode && @@ -345,7 +355,6 @@ const Searchbar = ({ backgroundColor: colors.outline, }, ]} - testID={testID ? `${testID}-divider` : undefined} /> )} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 4b851de75a..33886d7ec1 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -223,18 +223,12 @@ const SegmentedButtonItem = ({ style={[styles.content, { paddingVertical, opacity: textOpacity }]} > {showCheckedIcon ? ( - + ) : null} {showIcon ? ( - + ) : null} @@ -244,7 +238,6 @@ const SegmentedButtonItem = ({ selectable={false} numberOfLines={1} maxFontSizeMultiplier={labelMaxFontSizeMultiplier} - testID={testID ? `${testID}-label` : undefined} > {label} diff --git a/src/components/Snackbar.tsx b/src/components/Snackbar.tsx index fc43a08bf2..fac1f7a049 100644 --- a/src/components/Snackbar.tsx +++ b/src/components/Snackbar.tsx @@ -53,6 +53,10 @@ export type Props = Omit & { * Accessibility label for the icon button. This is read by the screen reader when the user taps the button. */ iconAccessibilityLabel?: string; + /** + * testID for the icon button. + */ + iconTestID?: string; /** * The duration for which the Snackbar is shown. */ @@ -150,6 +154,7 @@ const Snackbar = ({ icon, onIconPress, iconAccessibilityLabel = 'Close icon', + iconTestID, duration = DURATION_MEDIUM, onDismiss, children, @@ -339,7 +344,7 @@ const Snackbar = ({ } aria-label={iconAccessibilityLabel} style={styles.icon} - testID={testID ? `${testID}-icon` : undefined} + testID={iconTestID} /> ) : null} diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 144acc3d32..d0dc180115 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -216,7 +216,6 @@ const Tooltip = ({ ...(measurement.measured ? styles.visible : styles.hidden), }, ]} - testID="tooltip-container" > {pressed && rippleEffectEnabled && ( { describe('AppbarAction', () => { it('should be rendered with default theme color', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') - .props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarActionIcon.props.color).toBe( - LightTheme.colors.onSurfaceVariant - ); + expect(toJSON()).toMatchSnapshot(); }); it('should be rendered with specific theme color if is leading', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') - .props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarActionIcon.props.color).toBe(LightTheme.colors.onSurface); + expect(toJSON()).toMatchSnapshot(); }); it('should be rendered with custom color', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarActionIcon = screen.getByTestId('appbar-action-icon-current') - .props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarActionIcon.props.color).toBe('purple'); + expect(toJSON()).toMatchSnapshot(); }); it('should render AppbarBackAction with custom color', async () => { - await render( + const { toJSON } = await render( ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const appbarBackActionIcon = screen.getByTestId( - 'appbar-action-icon-current' - ).props.children; - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(appbarBackActionIcon.props.color).toBe('purple'); + expect(toJSON()).toMatchSnapshot(); }); }); @@ -254,7 +235,7 @@ describe('AppbarContent', () => { ); - expect(screen.getByTestId('appbar-content-title-text')).toHaveStyle( + expect(screen.getByText('Title')).toHaveStyle( LightTheme.fonts[modeTextVariant[mode]] ); }) diff --git a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap index ad5805e1e6..faeb89916b 100644 --- a/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap +++ b/src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap @@ -956,3 +956,1043 @@ exports[`Appbar passes additional props to AppbarBackAction, AppbarContent and A `; + +exports[`AppbarAction should be rendered with custom color 1`] = ` + + + + + + + + + menu + + + + + + + +`; + +exports[`AppbarAction should be rendered with default theme color 1`] = ` + + + + + + + + + menu + + + + + + + +`; + +exports[`AppbarAction should be rendered with specific theme color if is leading 1`] = ` + + + + + + + + + menu + + + + + + + +`; + +exports[`AppbarAction should render AppbarBackAction with custom color 1`] = ` + + + + + + + + + + + + + + + + +`; diff --git a/src/components/__tests__/BottomNavigation.test.tsx b/src/components/__tests__/BottomNavigation.test.tsx index 2f34c4c7c2..1020631055 100644 --- a/src/components/__tests__/BottomNavigation.test.tsx +++ b/src/components/__tests__/BottomNavigation.test.tsx @@ -159,6 +159,7 @@ it('calls onIndexChange', async () => { await render( { await render( { await render( { }); it('renders custom background color passed to barStyle property', async () => { - await render( + const { toJSON } = await render( { /> ); - const wrapper = screen.getByTestId('bottom-navigation-bar-content'); - expect(wrapper).toHaveStyle({ backgroundColor: Palette.error60 }); + expect(toJSON()).toMatchSnapshot(); }); it('uses the rendered bar height when hiding it for the keyboard', async () => { @@ -528,7 +530,7 @@ it('renders bottom navigation with getLazy', async () => { }); it('applies maxTabBarWidth styling if compact prop is truthy', async () => { - await render( + const { toJSON } = await render( { /> ); - expect( - screen.getByTestId('bottom-navigation-bar-content-wrapper') - ).toHaveStyle({ - maxWidth: 480, - }); + expect(toJSON()).toMatchSnapshot(); }); it('does not apply maxTabBarWidth styling if compact prop is falsy', async () => { - await render( + const { toJSON } = await render( /> ); - expect( - screen.getByTestId('bottom-navigation-bar-content-wrapper') - ).not.toHaveStyle({ - maxWidth: 480, - }); -}); - -it('renders bar content when shifting is enabled', async () => { - await render( - route.key === 'key-2'} - shifting - /> - ); - - expect(screen.getByTestId('bottom-navigation-bar-content')).toBeOnTheScreen(); -}); - -it('does not render legacy ripple overlay when shifting is disabled', async () => { - await render( - route.key === 'key-2'} - shifting={false} - /> - ); - - expect( - screen.queryByTestId('bottom-navigation-bar-content-ripple') - ).not.toBeOnTheScreen(); + expect(toJSON()).toMatchSnapshot(); }); describe('getActiveTintColor', () => { diff --git a/src/components/__tests__/Button.test.tsx b/src/components/__tests__/Button.test.tsx index 69329232e2..7ea496a419 100644 --- a/src/components/__tests__/Button.test.tsx +++ b/src/components/__tests__/Button.test.tsx @@ -180,7 +180,7 @@ describe('button text styles', () => { ); - expect(screen.getByTestId('button-text')).toHaveStyle({ + expect(screen.getByText('Test')).toHaveStyle({ textTransform: 'uppercase', }); }); @@ -192,7 +192,7 @@ describe('button text styles', () => { ); - expect(screen.getByTestId('button-text')).not.toHaveStyle({ + expect(screen.getByText('Test')).not.toHaveStyle({ textTransform: 'uppercase', }); }); @@ -200,60 +200,44 @@ describe('button text styles', () => { describe('button icon styles', () => { it('should return correct icon styles for compact text button', async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle({ - marginLeft: 6, - marginRight: 0, - }); + expect(toJSON()).toMatchSnapshot(); }); (['outlined', 'contained', 'contained-tonal', 'elevated'] as const).forEach( (mode) => it(`should return correct icon styles for compact ${mode} button`, async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle( - { - marginLeft: 8, - marginRight: 0, - } - ); + expect(toJSON()).toMatchSnapshot(); }) ); it('should return correct icon styles for text button', async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle({ - marginLeft: 12, - marginRight: -8, - }); + expect(toJSON()).toMatchSnapshot(); }); (['outlined', 'contained', 'contained-tonal', 'elevated'] as const).forEach( (mode) => it(`should return correct icon styles for compact ${mode} button`, async () => { - await render( + const { toJSON } = await render( ); - expect(screen.getByTestId('compact-button-icon-container')).toHaveStyle( - { - marginLeft: 16, - marginRight: -16, - } - ); + expect(toJSON()).toMatchSnapshot(); }) ); }); diff --git a/src/components/__tests__/Card/Card.test.tsx b/src/components/__tests__/Card/Card.test.tsx index fab15f0a26..7d84f0e44f 100644 --- a/src/components/__tests__/Card/Card.test.tsx +++ b/src/components/__tests__/Card/Card.test.tsx @@ -33,43 +33,31 @@ describe('Card', () => { }); it('renders an outlined card with a custom outline color', async () => { - const testID = 'custom-outline-card'; - - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId(`${testID}-outline`)).toHaveStyle({ - borderColor: 'purple', - borderWidth: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('renders an outlined card with custom border color', async () => { - const testID = 'custom-border-card'; - - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId(`${testID}-outline`)).toHaveStyle({ - borderColor: Palette.error50, - borderWidth: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('renders with a custom theme background color', async () => { @@ -92,12 +80,12 @@ describe('Card', () => { it('renders with a content style', async () => { await render( - + Content ); - expect(screen.getByTestId('card')).toHaveStyle(styles.contentStyle); + expect(screen.getByText('Content').parent).toHaveStyle(styles.contentStyle); }); it('does not render a disabled accessibility state', async () => { diff --git a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap index 0554292cee..216e5b5d08 100644 --- a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap +++ b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap @@ -121,3 +121,251 @@ exports[`Card renders an outlined card 1`] = ` /> `; + +exports[`Card renders an outlined card with a custom outline color 1`] = ` + + + + + +`; + +exports[`Card renders an outlined card with custom border color 1`] = ` + + + + + +`; diff --git a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx index b1a9c38925..ee52c1b3e4 100644 --- a/src/components/__tests__/Checkbox/CheckboxItem.test.tsx +++ b/src/components/__tests__/Checkbox/CheckboxItem.test.tsx @@ -59,14 +59,13 @@ it('disables the row when the prop disabled is true', async () => { }); it('should have maxFontSizeMultiplier set to 1.5 by default', async () => { - await render( - - ); - const checkboxItemText = screen.getByTestId('checkbox-item-text', { - includeHiddenElements: true, - }); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(checkboxItemText.props.maxFontSizeMultiplier).toBe(1.5); + const tree = ( + await render( + + ) + ).toJSON(); + + expect(tree).toMatchSnapshot(); }); it('should execute onLongPress', async () => { diff --git a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap index bc5b884910..e2d338f791 100644 --- a/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap +++ b/src/components/__tests__/Checkbox/__snapshots__/CheckboxItem.test.tsx.snap @@ -551,3 +551,278 @@ exports[`renders unchecked 1`] = ` `; + +exports[`should have maxFontSizeMultiplier set to 1.5 by default 1`] = ` + + + + + + + + + + + + + + + + + + +`; diff --git a/src/components/__tests__/DataTable.test.tsx b/src/components/__tests__/DataTable.test.tsx index 49f863720a..7a29a72800 100644 --- a/src/components/__tests__/DataTable.test.tsx +++ b/src/components/__tests__/DataTable.test.tsx @@ -66,25 +66,28 @@ describe('DataTable.Cell', () => { expect(tree).toMatchSnapshot(); }); - it('renders data table cell with text container', async () => { - await render( - Table cell - ); + it('renders data table cell with text content', async () => { + const tree = ( + await render( + Table cell + ) + ).toJSON(); expect(screen.getByText('Table cell')).toBeOnTheScreen(); - expect(screen.getByTestId('table-cell-text-container')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); - it('renders data table cell children without text container', async () => { - await render( - - - - ); + it('renders data table cell children without wrapping text component', async () => { + const tree = ( + await render( + + + + ) + ).toJSON(); - expect( - screen.queryByTestId('table-cell-text-container') - ).not.toBeOnTheScreen(); + expect(screen.getByTestId('table-cell-checkbox')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 32477e5fe1..48d22a587b 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -41,7 +41,7 @@ describe('Dialog', () => { ); - await userEvent.press(screen.getByTestId('dialog-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await act(() => { jest.runAllTimers(); @@ -57,7 +57,7 @@ describe('Dialog', () => { ); - await userEvent.press(screen.getByTestId('dialog-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await act(() => { jest.runAllTimers(); @@ -80,7 +80,7 @@ describe('Dialog', () => { ); - await userEvent.press(screen.getByTestId('dialog-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await act(() => { jest.runAllTimers(); diff --git a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx index fd0263b5e2..b301d39062 100644 --- a/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx +++ b/src/components/__tests__/Drawer/DrawerCollapsedItem.test.tsx @@ -1,80 +1,62 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../test-utils'; +import { render } from '../../../test-utils'; import DrawerCollapsedItem from '../../Drawer/DrawerCollapsedItem'; describe('DrawerCollapsedItem', () => { it('should have regular outline if label is specified', async () => { - await render( - - ); - - expect(screen.getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ - height: 32, - }); + const tree = ( + await render( + + ) + ).toJSON(); + + expect(tree).toMatchSnapshot(); }); it('should have rounded outline if label is not specified', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect(screen.getByTestId('drawer-collapsed-item-outline')).toHaveStyle({ - height: 56, - }); + expect(tree).toMatchSnapshot(); }); it('should display unfocused icon in inactive state, if unfocused icon is specified', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('drawer-collapsed-item-container').props.children[1] - .props.source - ).toBe('star-outline'); + expect(tree).toMatchSnapshot(); }); it('should display focused icon in inactive state, if unfocused icon is not specified', async () => { - await render( - - ); + const tree = ( + await render() + ).toJSON(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('drawer-collapsed-item-container').props.children[1] - .props.source - ).toBe('star'); + expect(tree).toMatchSnapshot(); }); it('should display focused icon in active state', async () => { - await render( - - ); - - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('drawer-collapsed-item-container').props.children[1] - .props.source - ).toBe('star'); + const tree = ( + await render( + + ) + ).toJSON(); + + expect(tree).toMatchSnapshot(); }); }); diff --git a/src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap b/src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap new file mode 100644 index 0000000000..1bdc1cec4f --- /dev/null +++ b/src/components/__tests__/Drawer/__snapshots__/DrawerCollapsedItem.test.tsx.snap @@ -0,0 +1,637 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DrawerCollapsedItem should display focused icon in active state 1`] = ` + + + + + + + star + + + + + +`; + +exports[`DrawerCollapsedItem should display focused icon in inactive state, if unfocused icon is not specified 1`] = ` + + + + + + + star + + + + + +`; + +exports[`DrawerCollapsedItem should display unfocused icon in inactive state, if unfocused icon is specified 1`] = ` + + + + + + + star-outline + + + + + +`; + +exports[`DrawerCollapsedItem should have regular outline if label is specified 1`] = ` + + + + + + + star-outline + + + + starred + + + + +`; + +exports[`DrawerCollapsedItem should have rounded outline if label is not specified 1`] = ` + + + + + + + star-outline + + + + + +`; diff --git a/src/components/__tests__/FABExtended.test.tsx b/src/components/__tests__/FABExtended.test.tsx index e8e530484d..e16ca3854b 100644 --- a/src/components/__tests__/FABExtended.test.tsx +++ b/src/components/__tests__/FABExtended.test.tsx @@ -1,4 +1,5 @@ -import { Platform } from 'react-native'; +import * as React from 'react'; +import { Platform, View } from 'react-native'; import { afterEach, expect, it, jest } from '@jest/globals'; import { fireEvent, userEvent } from '@testing-library/react-native'; @@ -43,20 +44,25 @@ it('expands to fit the measured label width', async () => { pageY: 0, }); + const ref = React.createRef(); await render( ); await jest.runAllTimersAsync(); - expect( - Reanimated.getAnimatedStyle(screen.getByTestId('extended-fab-container')) - ).toMatchObject({ width: 144 }); + if (!ref.current) { + throw new Error('Expected FAB ref to be attached'); + } + + expect(Reanimated.getAnimatedStyle(ref.current)).toMatchObject({ + width: 144, + }); }); it('renders extended FAB collapsed', async () => { diff --git a/src/components/__tests__/IconButton.test.tsx b/src/components/__tests__/IconButton.test.tsx index d804629670..914f1e2c55 100644 --- a/src/components/__tests__/IconButton.test.tsx +++ b/src/components/__tests__/IconButton.test.tsx @@ -2,7 +2,7 @@ import { StyleSheet } from 'react-native'; import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../test-utils'; +import { render } from '../../test-utils'; import { pink500 } from '../../theme/colors'; import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; @@ -53,7 +53,7 @@ it('renders icon change animated', async () => { }); it('renders icon button with custom border radius', async () => { - await render( + const { toJSON } = await render( { /> ); - expect(screen.getByTestId('icon-button-container')).toHaveStyle({ - borderRadius: 0, - }); + expect(toJSON()).toMatchSnapshot(); }); it('renders icon button with small border radius', async () => { - await render( + const { toJSON } = await render( { /> ); - expect(screen.getByTestId('icon-button-container')).toHaveStyle({ - borderRadius: 4, - }); + expect(toJSON()).toMatchSnapshot(); }); describe('getIconButtonColor - icon color', () => { diff --git a/src/components/__tests__/ListImage.test.tsx b/src/components/__tests__/ListImage.test.tsx index 8aa7e9b54d..04796fd73a 100644 --- a/src/components/__tests__/ListImage.test.tsx +++ b/src/components/__tests__/ListImage.test.tsx @@ -2,27 +2,16 @@ import { StyleSheet } from 'react-native'; import { expect, it } from '@jest/globals'; -import { render, screen } from '../../test-utils'; +import { render } from '../../test-utils'; import ListImage from '../List/ListImage'; const styles = StyleSheet.create({ - image: { - width: 56, - height: 56, - }, - video: { - width: 114, - height: 64, - marginLeft: 0, - }, container: { width: 30, height: 56, }, }); -const testID = 'list-image'; - it('renders ListImage with default variant', async () => { const tree = ( await render( @@ -49,23 +38,27 @@ it('renders ListImage with default variant & styles', async () => { }); it('renders ListImage with `image` variant', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect(screen.getByTestId(testID)).toHaveStyle(styles.image); + expect(tree).toMatchSnapshot(); }); it('renders ListImage with `video` variant', async () => { - await render( - - ); + const tree = ( + await render( + + ) + ).toJSON(); - expect(screen.getByTestId(testID)).toHaveStyle(styles.video); + expect(tree).toMatchSnapshot(); }); diff --git a/src/components/__tests__/ListItem.test.tsx b/src/components/__tests__/ListItem.test.tsx index e0e1cb5303..98e6e0f0fe 100644 --- a/src/components/__tests__/ListItem.test.tsx +++ b/src/components/__tests__/ListItem.test.tsx @@ -166,7 +166,7 @@ it('calling onPress on ListItem right component', async () => { }); it('renders list item with custom content style', async () => { - await render( + const { toJSON } = await render( { /> ); - expect(screen.getByTestId('list-item-content')).toHaveStyle(styles.content); + expect(toJSON()).toMatchSnapshot(); }); diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index 856e339e06..c35880b48f 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -70,7 +70,7 @@ elevations.forEach((elevation) =>
    ); - expect(screen.getByTestId(`${testID}-surface`)).toHaveStyle({ + expect(screen.getByTestId(testID)).toHaveStyle({ backgroundColor: LightTheme.colors.elevation[`level${elevation}`], }); }) @@ -108,7 +108,7 @@ it('uses the default anchorPosition of top', async () => { ); } - const { rerender } = await render(makeMenu(false)); + const { rerender, toJSON } = await render(makeMenu(false)); // You must update instead of creating directly and using it because // componentDidUpdate isn't called by default in jest. Forcing the update @@ -121,14 +121,13 @@ it('uses the default anchorPosition of top', async () => { }); await waitFor(() => { - const menu = screen.getByTestId(`${testID}-view`); - expect(menu).toHaveStyle({ - position: 'absolute', - left: 100, - top: 100, - }); + const json = JSON.stringify(toJSON()); + expect(json).toContain('"left":100'); + expect(json).toContain('"top":100'); }); + expect(toJSON()).toMatchSnapshot(); + measureSpy.mockRestore(); dimensionsSpy.mockRestore(); }); @@ -166,7 +165,7 @@ it('respects anchorPosition bottom', async () => { ); } - const { rerender } = await render(makeMenu(false)); + const { rerender, toJSON } = await render(makeMenu(false)); await act(async () => { await rerender(makeMenu(true)); @@ -175,14 +174,13 @@ it('respects anchorPosition bottom', async () => { }); await waitFor(() => { - const menu = screen.getByTestId(`${testID}-view`); - expect(menu).toHaveStyle({ - position: 'absolute', - left: 100, - top: 132, - }); + const json = JSON.stringify(toJSON()); + expect(json).toContain('"left":100'); + expect(json).toContain('"top":132'); }); + expect(toJSON()).toMatchSnapshot(); + measureSpy.mockRestore(); dimensionsSpy.mockRestore(); }); @@ -205,11 +203,8 @@ it('renders menu with mode "elevated"', async () => { ); - const menuSurface = screen.getByTestId(`${testID}-surface`); - - // Get flattened styles // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const styles = StyleSheet.flatten(menuSurface.props.style); + const styles = StyleSheet.flatten(screen.getByTestId(testID).props.style); expect(styles).toHaveProperty('shadowColor'); expect(styles).toHaveProperty('shadowOpacity'); @@ -233,11 +228,8 @@ it('renders menu with mode "flat"', async () => { ); - const menuSurface = screen.getByTestId(`${testID}-surface`); - - // Get flattened styles // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const styles = StyleSheet.flatten(menuSurface.props.style); + const styles = StyleSheet.flatten(screen.getByTestId(testID).props.style); expect(styles).not.toHaveProperty('shadowColor'); expect(styles).not.toHaveProperty('shadowOpacity'); diff --git a/src/components/__tests__/MenuItem.test.tsx b/src/components/__tests__/MenuItem.test.tsx index fd05f64cd5..7f83575109 100644 --- a/src/components/__tests__/MenuItem.test.tsx +++ b/src/components/__tests__/MenuItem.test.tsx @@ -48,10 +48,10 @@ describe('Menu Item', () => { /> ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('menu-item-title').props.maxFontSizeMultiplier - ).toBe(labelMaxFontSizeMultiplier); + expect(screen.getByText('Cut')).toHaveProp( + 'maxFontSizeMultiplier', + labelMaxFontSizeMultiplier + ); }); it('accepts aria-checked prop', async () => { diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 83d644e0e1..657eaf0992 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -57,7 +57,7 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ backgroundColor: LightTheme.colors.scrim, }); }); @@ -77,29 +77,26 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ backgroundColor: 'transparent', }); }); it('should receive appropriate top and bottom insets', async () => { - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-wrapper')).toHaveStyle({ - marginTop: 37, - marginBottom: 44, - }); + expect(toJSON()).toMatchSnapshot(); }); }); describe('when open', () => { describe('if backdrop touched', () => { it('should invoke the onDismiss function immediately', async () => { const onDismiss = jest.fn(); - await render( + const { toJSON } = await render( {null} @@ -107,42 +104,36 @@ describe('Modal', () => { expect(onDismiss).not.toHaveBeenCalled(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); expect(onDismiss).toHaveBeenCalled(); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); expect(onDismiss).toHaveBeenCalledTimes(1); }); }); it('runs the closing animation if visible toggled', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {}}> {null} ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); await rerender( {}}> @@ -150,59 +141,47 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.queryByTestId('modal-surface')).not.toBeOnTheScreen(); - - expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); + expect(toJSON()).toBeNull(); }); describe('if closed via Android back button', () => { it('invokes onDismiss', async () => { const onDismiss = jest.fn(); - await render( + const { toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { BackHandler.mockPressBack(); }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); expect(onDismiss).toHaveBeenCalledTimes(1); }); @@ -212,7 +191,7 @@ describe('Modal', () => { describe('when open as non-dismissible modal', () => { describe('if closed via touching backdrop', () => { it('will run the animation but not fade out', async () => { - await render( + const { toJSON } = await render( { ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('should not invoke onDismiss', async () => { @@ -261,7 +234,7 @@ describe('Modal', () => { expect(onDismiss).not.toHaveBeenCalled(); - await userEvent.press(screen.getByTestId('modal-backdrop')); + await userEvent.press(screen.getByLabelText('Close modal')); expect(onDismiss).not.toHaveBeenCalled(); @@ -275,7 +248,7 @@ describe('Modal', () => { describe('if closed via Android back button', () => { it('will run the animation but not fade out', async () => { - await render( + const { toJSON } = await render( { ); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { BackHandler.mockPressBack(); }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); it('should not invoke onDismiss', async () => { @@ -345,7 +312,7 @@ describe('Modal', () => { describe('when visible prop changes', () => { describe('from false to true (closed to open)', () => { it('should run fade-in animation on opening', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} @@ -359,40 +326,34 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 0, - }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: 0, }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); }); describe('from true to false (open to closed)', () => { it('should run fade-out animation on closing', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await rerender( @@ -400,12 +361,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); @@ -441,18 +400,16 @@ describe('Modal', () => { }); it('should close even if the dialog is not dismissible', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await rerender( @@ -460,12 +417,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { jest.runAllTimers(); @@ -479,18 +434,16 @@ describe('Modal', () => { describe('when visible prop changes again during the open/close animation', () => { describe('while closing, back to true (visible)', () => { it('should keep the modal open', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await rerender( @@ -498,12 +451,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); await act(() => { // Not a real seconds, this depends on how frequently @@ -521,24 +472,22 @@ describe('Modal', () => { jest.runAllTimers(); }); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: scrimAlpha, }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ - opacity: 1, - }); + expect(toJSON()).toMatchSnapshot(); }); }); describe('while opening, back to false (hidden)', () => { it('should keep the modal closed', async () => { - const { rerender } = await render( + const { rerender, toJSON } = await render( {null} ); - expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); + expect(screen.queryByLabelText('Close modal')).not.toBeOnTheScreen(); await rerender( @@ -546,12 +495,10 @@ describe('Modal', () => { ); - expect(screen.getByTestId('modal-backdrop')).toHaveStyle({ - opacity: 0, - }); - expect(screen.getByTestId('modal-surface')).toHaveStyle({ + expect(screen.getByLabelText('Close modal')).toHaveStyle({ opacity: 0, }); + expect(toJSON()).toMatchSnapshot(); await act(() => { // Not a real seconds, this depends on how frequently @@ -559,7 +506,7 @@ describe('Modal', () => { jest.advanceTimersToNextTimer(1000); }); - expect(screen.getByTestId('modal-backdrop')).toBeOnTheScreen(); + expect(screen.getByLabelText('Close modal')).toBeOnTheScreen(); await rerender( @@ -571,7 +518,7 @@ describe('Modal', () => { jest.runAllTimers(); }); - expect(screen.queryByTestId('modal-backdrop')).not.toBeOnTheScreen(); + expect(screen.queryByLabelText('Close modal')).not.toBeOnTheScreen(); }); }); }); diff --git a/src/components/__tests__/ProgressBar.test.tsx b/src/components/__tests__/ProgressBar.test.tsx index bed20ed08d..afd95f664b 100644 --- a/src/components/__tests__/ProgressBar.test.tsx +++ b/src/components/__tests__/ProgressBar.test.tsx @@ -94,12 +94,10 @@ it('has progressbar role', async () => { }); it('renders progress bar with custom style of filled part', async () => { - await render( + const view = await render( ); await triggerLayout(); - expect(screen.getByTestId('progress-bar-fill')).toHaveStyle({ - borderRadius: 4, - }); + expect(view.toJSON()).toMatchSnapshot(); }); diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 5fea2800ed..4f4f7ed4c3 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -30,13 +30,13 @@ it('activity indicator snapshot test', async () => { it('renders with ActivityIndicator', async () => { await render(); - expect(screen.getByTestId('activity-indicator')).toBeOnTheScreen(); + expect(screen.getByRole('progressbar')).toBeOnTheScreen(); }); it('renders without ActivityIndicator', async () => { await render(); - expect(screen.queryByTestId('activity-indicator')).not.toBeOnTheScreen(); + expect(screen.queryByRole('progressbar')).not.toBeOnTheScreen(); }); it('renders clear icon with custom color', async () => { @@ -44,30 +44,25 @@ it('renders clear icon with custom color', async () => { ); - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - const iconComponent = screen.getByTestId('search-bar-icon-wrapper').props - .children; - - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - expect(iconComponent.props.iconColor).toBe('purple'); -}); - -it('renders clear icon wrapper, which can be the target of touch events, if search has value', async () => { - await render(); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('search-bar-icon-wrapper').props.pointerEvents - ).toBe('auto'); + screen.getByText('close', { includeHiddenElements: true }) + ).toHaveStyle({ color: 'purple' }); }); -it('renders clear icon wrapper, which is never target of touch events, if search has no value', async () => { - await render(); +it('does not respond to touch on the clear icon when search has no value', async () => { + const onClearIconPressMock = jest.fn(); + await render( + + ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('search-bar-icon-wrapper').props.pointerEvents - ).toBe('none'); + await userEvent.press( + screen.getByLabelText('clear', { includeHiddenElements: true }) + ); + expect(onClearIconPressMock).not.toHaveBeenCalled(); }); it('defines onClearIconPress action and checks if it is called when close button is pressed', async () => { @@ -79,20 +74,12 @@ it('defines onClearIconPress action and checks if it is called when close button onClearIconPress={onClearIconPressMock} /> ); - await userEvent.press(screen.getByTestId('search-bar-clear-icon')); + await userEvent.press(screen.getByLabelText('clear')); expect(onClearIconPressMock).toHaveBeenCalledTimes(1); }); -it('renders clear icon wrapper, with appropriate style for v3', async () => { - const { rerender } = await render(); - - expect(screen.getByTestId('search-bar-icon-wrapper')).toHaveStyle({ - position: 'absolute', - right: 0, - marginLeft: 16, - }); - - await rerender( +it('hides the clear icon when a custom right element is rendered', async () => { + await render( { /> ); - expect( - screen.getByTestId('search-bar-icon-wrapper', { - includeHiddenElements: true, - }) - ).toHaveStyle({ display: 'none' }); + expect(screen.queryByLabelText('clear')).not.toBeOnTheScreen(); }); it('renders trailering icon when mode is set to "bar"', async () => { @@ -113,11 +96,12 @@ it('renders trailering icon when mode is set to "bar"', async () => { testID="search-bar" value={''} traileringIcon={'microphone'} + traileringIconAccessibilityLabel="microphone" mode="bar" /> ); - expect(screen.getByTestId('search-bar-trailering-icon')).toBeOnTheScreen(); + expect(screen.getByLabelText('microphone')).toBeOnTheScreen(); }); it('renders trailering icon with press functionality', async () => { @@ -128,12 +112,13 @@ it('renders trailering icon with press functionality', async () => { testID="search-bar" value={''} traileringIcon={'microphone'} + traileringIconAccessibilityLabel="microphone" onTraileringIconPress={onTraileringIconPressMock} mode="bar" /> ); - await userEvent.press(screen.getByTestId('search-bar-trailering-icon')); + await userEvent.press(screen.getByLabelText('microphone')); expect(onTraileringIconPressMock).toHaveBeenCalledTimes(1); }); @@ -143,31 +128,29 @@ it('renders clear icon instead of trailering icon', async () => { testID="search-bar" value={''} traileringIcon={'microphone'} + traileringIconAccessibilityLabel="microphone" mode="bar" /> ); - expect(screen.getByTestId('search-bar-trailering-icon')).toBeOnTheScreen(); + expect(screen.getByLabelText('microphone')).toBeOnTheScreen(); await rerender( ); - expect( - screen.queryByTestId('search-bar-trailering-icon') - ).not.toBeOnTheScreen(); - expect(screen.getByTestId('search-bar-icon-wrapper')).toBeOnTheScreen(); + expect(screen.queryByLabelText('microphone')).not.toBeOnTheScreen(); + expect(screen.getByLabelText('clear')).toBeOnTheScreen(); }); it('renders searchbar in "view" mode', async () => { - await render(); + const tree = (await render()).toJSON(); - expect(screen.getByTestId('search-bar-container')).toHaveStyle({ - borderRadius: 0, - }); + expect(tree).toMatchSnapshot(); }); diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 1e24072540..47134613ba 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -271,134 +271,138 @@ describe('getDisabledSegmentedButtonBorderWidth', () => { describe('should render icon when', () => { it('icon prop is passed', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); it('icon prop is passed along with label, no matter if button is checked', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); it('icon prop is passed along with label, button is checked, showSelectedCheck is false', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-button-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); describe('should not render icon when', () => { it('icon prop is not passed', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.queryByTestId('walking-button-icon')).not.toBeOnTheScreen(); - expect(screen.queryByTestId('driving-button-icon')).not.toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); it('icon prop is passed along with label, button is checked, showSelectedCheck is true', async () => { - await render( - {}} - /> - ); + const tree = ( + await render( + {}} + /> + ) + ).toJSON(); - expect(screen.queryByTestId('walking-button-icon')).not.toBeOnTheScreen(); - expect(screen.getByTestId('walking-button-check-icon')).toBeOnTheScreen(); - expect(screen.getByTestId('driving-button-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); @@ -437,25 +441,26 @@ describe('should have `accessibilityState={ checked: true }` when selected', () it('show selected check icon should be shown', async () => { const onValueChange = jest.fn(); - await render( - - multiSelect - value={['walk', 'transit']} - buttons={[ - { - value: 'walk', - label: 'Walking', - showSelectedCheck: true, - testID: 'walking-check-icon', - }, - { value: 'transit', label: 'Transit' }, - { value: 'drive', label: 'Driving' }, - ]} - onValueChange={onValueChange} - /> - ); + const tree = ( + await render( + + multiSelect + value={['walk', 'transit']} + buttons={[ + { + value: 'walk', + label: 'Walking', + showSelectedCheck: true, + }, + { value: 'transit', label: 'Transit' }, + { value: 'drive', label: 'Driving' }, + ]} + onValueChange={onValueChange} + /> + ) + ).toJSON(); - expect(screen.getByTestId('walking-check-icon')).toBeOnTheScreen(); + expect(tree).toMatchSnapshot(); }); }); @@ -482,10 +487,10 @@ describe('labelStyle is handled', () => { /> ); - expect(screen.getByTestId('walking-button-label')).toHaveStyle({ + expect(screen.getByText('Walking')).toHaveStyle({ fontSize: 10, }); - expect(screen.getByTestId('driving-button-label')).toHaveStyle({ + expect(screen.getByText('Driving')).toHaveStyle({ fontSize: 12, }); }); @@ -505,7 +510,7 @@ describe('labelStyle is handled', () => { /> ); - expect(screen.getByTestId('walking-button-label')).toHaveStyle({ + expect(screen.getByText('Walking')).toHaveStyle({ fontSize: 14, }); }); diff --git a/src/components/__tests__/Tooltip.test.tsx b/src/components/__tests__/Tooltip.test.tsx index 75b4a18cf7..d6b037e10c 100644 --- a/src/components/__tests__/Tooltip.test.tsx +++ b/src/components/__tests__/Tooltip.test.tsx @@ -193,18 +193,19 @@ describe('Tooltip', () => { describe('When it does not overflow', () => { it('centers the tooltip in the middle of the children component', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup(); await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 top: 250, // pageY (200) + height (50) }); @@ -214,18 +215,19 @@ describe('Tooltip', () => { describe('When it overflows to left', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 0, // Tooltip renders starting from children's x coord top: 250, }); @@ -235,18 +237,19 @@ describe('Tooltip', () => { describe('When it overflows to right', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen top: 250, }); @@ -256,18 +259,19 @@ describe('Tooltip', () => { describe('When it overflows to bottom', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageY: 600, height: 50 }); await userEvent.longPress(getTrigger(getByText)); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, }); @@ -388,19 +392,20 @@ describe('Tooltip', () => { describe('When it does not overflow', () => { it('centers the tooltip in the middle of the children component', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup(); await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, // pageX (220) + (width (80) - TOOLTIP_WIDTH (100)) / 2 = 210 top: 250, // pageY (200) + height (50) }); @@ -410,19 +415,20 @@ describe('Tooltip', () => { describe('When it overflows to left', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 0 }); // Component starting at the starting 0 X coord await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 0, // Tooltip renders starting from children's x coord top: 250, }); @@ -432,19 +438,20 @@ describe('Tooltip', () => { describe('When it overflows to right', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageX: 900, width: 150 }); // Component close to the screen limit await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 950, // pageX (900) + width (150) - 100 (TOOLTIP_WIDTH) // Tooltip is placed from right to left without going offscreen top: 250, }); @@ -454,19 +461,20 @@ describe('Tooltip', () => { describe('When it overflows to bottom', () => { it('renders the tooltip with the right placement', async () => { const { - wrapper: { getByText, getByTestId, findByText }, + wrapper: { getByText, findByText }, } = await setup({}, { pageY: 600, height: 50 }); await fireEvent(getTrigger(getByText), 'hoverIn'); await runTimers(500); - await fireEvent(await findByText('some tooltip text'), 'layout', { + const tooltip = await findByText('some tooltip text'); + await fireEvent(tooltip, 'layout', { nativeEvent: { layout: { width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT }, }, }); - expect(getByTestId('tooltip-container')).toHaveStyle({ + expect(tooltip.parent).toHaveStyle({ left: 210, top: 500, // pageY (600) - TOOLTIP_HEIGHT (100) // Tooltip is placed at the top of the component, }); diff --git a/src/components/__tests__/TouchableRipple.test.tsx b/src/components/__tests__/TouchableRipple.test.tsx index f3c4cb1168..663d540bec 100644 --- a/src/components/__tests__/TouchableRipple.test.tsx +++ b/src/components/__tests__/TouchableRipple.test.tsx @@ -48,25 +48,23 @@ describe('TouchableRipple', () => { Platform.OS = 'ios'; it('displays the underlay when pressed', async () => { - await render( + const { toJSON } = await render( Press me! ); - const underlay = screen.getByTestId('touchable-ripple-underlay'); - expect(underlay).toBeOnTheScreen(); + expect(toJSON()).toMatchSnapshot(); }); it('renders custom underlay color', async () => { - await render( + const { toJSON } = await render( Press me! ); - const underlay = screen.getByTestId('touchable-ripple-underlay'); - expect(underlay).toHaveStyle({ backgroundColor: 'purple' }); + expect(toJSON()).toMatchSnapshot(); }); }); }); diff --git a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap index 7ffd6c9752..22b83abaaa 100644 --- a/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap @@ -651,7 +651,7 @@ exports[`allows customizing Route's type via generics 1`] = ` `; -exports[`hides labels in non-shifting bottom navigation 1`] = ` +exports[`applies maxTabBarWidth styling if compact prop is truthy 1`] = ` + + + + Route: 1 + + + + + + + Route: 3 + + + + + + + Route: 4 + + + @@ -806,9 +947,8 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` pointerEvents="none" style={ { - "alignItems": "center", - "height": 80, - "justifyContent": "center", + "paddingBottom": 16, + "paddingTop": 12, } } > @@ -974,72 +1114,12 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` /> - - - - @@ -1047,131 +1127,112 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignItems": "center", "bottom": 0, "left": 0, - "opacity": 0, + "opacity": 1, "position": "absolute", "right": 0, - "top": 4, + "top": 0, } } > - camera - - - - - camera + Route: 0 + > + Route: 0 + @@ -1223,9 +1284,8 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` pointerEvents="none" style={ { - "alignItems": "center", - "height": 80, - "justifyContent": "center", + "paddingBottom": 16, + "paddingTop": 12, } } > @@ -1284,7 +1344,7 @@ exports[`hides labels in non-shifting bottom navigation 1`] = ` ] } > - inbox + camera - inbox + camera + + + + Route: 1 + + + + + Route: 1 + + + - - - - -`; - -exports[`hides labels in shifting bottom navigation 1`] = ` - - - - - - Route: 0 - - - - - - - @@ -1557,23 +1623,6 @@ exports[`hides labels in shifting bottom navigation 1`] = ` } } > - - magnify + inbox - magnify + inbox - - - - @@ -1778,132 +1767,113 @@ exports[`hides labels in shifting bottom navigation 1`] = ` collapsable={false} style={ { - "alignItems": "center", "bottom": 0, "left": 0, "opacity": 0, "position": "absolute", "right": 0, - "top": 4, + "top": 0, } } > - camera + Route: 2 - camera + Route: 2 - - - @@ -1954,9 +1924,8 @@ exports[`hides labels in shifting bottom navigation 1`] = ` pointerEvents="none" style={ { - "alignItems": "center", - "height": 80, - "justifyContent": "center", + "paddingBottom": 16, + "paddingTop": 12, } } > @@ -2015,7 +1984,7 @@ exports[`hides labels in shifting bottom navigation 1`] = ` ] } > - inbox + heart - inbox + heart + + + + Route: 3 + + + + + Route: 3 + + + - - - - -`; - -exports[`renders bottom navigation with getLazy 1`] = ` - - - - - - Route: 0 - - - - - - - Route: 1 - - - - - - - Route: 3 - - - - - + - - Route: 4 - - - - - - - + "paddingBottom": 16, + "paddingTop": 12, + } + } + > + + + + shopping-music + + + + + shopping-music + + + + + + + + + + Route: 4 + + + + + Route: 4 + + + + + + + + + +`; + +exports[`does not apply maxTabBarWidth styling if compact prop is falsy 1`] = ` + + + + + + Route: 0 + + + + + + + Route: 1 + + + + + + + Route: 3 + + + + + + + Route: 4 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + Route: 0 + + + + + Route: 0 + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + Route: 1 + + + + + Route: 1 + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + Route: 2 + + + + + Route: 2 + + + + + + + + + + + heart + + + + + heart + + + + + + + + + + Route: 3 + + + + + Route: 3 + + + + + + + + + + + shopping-music + + + + + shopping-music + + + + + + + + + + Route: 4 + + + + + Route: 4 + + + + + + + + + +`; + +exports[`hides labels in non-shifting bottom navigation 1`] = ` + + + + + + Route: 0 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + + + +`; + +exports[`hides labels in shifting bottom navigation 1`] = ` + + + + + + Route: 0 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + + + +`; + +exports[`renders bottom navigation with getLazy 1`] = ` + + + + + + Route: 0 + + + + + + + Route: 1 + + + + + + + Route: 3 + + + + + + + Route: 4 + + + + + + + + + + + + + + magnify + + + + + magnify + + + + + + + + + + Route: 0 + + + + + Route: 0 + + + + + + + + + + + camera + + + + + camera + + + + + + + + + + Route: 1 + + + + + Route: 1 + + + + + + + + + + + inbox + + + + + inbox + + + + + + + + + + Route: 2 + + + + + Route: 2 + + + + + - - magnify + heart - magnify + heart - Route: 0 + Route: 3 - Route: 0 + Route: 3 @@ -2803,7 +7505,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` ] } > - camera + shopping-music - camera + shopping-music - Route: 1 + Route: 4 - Route: 1 + Route: 4 + + + + +`; + +exports[`renders bottom navigation with scene animation 1`] = ` + + + + + + Route: 0 + + + + + + + + - inbox + magnify - inbox + magnify - - - Route: 2 - - - Route: 2 + Route: 0 @@ -3398,6 +8180,11 @@ exports[`renders bottom navigation with getLazy 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, + "transform": [ + { + "translateY": 7, + }, + ], "width": 32, } } @@ -3443,7 +8230,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` ] } > - heart + camera - heart + camera - Route: 3 - - - - - Route: 3 + Route: 1 @@ -3718,6 +8449,11 @@ exports[`renders bottom navigation with getLazy 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, + "transform": [ + { + "translateY": 7, + }, + ], "width": 32, } } @@ -3763,7 +8499,7 @@ exports[`renders bottom navigation with getLazy 1`] = ` ] } > - shopping-music + inbox - shopping-music + inbox - - - - - - Route: 4 - + /> + + - Route: 4 + Route: 2 - - - - -`; - -exports[`renders bottom navigation with scene animation 1`] = ` - - - - - - Route: 0 - - - - - - - - - magnify + heart - magnify + heart - Route: 0 + Route: 3 @@ -4488,7 +9037,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` ] } > - camera + shopping-music - camera + shopping-music - Route: 1 + Route: 4 + + + + +`; + +exports[`renders custom background color passed to barStyle property 1`] = ` + + + + + + Route: 0 + + + + + + + + - inbox + magnify - inbox + magnify + + + Route: 0 + + - Route: 2 + Route: 0 @@ -4976,11 +9709,6 @@ exports[`renders bottom navigation with scene animation 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], "width": 32, } } @@ -5026,7 +9754,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` ] } > - heart + camera - heart + camera - Route: 3 + Route: 1 + + + + + Route: 1 @@ -5245,11 +10029,6 @@ exports[`renders bottom navigation with scene animation 1`] = ` "marginBottom": 4, "marginHorizontal": 12, "marginTop": 0, - "transform": [ - { - "translateY": 7, - }, - ], "width": 32, } } @@ -5295,7 +10074,7 @@ exports[`renders bottom navigation with scene animation 1`] = ` ] } > - shopping-music + inbox - shopping-music + inbox - Route: 4 + Route: 2 + + + + + Route: 2 diff --git a/src/components/__tests__/__snapshots__/Button.test.tsx.snap b/src/components/__tests__/__snapshots__/Button.test.tsx.snap index 6f255f105a..628087c74b 100644 --- a/src/components/__tests__/__snapshots__/Button.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Button.test.tsx.snap @@ -1,5 +1,2551 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`button icon styles should return correct icon styles for compact contained button 1`] = ` + + + + + + + camera + + + + Compact + contained + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact contained button 2`] = ` + + + + + + + camera + + + + contained + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact contained-tonal button 1`] = ` + + + + + + + camera + + + + Compact + contained-tonal + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact contained-tonal button 2`] = ` + + + + + + + camera + + + + contained-tonal + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact elevated button 1`] = ` + + + + + + + camera + + + + Compact + elevated + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact elevated button 2`] = ` + + + + + + + camera + + + + elevated + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact outlined button 1`] = ` + + + + + + + camera + + + + Compact + outlined + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact outlined button 2`] = ` + + + + + + + camera + + + + outlined + button + + + + +`; + +exports[`button icon styles should return correct icon styles for compact text button 1`] = ` + + + + + + + camera + + + + Compact text button + + + + +`; + +exports[`button icon styles should return correct icon styles for text button 1`] = ` + + + + + + + camera + + + + text button + + + + +`; + exports[`renders button with an accessibility hint 1`] = ` Button with custom testID diff --git a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap index c41b1b39de..9084841e9b 100644 --- a/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/DataTable.test.tsx.snap @@ -71,6 +71,310 @@ exports[`DataTable.Cell renders data table cell 1`] = ` `; +exports[`DataTable.Cell renders data table cell children without wrapping text component 1`] = ` + + + + + + + + + + + + + + + +`; + +exports[`DataTable.Cell renders data table cell with text content 1`] = ` + + + Table cell + + +`; + exports[`DataTable.Cell renders right aligned data table cell 1`] = ` Rows per page diff --git a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap index 23b577a775..8b66657112 100644 --- a/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/FABMenu.test.tsx.snap @@ -197,7 +197,6 @@ exports[`renders FAB.Menu closed 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -374,7 +373,6 @@ exports[`renders FAB.Menu closed 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -976,7 +974,6 @@ exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -1153,7 +1150,6 @@ exports[`renders FAB.Menu not expanded when trigger is not visible 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -1755,7 +1751,6 @@ exports[`renders FAB.Menu open 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -1932,7 +1927,6 @@ exports[`renders FAB.Menu open 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -2534,7 +2528,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-0-text" > Item 1 @@ -2711,7 +2704,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-1-text" > Item 2 @@ -2888,7 +2880,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-2-text" > Item 3 @@ -3065,7 +3056,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-3-text" > Item 4 @@ -3242,7 +3232,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-4-text" > Item 5 @@ -3419,7 +3408,6 @@ exports[`renders FAB.Menu with 6 items 1`] = ` }, ] } - testID="item-5-text" > Item 6 @@ -4022,7 +4010,6 @@ exports[`renders FAB.Menu with center alignment 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -4199,7 +4186,6 @@ exports[`renders FAB.Menu with center alignment 1`] = ` }, ] } - testID="item-1-text" > Set reminder @@ -5638,7 +5624,6 @@ exports[`renders FAB.Menu with start alignment 1`] = ` }, ] } - testID="item-0-text" > Send email @@ -5815,7 +5800,6 @@ exports[`renders FAB.Menu with start alignment 1`] = ` }, ] } - testID="item-1-text" > Set reminder diff --git a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap index c1e777d963..7534e6f77e 100644 --- a/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/IconButton.test.tsx.snap @@ -360,6 +360,129 @@ exports[`renders icon button with color 1`] = ` `; +exports[`renders icon button with custom border radius 1`] = ` + + + + + camera + + + + +`; + exports[`renders icon button with size 1`] = ` `; +exports[`renders icon button with small border radius 1`] = ` + + + + + camera + + + + +`; + exports[`renders icon change animated 1`] = ` +`; + +exports[`renders ListImage with \`video\` variant 1`] = ` + +`; + exports[`renders ListImage with default variant & styles 1`] = ` `; @@ -41,6 +81,5 @@ exports[`renders ListImage with default variant 1`] = ` }, ] } - testID="list-image" /> `; diff --git a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap index c4d5a5d847..e63259b5ef 100644 --- a/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ListItem.test.tsx.snap @@ -1,5 +1,149 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`renders list item with custom content style 1`] = ` + + + + + First Item + + + Item description + + + + +`; + exports[`renders list item with custom description 1`] = ` `; + +exports[`respects anchorPosition bottom 1`] = ` +<> + + + + + + + + Open menu + + + + + + + + + + + + + + + + + + Undo + + + + + + + + + Redo + + + + + + + + + + +`; + +exports[`uses the default anchorPosition of top 1`] = ` +<> + + + + + + + + Open menu + + + + + + + + + + + + + + + + + + Undo + + + + + + + + + Redo + + + + + + + + + + +`; diff --git a/src/components/__tests__/__snapshots__/Modal.test.tsx.snap b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap new file mode 100644 index 0000000000..5b36c6a27a --- /dev/null +++ b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap @@ -0,0 +1,4576 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Modal by default should receive appropriate top and bottom insets 1`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 1`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 2`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 3`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 1`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 2`] = ` + + + + + + + + +`; + +exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 3`] = ` + + + + + + + + +`; + +exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 1`] = ` + + + + + + + + +`; + +exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 2`] = ` + + + + + + + + +`; + +exports[`Modal when open if closed via Android back button invokes onDismiss 1`] = ` + + + + + + + + +`; + +exports[`Modal when open if closed via Android back button invokes onDismiss 2`] = ` + + + + + + + + +`; + +exports[`Modal when open if closed via Android back button invokes onDismiss 3`] = ` + + + + + + + + +`; + +exports[`Modal when open runs the closing animation if visible toggled 1`] = ` + + + + + + + + +`; + +exports[`Modal when open runs the closing animation if visible toggled 2`] = ` + + + + + + + + +`; + +exports[`Modal when open runs the closing animation if visible toggled 3`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 2`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 3`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes again during the open/close animation while opening, back to false (hidden) should keep the modal closed 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 2`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 2`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 1`] = ` + + + + + + + + +`; + +exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 2`] = ` + + + + + + + + +`; diff --git a/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap b/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap index b7fea2ca34..d747e9794b 100644 --- a/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/ProgressBar.test.tsx.snap @@ -129,6 +129,52 @@ exports[`renders indeterminate progress bar 1`] = ` `; +exports[`renders progress bar with custom style of filled part 1`] = ` + + + + + +`; + exports[`renders progress bar with specific progress 1`] = ` `; +exports[`renders searchbar in "view" mode 1`] = ` + + + + + + + magnify + + + + + + + + + + + close + + + + + + + +`; + exports[`renders with placeholder 1`] = ` `; + +exports[`should have \`accessibilityState={ checked: true }\` when selected show selected check icon should be shown 1`] = ` + + + + + + + check + + + + Walking + + + + + + + + + Transit + + + + + + + + + Driving + + + + + +`; + +exports[`should not render icon when icon prop is not passed 1`] = ` + + + + + + + + + + + + + + + + +`; + +exports[`should not render icon when icon prop is passed along with label, button is checked, showSelectedCheck is true 1`] = ` + + + + + + + check + + + + Walking + + + + + + + + + + car + + + + Driving + + + + + +`; + +exports[`should render icon when icon prop is passed 1`] = ` + + + + + + + walk + + + + + + + + + + + + car + + + + + + + +`; + +exports[`should render icon when icon prop is passed along with label, button is checked, showSelectedCheck is false 1`] = ` + + + + + + + walk + + + + Walking + + + + + + + + + + car + + + + Driving + + + + + +`; + +exports[`should render icon when icon prop is passed along with label, no matter if button is checked 1`] = ` + + + + + + + walk + + + + Walking + + + + + + + + + + car + + + + Driving + + + + + +`; diff --git a/src/components/__tests__/__snapshots__/TouchableRipple.test.tsx.snap b/src/components/__tests__/__snapshots__/TouchableRipple.test.tsx.snap new file mode 100644 index 0000000000..00ac206260 --- /dev/null +++ b/src/components/__tests__/__snapshots__/TouchableRipple.test.tsx.snap @@ -0,0 +1,123 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TouchableRipple on iOS displays the underlay when pressed 1`] = ` + + + + Press me! + + +`; + +exports[`TouchableRipple on iOS renders custom underlay color 1`] = ` + + + + Press me! + + +`; From 23b4d3cd177e4c5220031cee318ea74b92ad2e5f Mon Sep 17 00:00:00 2001 From: Bao Nguyen <39545125+giaBaoJS@users.noreply.github.com> Date: Fri, 11 Sep 2026 22:06:17 +0700 Subject: [PATCH 72/94] fix(portal): replay queued portal operations in order (#5048) `PortalHost` queues portal operations that arrive before its `PortalManager` ref is attached, which is every portal that mounts in the first commit. `componentDidMount` drained that queue with `pop()`, replaying the operations LIFO, so portals mounted in the same commit were stacked in reverse source order. Drain with `shift()` instead. --- src/components/Portal/PortalHost.tsx | 4 +- src/components/__tests__/Portal.test.tsx | 48 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/components/Portal/PortalHost.tsx b/src/components/Portal/PortalHost.tsx index d8193007b8..ecc20b8a72 100644 --- a/src/components/Portal/PortalHost.tsx +++ b/src/components/Portal/PortalHost.tsx @@ -51,7 +51,9 @@ export default class PortalHost extends React.Component { const queue = this.queue; while (queue.length && manager) { - const action = queue.pop(); + // Replay in the order the operations were recorded, otherwise portals + // that mounted in the same commit end up stacked in reverse. + const action = queue.shift(); if (action) { switch (action.type) { case 'mount': diff --git a/src/components/__tests__/Portal.test.tsx b/src/components/__tests__/Portal.test.tsx index 855d74b6ab..6865d4d159 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -5,6 +5,8 @@ import { expect, it, jest } from '@jest/globals'; import { LocaleProvider, useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import Dialog from '../Dialog/Dialog'; +import Modal from '../Modal'; import Portal from '../Portal/Portal'; jest.useRealTimers(); @@ -57,3 +59,49 @@ it('passes local theme overrides and locale to portal content and updates them', expect(screen.getByText('3 ltr')).toBeOnTheScreen(); expect(screen.queryByText('2 rtl')).not.toBeOnTheScreen(); }); + +it('renders portals in source order when mounted in the same commit', async () => { + await render( + + + first + + + second + + + third + + + ); + + const portals = await screen.findAllByTestId('portal-content'); + + expect(portals).toHaveLength(3); + expect(portals[0]).toHaveTextContent('first'); + expect(portals[1]).toHaveTextContent('second'); + expect(portals[2]).toHaveTextContent('third'); +}); + +it('stacks components mounted in the same commit in source order', async () => { + await render( + + + {}}> + modal + + + + {}}> + dialog + + + + ); + + const layers = await screen.findAllByTestId('layer'); + + expect(layers).toHaveLength(2); + expect(layers[0]).toHaveTextContent('modal'); + expect(layers[1]).toHaveTextContent('dialog'); +}); From 31d8c58ecca01e74677816958fa96059952012cd Mon Sep 17 00:00:00 2001 From: k0ndee Date: Fri, 11 Sep 2026 17:07:23 +0200 Subject: [PATCH 73/94] fix: rename misspelled props and various typos (#5117) --- docs/6.x/docs/guides/fonts.md | 2 +- docs/6.x/docs/guides/migration.md | 22 ++++++++++ docs/src/components/ScreenshotTabs.tsx | 6 +-- docs/src/data/themeColors.ts | 4 +- example/src/Examples/SearchbarExample.tsx | 34 ++++++++-------- src/components/Button/Button.tsx | 2 +- src/components/Checkbox/CheckboxItem.tsx | 2 +- src/components/Chip/Chip.tsx | 2 +- src/components/Drawer/DrawerItem.tsx | 2 +- src/components/List/ListAccordion.tsx | 2 +- src/components/Menu/MenuItem.tsx | 4 +- .../RadioButton/RadioButtonItem.tsx | 2 +- src/components/Searchbar.tsx | 40 +++++++++---------- .../SegmentedButtons/SegmentedButtonItem.tsx | 2 +- src/components/Surface.tsx | 4 +- .../TouchableRipple/TouchableRipple.tsx | 2 +- src/components/Typography/Text.tsx | 2 +- .../__tests__/Appbar/Appbar.test.tsx | 2 +- src/components/__tests__/ListUtils.test.tsx | 4 +- src/components/__tests__/Searchbar.test.tsx | 28 ++++++------- .../__tests__/SegmentedButton.test.tsx | 2 +- 21 files changed, 96 insertions(+), 74 deletions(-) diff --git a/docs/6.x/docs/guides/fonts.md b/docs/6.x/docs/guides/fonts.md index a72f42c83e..139c5a9635 100644 --- a/docs/6.x/docs/guides/fonts.md +++ b/docs/6.x/docs/guides/fonts.md @@ -57,7 +57,7 @@ Older Material Design 2 platform-split font configuration (`configureFonts` with In the latest version fonts in theme are structured based on the `variant` keys e.g. `displayLarge` or `bodyMedium` which are then used in `Text`'s component throughout the whole library. :::info -The default `fontFamily` is different per particular platfrom: +The default `fontFamily` is different per particular platform: ```js Platform.select({ diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index 35a8f74830..c4874a10a9 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -188,6 +188,28 @@ e.g.: - The default elevation changed from level `1` to level `3`. - The `style` prop no longer configures the background color or border radius. You can override `theme.colors.surfaceContainerHigh` and `theme.shapes.corner.extraLarge` using the `theme` prop instead. +### Searchbar + +The misspelled `traileringIcon` props have been renamed: + +- **`traileringIcon`** → **`trailingIcon`** +- **`traileringIconColor`** → **`trailingIconColor`** +- **`traileringIconAccessibilityLabel`** → **`trailingIconAccessibilityLabel`** +- **`onTraileringIconPress`** → **`onTrailingIconPress`** + +```diff + +``` + ### TextInput The Paper 6.x `TextInput` is a complete rewrite with a new API. Import the component the same way, but note that the props and behavior have changed significantly. diff --git a/docs/src/components/ScreenshotTabs.tsx b/docs/src/components/ScreenshotTabs.tsx index 6a524c3d21..ee45872920 100644 --- a/docs/src/components/ScreenshotTabs.tsx +++ b/docs/src/components/ScreenshotTabs.tsx @@ -16,17 +16,17 @@ const getClassName = (value: string) => : `tabScreenshot${value.includes('full-width') ? 'full-width' : ''}`; const ScreenshotTabs = ({ screenshotData }: ScreenshotTabsProps) => { - const renderScreenhot = (src: string): ReactNode => ( + const renderScreenshot = (src: string): ReactNode => ( ); if (typeof screenshotData === 'string') { - return renderScreenhot(screenshotData); + return renderScreenshot(screenshotData); } const screenshots = Object.entries(screenshotData).map(([key, value]) => ( - {typeof value === 'string' ? renderScreenhot(value) : null} + {typeof value === 'string' ? renderScreenshot(value) : null} )); diff --git a/docs/src/data/themeColors.ts b/docs/src/data/themeColors.ts index 20f16962f7..118114da27 100644 --- a/docs/src/data/themeColors.ts +++ b/docs/src/data/themeColors.ts @@ -164,7 +164,7 @@ export const themeColors = { 'textColor/iconColor': 'theme.colors.onTertiaryContainer', }, surface: { - backgroundColor: 'theme.colors.elevarion.level3', + backgroundColor: 'theme.colors.elevation.level3', 'textColor/iconColor': 'theme.colors.primary', }, }, @@ -186,7 +186,7 @@ export const themeColors = { 'textColor/iconColor': 'theme.colors.onTertiaryContainer', }, surface: { - backgroundColor: 'theme.colors.elevarion.level3', + backgroundColor: 'theme.colors.elevation.level3', 'textColor/iconColor': 'theme.colors.primary', }, }, diff --git a/example/src/Examples/SearchbarExample.tsx b/example/src/Examples/SearchbarExample.tsx index 739002156b..1a3e98bb8f 100644 --- a/example/src/Examples/SearchbarExample.tsx +++ b/example/src/Examples/SearchbarExample.tsx @@ -19,8 +19,8 @@ const SearchExample = () => { const [isVisible, setIsVisible] = React.useState(false); const [searchQueries, setSearchQuery] = React.useState({ searchBarMode: '', - traileringIcon: '', - traileringIconWithRightItem: '', + trailingIcon: '', + trailingIconWithRightItem: '', rightItem: '', loadingBarMode: '', searchViewMode: '', @@ -47,36 +47,36 @@ const SearchExample = () => { mode="bar" /> - setSearchQuery({ ...searchQueries, traileringIcon: query }) + setSearchQuery({ ...searchQueries, trailingIcon: query }) } - value={searchQueries.traileringIcon} - traileringIcon={'microphone'} - traileringIconColor={ + value={searchQueries.trailingIcon} + trailingIcon={'microphone'} + trailingIconColor={ isVisible ? Palette.error40 : colors.onSurfaceVariant } - traileringIconAccessibilityLabel={'microphone button'} - onTraileringIconPress={() => setIsVisible(true)} + trailingIconAccessibilityLabel={'microphone button'} + onTrailingIconPress={() => setIsVisible(true)} style={styles.searchbar} mode="bar" /> setSearchQuery({ ...searchQueries, - traileringIconWithRightItem: query, + trailingIconWithRightItem: query, }) } - value={searchQueries.traileringIconWithRightItem} - traileringIcon={'microphone'} - traileringIconColor={ + value={searchQueries.trailingIconWithRightItem} + trailingIcon={'microphone'} + trailingIconColor={ isVisible ? Palette.error40 : colors.onSurfaceVariant } - traileringIconAccessibilityLabel={'microphone button'} - onTraileringIconPress={() => setIsVisible(true)} + trailingIconAccessibilityLabel={'microphone button'} + onTrailingIconPress={() => setIsVisible(true)} right={(props) => ( { style={styles.searchbar} mode="bar" loading - traileringIcon={'microphone'} + trailingIcon={'microphone'} /> diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 82b327dc74..6f07113572 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -74,7 +74,7 @@ export type Props = Omit & { */ uppercase?: boolean; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/Checkbox/CheckboxItem.tsx b/src/components/Checkbox/CheckboxItem.tsx index 4cffd781fa..7d0f8ee64a 100644 --- a/src/components/Checkbox/CheckboxItem.tsx +++ b/src/components/Checkbox/CheckboxItem.tsx @@ -37,7 +37,7 @@ export type Props = { */ onLongPress?: (e: GestureResponderEvent) => void; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/Chip/Chip.tsx b/src/components/Chip/Chip.tsx index 5a015fcf8d..4507561a48 100644 --- a/src/components/Chip/Chip.tsx +++ b/src/components/Chip/Chip.tsx @@ -75,7 +75,7 @@ export type Props = Omit & { */ disabled?: boolean; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/Drawer/DrawerItem.tsx b/src/components/Drawer/DrawerItem.tsx index dda753186b..50086bbe71 100644 --- a/src/components/Drawer/DrawerItem.tsx +++ b/src/components/Drawer/DrawerItem.tsx @@ -39,7 +39,7 @@ export type Props = ViewProps & { */ onPress?: (e: GestureResponderEvent) => void; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 97c930c52e..7c552fce36 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -67,7 +67,7 @@ export type Props = { */ theme?: ThemeProp; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/Menu/MenuItem.tsx b/src/components/Menu/MenuItem.tsx index 798abd2c6c..c76bee0557 100644 --- a/src/components/Menu/MenuItem.tsx +++ b/src/components/Menu/MenuItem.tsx @@ -46,11 +46,11 @@ export type Props = { /** * @supported Available in v5.x with theme version 3 * - * Sets min height with densed layout. + * Sets min height with dense layout. */ dense?: boolean; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/RadioButton/RadioButtonItem.tsx b/src/components/RadioButton/RadioButtonItem.tsx index 71f7aaec23..b987f7f0e7 100644 --- a/src/components/RadioButton/RadioButtonItem.tsx +++ b/src/components/RadioButton/RadioButtonItem.tsx @@ -34,7 +34,7 @@ export type Props = { */ disabled?: boolean; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/Searchbar.tsx b/src/components/Searchbar.tsx index f1580078f4..02855eef0f 100644 --- a/src/components/Searchbar.tsx +++ b/src/components/Searchbar.tsx @@ -82,25 +82,25 @@ export type Props = Omit & { clearTestID?: string; /** * @supported Available in v5.x with theme version 3 - * Icon name for the right trailering icon button. + * Icon name for the right trailing icon button. * Works only when `mode` is set to "bar". It won't be displayed if `loading` is set to `true`. */ - traileringIcon?: IconSource; + trailingIcon?: IconSource; /** * @supported Available in v5.x with theme version 3 - * Custom color for the right trailering icon, default will be derived from theme + * Custom color for the right trailing icon, default will be derived from theme */ - traileringIconColor?: ColorValue; + trailingIconColor?: ColorValue; /** - * Callback to execute on the right trailering icon button press. + * Callback to execute on the right trailing icon button press. */ - onTraileringIconPress?: (e: GestureResponderEvent) => void; + onTrailingIconPress?: (e: GestureResponderEvent) => void; /** - * Accessibility label for the right trailering icon button. This is read by the screen reader when the user taps the button. + * Accessibility label for the right trailing icon button. This is read by the screen reader when the user taps the button. */ - traileringIconAccessibilityLabel?: string; + trailingIconAccessibilityLabel?: string; /** - * testID for the right trailering icon button. + * testID for the right trailing icon button. */ trailingTestID?: string; /** @@ -183,11 +183,11 @@ const Searchbar = ({ clearAccessibilityLabel = 'clear', clearTestID, onClearIconPress, - traileringIcon, - traileringIconColor, - traileringIconAccessibilityLabel, + trailingIcon, + trailingIconColor, + trailingIconAccessibilityLabel, trailingTestID, - onTraileringIconPress, + onTrailingIconPress, right, mode = 'bar', showDivider = true, @@ -242,8 +242,8 @@ const Searchbar = ({ const isBarMode = mode === 'bar'; const inputTextAlign = direction === 'rtl' ? 'right' : 'left'; - const shouldRenderTraileringIcon = - isBarMode && traileringIcon && !loading && (!value || right !== undefined); + const shouldRenderTrailingIcon = + isBarMode && trailingIcon && !loading && (!value || right !== undefined); return ( )} - {shouldRenderTraileringIcon ? ( + {shouldRenderTrailingIcon ? ( ) : null} diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index 33886d7ec1..3b9962a457 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -59,7 +59,7 @@ export type Props = { */ disabled?: boolean; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: PressableAndroidRippleConfig; diff --git a/src/components/Surface.tsx b/src/components/Surface.tsx index 48df1a4d4a..a91fd938d5 100644 --- a/src/components/Surface.tsx +++ b/src/components/Surface.tsx @@ -171,7 +171,7 @@ export type Props = Omit & const Surface = ({ elevation = 1, children, - theme: overridenTheme, + theme: overriddenTheme, style, backgroundColor: customBackgroundColor, borderRadius, @@ -194,7 +194,7 @@ const Surface = ({ ref, ...rest }: Props) => { - const theme = useInternalTheme(overridenTheme); + const theme = useInternalTheme(overriddenTheme); const { colors } = theme; diff --git a/src/components/TouchableRipple/TouchableRipple.tsx b/src/components/TouchableRipple/TouchableRipple.tsx index 128e2c017e..3b98d252c7 100644 --- a/src/components/TouchableRipple/TouchableRipple.tsx +++ b/src/components/TouchableRipple/TouchableRipple.tsx @@ -24,7 +24,7 @@ export type Props = PressableProps & { */ borderless?: boolean; /** - * Type of background drawabale to display the feedback (Android). + * Type of background drawable to display the feedback (Android). * https://reactnative.dev/docs/pressable#rippleconfig */ background?: Object; diff --git a/src/components/Typography/Text.tsx b/src/components/Typography/Text.tsx index 73572d1601..ced8a2e03b 100644 --- a/src/components/Typography/Text.tsx +++ b/src/components/Typography/Text.tsx @@ -127,7 +127,7 @@ const Text = ({ textStyle = [style, font]; } - // Case two: Nested `Text` has specified `styles` which intefere + // Case two: Nested `Text` has specified `styles` which interfere // with font properties, from the parent's `variant`. For example: // // Nested diff --git a/src/components/__tests__/Appbar/Appbar.test.tsx b/src/components/__tests__/Appbar/Appbar.test.tsx index 95aec82ddd..0d6abb6029 100644 --- a/src/components/__tests__/Appbar/Appbar.test.tsx +++ b/src/components/__tests__/Appbar/Appbar.test.tsx @@ -89,7 +89,7 @@ describe('renderAppbarContent', () => { expect(result).toHaveLength(3); }); - it('should render only children types specifed in renderOnly', () => { + it('should render only children types specified in renderOnly', () => { const result = renderAppbarContent({ children, isDark: false, diff --git a/src/components/__tests__/ListUtils.test.tsx b/src/components/__tests__/ListUtils.test.tsx index f89e57ea98..b733f832d0 100644 --- a/src/components/__tests__/ListUtils.test.tsx +++ b/src/components/__tests__/ListUtils.test.tsx @@ -29,7 +29,7 @@ it('returns styles for left item without description', () => { expect(style).toStrictEqual({ ...styles.leftItemV3, marginVertical: 0 }); }); -it('returns styles for left item w/ desctiption', () => { +it('returns styles for left item w/ description', () => { const style = getLeftStyles(true, description); expect(style).toStrictEqual({ ...styles.leftItemV3, @@ -46,7 +46,7 @@ it('returns styles for right item without description', () => { expect(style).toStrictEqual({ ...styles.rightItemV3, marginVertical: 0 }); }); -it('returns styles for right item w/ desctiption', () => { +it('returns styles for right item w/ description', () => { const style = getRightStyles(true, description); expect(style).toStrictEqual({ ...styles.rightItemV3, diff --git a/src/components/__tests__/Searchbar.test.tsx b/src/components/__tests__/Searchbar.test.tsx index 4f4f7ed4c3..ac39f78662 100644 --- a/src/components/__tests__/Searchbar.test.tsx +++ b/src/components/__tests__/Searchbar.test.tsx @@ -90,13 +90,13 @@ it('hides the clear icon when a custom right element is rendered', async () => { expect(screen.queryByLabelText('clear')).not.toBeOnTheScreen(); }); -it('renders trailering icon when mode is set to "bar"', async () => { +it('renders trailing icon when mode is set to "bar"', async () => { await render( ); @@ -104,31 +104,31 @@ it('renders trailering icon when mode is set to "bar"', async () => { expect(screen.getByLabelText('microphone')).toBeOnTheScreen(); }); -it('renders trailering icon with press functionality', async () => { - const onTraileringIconPressMock = jest.fn(); +it('renders trailing icon with press functionality', async () => { + const onTrailingIconPressMock = jest.fn(); await render( ); await userEvent.press(screen.getByLabelText('microphone')); - expect(onTraileringIconPressMock).toHaveBeenCalledTimes(1); + expect(onTrailingIconPressMock).toHaveBeenCalledTimes(1); }); -it('renders clear icon instead of trailering icon', async () => { +it('renders clear icon instead of trailing icon', async () => { const { rerender } = await render( ); @@ -139,8 +139,8 @@ it('renders clear icon instead of trailering icon', async () => { ); diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 47134613ba..807d5eb502 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -150,7 +150,7 @@ describe('getSegmentedButtonColors', () => { ).toMatchObject({ backgroundColor: LightTheme.colors.secondaryContainer }); }); - it('should return correct background color when uncheked (V3 & V2)', () => { + it('should return correct background color when unchecked (V3 & V2)', () => { expect( getSegmentedButtonColors({ theme: LightTheme, From 7d2139be3b3a30117c7459bfc4ec3a50903c652e Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Tue, 15 Sep 2026 14:40:46 +0200 Subject: [PATCH 74/94] chore: update pull request template and add automated checks --- .github/pull_request_template.md | 4 +- .github/workflows/pull-request-checks.yml | 97 +++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pull-request-checks.yml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 81aae4c0a2..4d825afb12 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,7 +3,7 @@ ### Motivation - + ### Related issue @@ -15,4 +15,4 @@ ### Test plan - + diff --git a/.github/workflows/pull-request-checks.yml b/.github/workflows/pull-request-checks.yml new file mode 100644 index 0000000000..ced505b5a4 --- /dev/null +++ b/.github/workflows/pull-request-checks.yml @@ -0,0 +1,97 @@ +name: Pull Request Checks + +on: + pull_request_target: + types: [opened, edited, reopened] + +permissions: + issues: write + pull-requests: read + +concurrency: + group: pull-request-checks-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + check: + name: Check description and visual evidence + runs-on: ubuntu-latest + steps: + - name: Check pull request details + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const MAX_PR_BODY_LENGTH = 1000; + const MARKER = ''; + + const { owner, repo } = context.repo; + + const issueNumber = context.payload.pull_request.number; + const body = context.payload.pull_request.body || ''; + + const problems = []; + + if (Array.from(body).length > MAX_PR_BODY_LENGTH) { + problems.push( + `- The description is too long. Please keep it under ${MAX_PR_BODY_LENGTH} characters.` + ); + } + + if ( + !/(?:https?:\/\/[^\s)"']+\.(?:png|jpe?g|gif|webp|avif|mp4|mov|webm|m4v)(?:[?#][^\s)"']*)?|https?:\/\/github\.com\/user-attachments\/assets\/[^\s)"']+)/i.test( + body + ) + ) { + problems.push( + '- Screenshot or video evidence is missing. Make sure to include one if it affects the UI.' + ); + } + + const existingComment = ( + await github.paginate(github.rest.issues.listComments, { + owner, + repo, + issue_number: issueNumber, + per_page: 100, + }) + ).find( + (comment) => + comment.user?.login === 'github-actions[bot]' && + comment.body?.includes(MARKER) + ); + + if (problems.length === 0) { + if (existingComment) { + await github.rest.issues.deleteComment({ + owner, + repo, + comment_id: existingComment.id, + }); + } + + return; + } + + const commentBody = [ + MARKER, + 'Found potential problems with the pull request:', + '', + problems.join('\n'), + ].join('\n'); + + if (existingComment) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: existingComment.id, + body: commentBody, + }); + } else { + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issueNumber, + body: commentBody, + }); + } From 6f3335739c644419c23466af55e9f78c909c712d Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Tue, 15 Sep 2026 22:45:37 +0200 Subject: [PATCH 75/94] docs: remove leftover reference to theme: mode --- docs/6.x/docs/guides/theming.mdx | 9 +-------- src/components/Appbar/Appbar.tsx | 3 --- src/components/BottomNavigation/BottomNavigation.tsx | 3 --- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/docs/6.x/docs/guides/theming.mdx b/docs/6.x/docs/guides/theming.mdx index 58836dc167..c7eed1703d 100644 --- a/docs/6.x/docs/guides/theming.mdx +++ b/docs/6.x/docs/guides/theming.mdx @@ -66,7 +66,6 @@ A theme usually contains the following properties: - `dark` (`boolean`): whether this is a dark theme or light theme. - `version`: Material You (MD3); kept for compatibility and normalized to `3` by `PaperProvider` -- `mode` (`'adaptive' | 'exact'`): color mode for dark theme (See [Dark Theme](#dark-theme)). - `roundness` (`number`): roundness of common elements, such as buttons. - `colors` (`object`): various colors used throughout different elements. @@ -503,15 +502,9 @@ Now you can use your `FancyButton` component everywhere instead of using `Button ## Dark Theme Since 3.0 we adapt dark theme to follow [Material design guidelines](https://material.io/design/color/dark-theme.html).
    -In contrast to light theme, dark theme by default uses `surface` colour instead of `primary` on large components like `AppBar` or `BottomNavigation`.
    +In contrast to light theme, dark theme by default uses `surface` colour instead of `primary` on large components like `Appbar` or `BottomNavigation`.
    The dark theme adds a white overlay with opacity depending on elevation of surfaces. It uses it for the better accentuation of surface elevation. Using only shadow is highly imperceptible on dark surfaces. -We are aware that users often use dark theme in their own ways and may not want to use the default dark theme features from the guidelines.
    -That's why if you are using dark theme you can switch between two dark theme `mode`s: - -- `exact` where everything is like it was before. `Appbar` and `BottomNavigation` will still use primary colour by default.
    -- `adaptive` where we follow [Material design guidelines](https://material.io/design/color/dark-theme.html), the surface will use white overlay with opacity to show elevation, `Appbar` and `BottomNavigation` will use surface colour as a background. - If you don't use a custom theme, Paper will automatically change between the default theme and the default dark theme, depending on device settings. Otherwise, your custom theme will need to handle it manually, using React Native's [Appearance API](https://reactnative.dev/docs/appearance). diff --git a/src/components/Appbar/Appbar.tsx b/src/components/Appbar/Appbar.tsx index a324a61d5c..28ea39426a 100644 --- a/src/components/Appbar/Appbar.tsx +++ b/src/components/Appbar/Appbar.tsx @@ -64,9 +64,6 @@ export type Props = Omit, 'style'> & { * The top bar usually contains the screen title, controls such as navigation buttons, menu button etc. * The bottom bar usually provides access to a drawer and up to four actions. * - * By default Appbar uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. - * See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more informations - * * ## Usage * ### Top bar * ```js diff --git a/src/components/BottomNavigation/BottomNavigation.tsx b/src/components/BottomNavigation/BottomNavigation.tsx index adecee59a3..62a8d07ea2 100644 --- a/src/components/BottomNavigation/BottomNavigation.tsx +++ b/src/components/BottomNavigation/BottomNavigation.tsx @@ -268,9 +268,6 @@ const SceneComponent = React.memo(({ component, ...rest }: any) => * BottomNavigation provides quick navigation between top-level views of an app with a bottom navigation bar. * It is primarily designed for use on mobile. If you want to use the navigation bar only see [`BottomNavigation.Bar`](BottomNavigationBar). * - * By default BottomNavigation uses primary color as a background, in dark theme with `adaptive` mode it will use surface colour instead. - * See [Dark Theme](https://callstack.github.io/react-native-paper/docs/guides/theming#dark-theme) for more information. - * * ## Usage * ```js * import * as React from 'react'; From e98fce0565bc4837786d4a062d476fbbfc0cbbd1 Mon Sep 17 00:00:00 2001 From: Oleksandr Date: Tue, 15 Sep 2026 22:48:19 +0200 Subject: [PATCH 76/94] refactor: remove ToggleButton (#5095) --- docs/6.x/docs/guides/migration.md | 29 +- docs/component-docs.config.ts | 5 - docs/src/data/screenshots.ts | 3 - example/src/ExampleList.tsx | 2 - example/src/Examples/ToggleButtonExample.tsx | 123 ------ example/src/Examples/TooltipExample.tsx | 38 +- src/components/ToggleButton/ToggleButton.tsx | 162 -------- .../ToggleButton/ToggleButtonGroup.tsx | 74 ---- .../ToggleButton/ToggleButtonRow.tsx | 108 ----- src/components/ToggleButton/index.ts | 16 - src/components/ToggleButton/utils.ts | 14 - .../__tests__/ToggleButton.test.tsx | 54 --- .../__snapshots__/ToggleButton.test.tsx.snap | 385 ------------------ src/index.tsx | 4 - 14 files changed, 53 insertions(+), 964 deletions(-) delete mode 100644 example/src/Examples/ToggleButtonExample.tsx delete mode 100644 src/components/ToggleButton/ToggleButton.tsx delete mode 100644 src/components/ToggleButton/ToggleButtonGroup.tsx delete mode 100644 src/components/ToggleButton/ToggleButtonRow.tsx delete mode 100644 src/components/ToggleButton/index.ts delete mode 100644 src/components/ToggleButton/utils.ts delete mode 100644 src/components/__tests__/ToggleButton.test.tsx delete mode 100644 src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index c4874a10a9..e51de006aa 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -25,7 +25,6 @@ The following props now accept animated styles returned from `useAnimatedStyle`. - `Searchbar`: `style` - `Snackbar`: `style` - `Surface`: `style` -- `ToggleButton`: `style` So you can use Reanimated's `useSharedValue` and `useAnimatedStyle` to animate these components instead of the React Native `Animated` API. @@ -330,3 +329,31 @@ const theme = { style={{ fontSize: 16, color: '#1C1B1F' }} /> ``` + +### ToggleButton + +`ToggleButton`, `ToggleButton.Group` and `ToggleButton.Row` were removed. For an +icon-only toggle, use `IconButton` with the `selected` prop. For a set of +mutually exclusive options, use `SegmentedButtons`. + +```tsx +// Before (v5) + + + + + +// After (v6) +<> + setValue('bold')} + /> + setValue('italic')} + /> + +``` diff --git a/docs/component-docs.config.ts b/docs/component-docs.config.ts index 4196e8e7bd..2cf106849c 100644 --- a/docs/component-docs.config.ts +++ b/docs/component-docs.config.ts @@ -149,11 +149,6 @@ const pages = { props: 'TextInputAccessoryProps', }, }, - ToggleButton: { - ToggleButton: 'ToggleButton/ToggleButton', - ToggleButtonGroup: 'ToggleButton/ToggleButtonGroup', - ToggleButtonRow: 'ToggleButton/ToggleButtonRow', - }, Tooltip: { Tooltip: 'Tooltip/Tooltip', }, diff --git a/docs/src/data/screenshots.ts b/docs/src/data/screenshots.ts index 92bf8f2788..b7c59d424d 100644 --- a/docs/src/data/screenshots.ts +++ b/docs/src/data/screenshots.ts @@ -149,9 +149,6 @@ export const screenshots = { filled: 'screenshots/text-input-filled.png', outlined: 'screenshots/text-input-outlined.png', }, - ToggleButton: 'screenshots/toggle-button.png', - 'ToggleButton.Group': 'screenshots/toggle-button-group.gif', - 'ToggleButton.Row': 'screenshots/toggle-button-row.gif', Tooltip: 'screenshots/tooltip.png', TouchableRipple: 'screenshots/touchable-ripple.gif', }; diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index 8a2646d798..2af1eed385 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -44,7 +44,6 @@ import TextExample from './Examples/TextExample'; import TextInputExample from './Examples/TextInputExample'; import ThemeExample from './Examples/ThemeExample'; import ThemingWithReactNavigation from './Examples/ThemingWithReactNavigation'; -import ToggleButtonExample from './Examples/ToggleButtonExample'; import TooltipExample from './Examples/TooltipExample'; import TouchableRippleExample from './Examples/TouchableRippleExample'; @@ -83,7 +82,6 @@ export const mainExamples = { Switch: SwitchExample, Text: TextExample, TextInput: TextInputExample, - ToggleButton: ToggleButtonExample, TooltipExample, TouchableRipple: TouchableRippleExample, Theme: ThemeExample, diff --git a/example/src/Examples/ToggleButtonExample.tsx b/example/src/Examples/ToggleButtonExample.tsx deleted file mode 100644 index f07caaaffb..0000000000 --- a/example/src/Examples/ToggleButtonExample.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import * as React from 'react'; -import { View, StyleSheet, ImageBackground } from 'react-native'; - -import { ToggleButton, List } from 'react-native-paper'; - -import ScreenWrapper from '../ScreenWrapper'; - -type StatusState = 'checked' | 'unchecked'; -type Fruits = 'watermelon' | 'strawberries'; - -const fonts = { - noFormat: 'no-format', - italic: 'italic', - bold: 'bold', - underline: 'underlined', - colorText: 'format-color', -} as const; - -type Font = (typeof fonts)[keyof typeof fonts]; - -const ToggleButtonExample = () => { - const [first, setFirst] = React.useState('bold'); - const [fruit, setFruit] = React.useState('watermelon'); - const [status, setStatus] = React.useState('checked'); - const [font, setFont] = React.useState(fonts.noFormat); - - const handleFruit = (value: Fruits) => setFruit(value); - - return ( - - - - - setStatus(status === 'checked' ? 'unchecked' : 'checked') - } - /> - - - - setFirst(value)} - style={styles.padding} - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -ToggleButtonExample.title = 'Toggle Button'; - -const styles = StyleSheet.create({ - padding: { - paddingHorizontal: 16, - }, - row: { - flexDirection: 'row', - }, - customImage: { - width: 143, - height: 153, - margin: 2, - }, - customButton: { - position: 'absolute', - right: 0, - }, -}); - -export default ToggleButtonExample; diff --git a/example/src/Examples/TooltipExample.tsx b/example/src/Examples/TooltipExample.tsx index 325e0f0113..4ecbdfe544 100644 --- a/example/src/Examples/TooltipExample.tsx +++ b/example/src/Examples/TooltipExample.tsx @@ -10,7 +10,6 @@ import { FAB, IconButton, List, - ToggleButton, Tooltip, Card, } from 'react-native-paper'; @@ -39,7 +38,7 @@ const formOfTransport = [ const TooltipExample = () => { const navigation = useNavigation('TooltipExample'); - const [textAlign, setTextAlign] = React.useState('bold'); + const [textAlign, setTextAlign] = React.useState('left'); React.useLayoutEffect(() => { navigation.setOptions({ header: () => ( @@ -91,22 +90,34 @@ const TooltipExample = () => { ))}
    - - + + - + setTextAlign('left')} + /> - + setTextAlign('center')} + /> - + setTextAlign('right')} + /> - +
    @@ -178,7 +189,8 @@ const styles = StyleSheet.create({ cardContainer: { margin: 16, }, - toggleButtonRow: { + toggleRow: { + flexDirection: 'row', paddingHorizontal: 16, }, iconButtonContainer: { diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx deleted file mode 100644 index 72f6dc3161..0000000000 --- a/src/components/ToggleButton/ToggleButton.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import * as React from 'react'; -import { StyleSheet, View } from 'react-native'; -import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; - -import type { AnimatedStyle } from 'react-native-reanimated'; - -import { ToggleButtonGroupContext } from './ToggleButtonGroup'; -import { getToggleButtonColor } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../theme/types'; -import type { IconSource } from '../Icon'; -import IconButton from '../IconButton/IconButton'; - -export type Props = { - /** - * Icon to display for the `ToggleButton`. - */ - icon: IconSource; - /** - * Size of the icon. - */ - size?: number; - /** - * Custom text color for button. - */ - iconColor?: string; - /** - * Whether the button is disabled. - */ - disabled?: boolean; - /** - * Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button. - */ - 'aria-label'?: string; - /** - * Function to execute on press. - */ - onPress?: (value?: GestureResponderEvent | string) => void; - /** - * Value of button. - */ - value?: string; - /** - * Status of button. - */ - status?: 'checked' | 'unchecked'; - style?: StyleProp>; - /** - * @optional - */ - theme?: ThemeProp; - ref?: React.Ref; - /** - * testID to be used on tests. - */ - testID?: string; -}; - -/** - * Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons, - * a group should share a common container. - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { ToggleButton } from 'react-native-paper'; - * - * const ToggleButtonExample = () => { - * const [status, setStatus] = React.useState('checked'); - * - * const onButtonToggle = value => { - * setStatus(status === 'checked' ? 'unchecked' : 'checked'); - * }; - * - * return ( - * - * ); - * }; - * - * export default ToggleButtonExample; - * - * ``` - */ -const ToggleButton = ({ - icon, - size, - theme: themeOverrides, - 'aria-label': ariaLabel, - disabled, - style, - value, - status, - onPress, - ref, - ...rest -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const borderRadius = theme.shapes.corner.extraSmall; - - return ( - - {(context: { value: string | null; onValueChange: Function } | null) => { - const checked: boolean | null = - (context && context.value === value) || status === 'checked'; - - const backgroundColor = getToggleButtonColor({ theme, checked }); - const borderColor = theme.colors.outline; - - return ( - { - if (onPress) { - onPress(e); - } - - if (context) { - context.onValueChange(!checked ? value : null); - } - }} - size={size} - aria-label={ariaLabel} - aria-disabled={disabled} - aria-selected={checked} - disabled={disabled} - style={[ - styles.content, - { - backgroundColor, - borderRadius, - borderColor, - }, - style, - ]} - ref={ref} - theme={theme} - {...rest} - /> - ); - }} - - ); -}; - -const styles = StyleSheet.create({ - content: { - width: 42, - height: 42, - margin: 0, - }, -}); - -export default ToggleButton; - -// @component-docs ignore-next-line -export { ToggleButton }; diff --git a/src/components/ToggleButton/ToggleButtonGroup.tsx b/src/components/ToggleButton/ToggleButtonGroup.tsx deleted file mode 100644 index f3e52356d8..0000000000 --- a/src/components/ToggleButton/ToggleButtonGroup.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react'; - -export type Props = { - /** - * Function to execute on selection change. - */ - onValueChange: (value: Value) => void; - /** - * Value of the currently selected toggle button. - */ - value: Value | null; - /** - * React elements containing toggle buttons. - */ - children: React.ReactNode; -}; - -type ToggleButtonContextType = { - value: Value | null; - onValueChange: (item: Value) => void; -}; - -export const ToggleButtonGroupContext = - //@ts-expect-error: TS can't ensure the type from Group to children - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - React.createContext(null as any); - -/** - * Toggle group allows to control a group of toggle buttons.
    - * It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow). - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { ToggleButton } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [value, setValue] = React.useState('left'); - * - * return ( - * setValue(value)} - * value={value} - * > - * - * - * - * ); - * }; - * - * export default MyComponent; - *``` - */ -const ToggleButtonGroup = ({ - value, - onValueChange, - children, -}: Props) => ( - - {children} - -); - -ToggleButtonGroup.displayName = 'ToggleButton.Group'; - -export default ToggleButtonGroup; - -// @component-docs ignore-next-line -export { ToggleButtonGroup }; diff --git a/src/components/ToggleButton/ToggleButtonRow.tsx b/src/components/ToggleButton/ToggleButtonRow.tsx deleted file mode 100644 index 46e6aee48f..0000000000 --- a/src/components/ToggleButton/ToggleButtonRow.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import * as React from 'react'; -import { StyleSheet, View } from 'react-native'; -import type { StyleProp, ViewStyle } from 'react-native'; - -import ToggleButton from './ToggleButton'; -import ToggleButtonGroup from './ToggleButtonGroup'; - -export type Props = { - /** - * Function to execute on selection change. - */ - onValueChange: (value: string) => void; - /** - * Value of the currently selected toggle button. - */ - value: string; - /** - * React elements containing toggle buttons. - */ - children: React.ReactNode; - style?: StyleProp; -}; - -/** - * Toggle button row renders a group of toggle buttons in a row. - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { ToggleButton } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [value, setValue] = React.useState('left'); - * - * return ( - * setValue(value)} value={value}> - * - * - * - * ); - * }; - * - * export default MyComponent; - * - *``` - */ -const ToggleButtonRow = ({ value, onValueChange, children, style }: Props) => { - const count = React.Children.count(children); - - return ( - - - {React.Children.map(children, (child, i) => { - // @ts-expect-error: TypeScript complains about child.type but it doesn't matter - if (child && child.type === ToggleButton) { - // @ts-expect-error: We're sure that child is a React Element - return React.cloneElement(child, { - style: [ - styles.button, - i === 0 - ? styles.first - : i === count - 1 - ? styles.last - : styles.middle, - // @ts-expect-error: We're sure that child is a React Element - child.props.style, - ], - }); - } - - return child; - })} - - - ); -}; - -ToggleButtonRow.displayName = 'ToggleButton.Row'; - -const styles = StyleSheet.create({ - row: { - flexDirection: 'row', - }, - button: { - borderWidth: StyleSheet.hairlineWidth, - }, - - first: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - middle: { - borderRadius: 0, - borderLeftWidth: 0, - }, - - last: { - borderLeftWidth: 0, - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }, -}); - -export default ToggleButtonRow; - -// @component-docs ignore-next-line -export { ToggleButtonRow }; diff --git a/src/components/ToggleButton/index.ts b/src/components/ToggleButton/index.ts deleted file mode 100644 index 222d93fb52..0000000000 --- a/src/components/ToggleButton/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import ToggleButtonComponent from './ToggleButton'; -import ToggleButtonGroup from './ToggleButtonGroup'; -import ToggleButtonRow from './ToggleButtonRow'; - -const ToggleButton = Object.assign( - // @component ./ToggleButton.tsx - ToggleButtonComponent, - { - // @component ./ToggleButtonGroup.tsx - Group: ToggleButtonGroup, - // @component ./ToggleButtonRow.tsx - Row: ToggleButtonRow, - } -); - -export default ToggleButton; diff --git a/src/components/ToggleButton/utils.ts b/src/components/ToggleButton/utils.ts deleted file mode 100644 index e5ef7d0c4d..0000000000 --- a/src/components/ToggleButton/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { InternalTheme } from '../../theme/types'; - -export const getToggleButtonColor = ({ - theme, - checked, -}: { - theme: InternalTheme; - checked: boolean | null; -}) => { - if (checked) { - return theme.colors.surfaceContainerHighest; - } - return theme.colors.surfaceContainer; -}; diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx deleted file mode 100644 index 006ae179c5..0000000000 --- a/src/components/__tests__/ToggleButton.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; - -import { render } from '../../test-utils'; -import { DarkTheme, LightTheme } from '../../theme/schemes'; -import ToggleButton from '../ToggleButton'; -import { getToggleButtonColor } from '../ToggleButton/utils'; - -it('renders toggle button', async () => { - const tree = ( - await render( - {}} icon="heart" /> - ) - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders disabled toggle button', async () => { - const tree = ( - await render( - - ) - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders unchecked toggle button', async () => { - const tree = ( - await render() - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -describe('getToggleButtonColor', () => { - it('should return correct color when checked and theme version 3', () => { - expect(getToggleButtonColor({ theme: LightTheme, checked: true })).toBe( - LightTheme.colors.surfaceContainerHighest - ); - }); - - it('should return correct color when checked and theme version 3, dark theme', () => { - expect(getToggleButtonColor({ theme: DarkTheme, checked: true })).toBe( - DarkTheme.colors.surfaceContainerHighest - ); - }); - - it('should return correct color when not checked', () => { - expect(getToggleButtonColor({ theme: LightTheme, checked: false })).toBe( - LightTheme.colors.surfaceContainer - ); - }); -}); diff --git a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap deleted file mode 100644 index 62fedc3a85..0000000000 --- a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap +++ /dev/null @@ -1,385 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders disabled toggle button 1`] = ` - - - - - heart - - - - -`; - -exports[`renders toggle button 1`] = ` - - - - - heart - - - - -`; - -exports[`renders unchecked toggle button 1`] = ` - - - - - heart - - - - -`; diff --git a/src/index.tsx b/src/index.tsx index f46d8e22d8..1807d2f249 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -47,7 +47,6 @@ export { default as Switch } from './components/Switch/Switch'; export { default as Appbar } from './components/Appbar'; export { default as TouchableRipple } from './components/TouchableRipple/TouchableRipple'; export { default as TextInput } from './components/TextInput'; -export { default as ToggleButton } from './components/ToggleButton'; export { default as SegmentedButtons } from './components/SegmentedButtons/SegmentedButtons'; export { default as Tooltip } from './components/Tooltip/Tooltip'; @@ -138,9 +137,6 @@ export type { TextInputAccessoryProps, TextInputIconProps, } from './components/TextInput/TextInputIcon'; -export type { Props as ToggleButtonProps } from './components/ToggleButton/ToggleButton'; -export type { Props as ToggleButtonGroupProps } from './components/ToggleButton/ToggleButtonGroup'; -export type { Props as ToggleButtonRowProps } from './components/ToggleButton/ToggleButtonRow'; export type { Props as TouchableRippleProps } from './components/TouchableRipple/TouchableRipple'; export type { Props as TextProps } from './components/Typography/Text'; export type { Props as SegmentedButtonsProps } from './components/SegmentedButtons/SegmentedButtons'; From a74781ed3313b26d92b15940f00395f2636a0a08 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Wed, 16 Sep 2026 14:58:54 +0200 Subject: [PATCH 77/94] ci: fix pull request check workflow permissions --- .github/workflows/pull-request-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request-checks.yml b/.github/workflows/pull-request-checks.yml index ced505b5a4..350cd9a621 100644 --- a/.github/workflows/pull-request-checks.yml +++ b/.github/workflows/pull-request-checks.yml @@ -6,7 +6,7 @@ on: permissions: issues: write - pull-requests: read + pull-requests: write concurrency: group: pull-request-checks-${{ github.event.pull_request.number }} From 84529e066767f99067e975524fa30b34cef2f8a7 Mon Sep 17 00:00:00 2001 From: konstmar Date: Wed, 16 Sep 2026 15:01:13 +0200 Subject: [PATCH 78/94] fix(portal): forward the reduce motion preference and match queued update keys (#5124) --- src/components/Portal/Portal.tsx | 10 +++++++--- src/components/Portal/PortalHost.tsx | 4 +++- src/components/__tests__/Portal.test.tsx | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index 1326026913..633b1c9a05 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -8,6 +8,7 @@ import { Provider as SettingsProvider, } from '../../core/settings'; import { ThemeProvider, useInternalTheme } from '../../core/theming'; +import { ReduceMotionContext } from '../../theme/accessibility/ReduceMotionContext'; import type { ThemeProp } from '../../theme/types'; export type Props = { @@ -46,13 +47,16 @@ const Portal = ({ children, theme: themeOverrides }: Props) => { const { direction } = useLocale(); const settings = React.useContext(SettingsContext); const manager = React.useContext(PortalContext); + const reduceMotion = React.useContext(ReduceMotionContext); return ( - - {children} - + + + {children} + + ); diff --git a/src/components/Portal/PortalHost.tsx b/src/components/Portal/PortalHost.tsx index ecc20b8a72..4238f48947 100644 --- a/src/components/Portal/PortalHost.tsx +++ b/src/components/Portal/PortalHost.tsx @@ -92,7 +92,9 @@ export default class PortalHost extends React.Component { } else { const op: Operation = { type: 'mount', key, children }; const index = this.queue.findIndex( - (o) => o.type === 'mount' || (o.type === 'update' && o.key === key) + (o) => + (o.type === 'mount' && o.key === key) || + (o.type === 'update' && o.key === key) ); if (index > -1) { diff --git a/src/components/__tests__/Portal.test.tsx b/src/components/__tests__/Portal.test.tsx index 6865d4d159..7e8118a045 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -3,8 +3,10 @@ import { Text } from 'react-native'; import { expect, it, jest } from '@jest/globals'; import { LocaleProvider, useLocale } from '../../core/locale'; +import PaperProvider from '../../core/PaperProvider'; import { useInternalTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; +import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; import Dialog from '../Dialog/Dialog'; import Modal from '../Modal'; import Portal from '../Portal/Portal'; @@ -60,6 +62,22 @@ it('passes local theme overrides and locale to portal content and updates them', expect(screen.queryByText('2 rtl')).not.toBeOnTheScreen(); }); +const PortalReduceMotionContent = () => ( + {`reduce motion: ${useReduceMotion()}`} +); + +it('passes the reduce motion preference to portal content', async () => { + await render( + + + + + + ); + + expect(await screen.findByText('reduce motion: true')).toBeOnTheScreen(); +}); + it('renders portals in source order when mounted in the same commit', async () => { await render( From 80092b715a1f03855973dcc4927d18c30593b2b3 Mon Sep 17 00:00:00 2001 From: konstmar Date: Mon, 28 Sep 2026 14:51:47 +0200 Subject: [PATCH 79/94] feat(portal)!: hide content below modals from assistive technology (#5126) - Render Modal and Dialog in their own portals - Hide the app and lower portal layers from assistive technology and focus while a modal is open - Preserve reduced motion settings in portal content BREAKING CHANGE: This adds `Portal` to `Modal` and `Dialog` internally. So they need to remove any wrapper `Portal` and must be rendered inside a `Portal.Host` or `PaperProvider`. --------- Co-authored-by: Konstantin Marushchak Co-authored-by: Satyajit Sahoo --- docs/6.x/docs/guides/migration.md | 26 + example/src/DrawerItems.tsx | 35 +- .../Dialogs/DialogWithCustomColors.tsx | 46 +- .../DialogWithDismissableBackButton.tsx | 44 +- .../src/Examples/Dialogs/DialogWithIcon.tsx | 36 +- .../Dialogs/DialogWithLoadingIndicator.tsx | 30 +- .../Examples/Dialogs/DialogWithLongText.tsx | 110 +- .../Examples/Dialogs/DialogWithRadioBtns.tsx | 125 +- .../Examples/Dialogs/UndismissableDialog.tsx | 28 +- src/components/Dialog/Dialog.tsx | 23 +- src/components/Dialog/DialogActions.tsx | 16 +- src/components/Dialog/DialogContent.tsx | 14 +- src/components/Dialog/DialogIcon.tsx | 18 +- src/components/Dialog/DialogScrollArea.tsx | 18 +- src/components/Dialog/DialogTitle.tsx | 16 +- src/components/Modal.tsx | 101 +- src/components/Portal/Portal.tsx | 10 +- src/components/Portal/PortalConsumer.tsx | 5 +- src/components/Portal/PortalHost.tsx | 63 +- src/components/Portal/PortalLayer.tsx | 19 + src/components/Portal/PortalManager.tsx | 77 +- src/components/__tests__/Dialog.test.tsx | 62 +- src/components/__tests__/Modal.test.tsx | 267 +- src/components/__tests__/Portal.test.tsx | 207 +- .../__snapshots__/Modal.test.tsx.snap | 7852 +++++++++-------- 25 files changed, 5070 insertions(+), 4178 deletions(-) create mode 100644 src/components/Portal/PortalLayer.tsx diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index e51de006aa..054288fec5 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -158,6 +158,19 @@ e.g.: ### Modal +`Modal` now uses a `Portal` internally and doesn't require an explicit `Portal` wrapper. So you need to remove any existing `Portal` wrappers around `Modal`: + +```diff +- +- +- Content +- +- ++ ++ Content ++ +``` + - The `contentContainerStyle` prop no longer configures the background color or any border radius property. We have added new props for these: - `contentBackgroundColor` - `contentBorderRadius` @@ -184,6 +197,19 @@ e.g.: ### Dialog +`Dialog` now uses a `Modal` internally and doesn't require an explicit `Portal` wrapper. So you need to remove any existing `Portal` wrappers around `Dialog`: + +```diff +- +- +- Alert +- +- ++ ++ Alert ++ +``` + - The default elevation changed from level `1` to level `3`. - The `style` prop no longer configures the background color or border radius. You can override `theme.colors.surfaceContainerHigh` and `theme.shapes.corner.extraLarge` using the `theme` prop instead. diff --git a/example/src/DrawerItems.tsx b/example/src/DrawerItems.tsx index 49bdd5f99d..7e39596f9b 100644 --- a/example/src/DrawerItems.tsx +++ b/example/src/DrawerItems.tsx @@ -10,7 +10,6 @@ import { Dialog, Drawer, Palette, - Portal, Switch, Text, TouchableRipple, @@ -240,24 +239,22 @@ function DrawerItems() {
    )} - - - Changing to RTL - - - Due to Expo Go limitations it is impossible to change RTL - dynamically. To do so, you need to create a development build of - Example app or change it statically by setting{' '} - forcesRTL property to true in{' '} - app.json within{' '} - example directory. - - - - - - - + + Changing to RTL + + + Due to Expo Go limitations it is impossible to change RTL + dynamically. To do so, you need to create a development build of + Example app or change it statically by setting{' '} + forcesRTL property to true in{' '} + app.json within{' '} + example directory. + + + + + + ); } diff --git a/example/src/Examples/Dialogs/DialogWithCustomColors.tsx b/example/src/Examples/Dialogs/DialogWithCustomColors.tsx index 75b5ee6b6e..df24a5f3f6 100644 --- a/example/src/Examples/Dialogs/DialogWithCustomColors.tsx +++ b/example/src/Examples/Dialogs/DialogWithCustomColors.tsx @@ -1,4 +1,4 @@ -import { Button, Portal, Dialog, Palette } from 'react-native-paper'; +import { Button, Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -10,29 +10,27 @@ const DialogWithCustomColors = ({ close: () => void; }) => { return ( - - - Alert - - - This is a dialog with custom colors - - - - - - - + + Alert + + + This is a dialog with custom colors + + + + + + ); }; diff --git a/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx b/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx index e9a7189068..d7a37e5569 100644 --- a/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx +++ b/example/src/Examples/Dialogs/DialogWithDismissableBackButton.tsx @@ -1,4 +1,4 @@ -import { Button, Portal, Dialog, Palette } from 'react-native-paper'; +import { Button, Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -9,28 +9,26 @@ const DialogWithDismissableBackButton = ({ visible: boolean; close: () => void; }) => ( - - - Alert - - - This is an undismissable dialog, however you can use hardware back - button to close it! - - - - - - - - + + Alert + + + This is an undismissable dialog, however you can use hardware back + button to close it! + + + + + + + ); export default DialogWithDismissableBackButton; diff --git a/example/src/Examples/Dialogs/DialogWithIcon.tsx b/example/src/Examples/Dialogs/DialogWithIcon.tsx index 6281e9bf6c..0d5f595f9c 100644 --- a/example/src/Examples/Dialogs/DialogWithIcon.tsx +++ b/example/src/Examples/Dialogs/DialogWithIcon.tsx @@ -1,6 +1,6 @@ import { StyleSheet } from 'react-native'; -import { Button, Portal, Dialog, Palette } from 'react-native-paper'; +import { Button, Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -12,24 +12,22 @@ const DialogWithIcon = ({ close: () => void; }) => { return ( - - - - Dialog with Icon - - - This is a dialog with new component called DialogIcon. When icon is - displayed you should center the header. - - - - - - - - + + + Dialog with Icon + + + This is a dialog with new component called DialogIcon. When icon is + displayed you should center the header. + + + + + + + ); }; diff --git a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx index d854dcb587..f70c93d576 100644 --- a/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx +++ b/example/src/Examples/Dialogs/DialogWithLoadingIndicator.tsx @@ -1,6 +1,6 @@ import { ActivityIndicator, Platform, StyleSheet, View } from 'react-native'; -import { Dialog, Palette, Portal } from 'react-native-paper'; +import { Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -12,21 +12,19 @@ const DialogWithLoadingIndicator = ({ close: () => void; }) => { return ( - - - Progress Dialog - - - - Loading..... - - - - + + Progress Dialog + + + + Loading..... + + + ); }; diff --git a/example/src/Examples/Dialogs/DialogWithLongText.tsx b/example/src/Examples/Dialogs/DialogWithLongText.tsx index eeac1c7d3c..eed30ec358 100644 --- a/example/src/Examples/Dialogs/DialogWithLongText.tsx +++ b/example/src/Examples/Dialogs/DialogWithLongText.tsx @@ -1,6 +1,6 @@ import { Dimensions, ScrollView, StyleSheet } from 'react-native'; -import { Button, Portal, Dialog } from 'react-native-paper'; +import { Button, Dialog } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -11,61 +11,59 @@ const DialogWithLongText = ({ visible: boolean; close: () => void; }) => ( - - - Alert - - - - Material is the metaphor - {'\n'} - {'\n'}A material metaphor is the unifying theory of a rationalized - space and a system of motion. The material is grounded in tactile - reality, inspired by the study of paper and ink, yet technologically - advanced and open to imagination and magic. - {'\n'} - {'\n'} - Surfaces and edges of the material provide visual cues that are - grounded in reality. The use of familiar tactile attributes helps - users quickly understand affordances. Yet the flexibility of the - material creates new affordances that supersede those in the - physical world, without breaking the rules of physics. - {'\n'} - {'\n'} - The fundamentals of light, surface, and movement are key to - conveying how objects move, interact, and exist in space and in - relation to each other. Realistic lighting shows seams, divides - space, and indicates moving parts. - {'\n'} - {'\n'}A material metaphor is the unifying theory of a rationalized - space and a system of motion. The material is grounded in tactile - reality, inspired by the study of paper and ink, yet technologically - advanced and open to imagination and magic. - {'\n'} - {'\n'} - Surfaces and edges of the material provide visual cues that are - grounded in reality. The use of familiar tactile attributes helps - users quickly understand affordances. Yet the flexibility of the - material creates new affordances that supersede those in the - physical world, without breaking the rules of physics. - {'\n'} - {'\n'} - The fundamentals of light, surface, and movement are key to - conveying how objects move, interact, and exist in space and in - relation to each other. Realistic lighting shows seams, divides - space, and indicates moving parts. - - - - - - - - + + Alert + + + + Material is the metaphor + {'\n'} + {'\n'}A material metaphor is the unifying theory of a rationalized + space and a system of motion. The material is grounded in tactile + reality, inspired by the study of paper and ink, yet technologically + advanced and open to imagination and magic. + {'\n'} + {'\n'} + Surfaces and edges of the material provide visual cues that are + grounded in reality. The use of familiar tactile attributes helps + users quickly understand affordances. Yet the flexibility of the + material creates new affordances that supersede those in the physical + world, without breaking the rules of physics. + {'\n'} + {'\n'} + The fundamentals of light, surface, and movement are key to conveying + how objects move, interact, and exist in space and in relation to each + other. Realistic lighting shows seams, divides space, and indicates + moving parts. + {'\n'} + {'\n'}A material metaphor is the unifying theory of a rationalized + space and a system of motion. The material is grounded in tactile + reality, inspired by the study of paper and ink, yet technologically + advanced and open to imagination and magic. + {'\n'} + {'\n'} + Surfaces and edges of the material provide visual cues that are + grounded in reality. The use of familiar tactile attributes helps + users quickly understand affordances. Yet the flexibility of the + material creates new affordances that supersede those in the physical + world, without breaking the rules of physics. + {'\n'} + {'\n'} + The fundamentals of light, surface, and movement are key to conveying + how objects move, interact, and exist in space and in relation to each + other. Realistic lighting shows seams, divides space, and indicates + moving parts. + + + + + + + ); const styles = StyleSheet.create({ diff --git a/example/src/Examples/Dialogs/DialogWithRadioBtns.tsx b/example/src/Examples/Dialogs/DialogWithRadioBtns.tsx index 966422369e..5a729d6299 100644 --- a/example/src/Examples/Dialogs/DialogWithRadioBtns.tsx +++ b/example/src/Examples/Dialogs/DialogWithRadioBtns.tsx @@ -3,7 +3,6 @@ import { ScrollView, View, StyleSheet } from 'react-native'; import { Button, - Portal, Dialog, RadioButton, TouchableRipple, @@ -22,73 +21,71 @@ const DialogWithRadioBtns = ({ visible, close }: Props) => { const [checked, setChecked] = React.useState('normal'); return ( - - - Choose an option - - - - setChecked('normal')}> - - - - - - Option 1 - + + Choose an option + + + + setChecked('normal')}> + + + - - setChecked('second')}> - - - - - - Option 2 - + + Option 1 + + + + setChecked('second')}> + + + - - setChecked('third')}> - - - - - - Option 3 - + + Option 2 + + + + setChecked('third')}> + + + - - setChecked('fourth')}> - - - - - - Option 4 - + + Option 3 + + + + setChecked('fourth')}> + + + - - - - - - - - - - + + Option 4 + +
    + +
    + + + + + + + ); }; diff --git a/example/src/Examples/Dialogs/UndismissableDialog.tsx b/example/src/Examples/Dialogs/UndismissableDialog.tsx index 208c809967..79dc777484 100644 --- a/example/src/Examples/Dialogs/UndismissableDialog.tsx +++ b/example/src/Examples/Dialogs/UndismissableDialog.tsx @@ -1,4 +1,4 @@ -import { Button, Portal, Dialog, Palette } from 'react-native-paper'; +import { Button, Dialog, Palette } from 'react-native-paper'; import { TextComponent } from './DialogTextComponent'; @@ -9,20 +9,18 @@ const UndismissableDialog = ({ visible: boolean; close: () => void; }) => ( - - - Alert - - This is an undismissable dialog!! - - - - - - - + + Alert + + This is an undismissable dialog!! + + + + + + ); export default UndismissableDialog; diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 61af39bb31..9c75d4c08a 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -55,13 +55,12 @@ const DIALOG_ELEVATION: Elevation = 3; /** * Dialogs inform users about a specific task and may contain critical information, require decisions, or involve multiple tasks. - * To render the `Dialog` above other components, you'll need to wrap it with the [`Portal`](../Portal) component. * * ## Usage * ```js * import * as React from 'react'; * import { View } from 'react-native'; - * import { Button, Dialog, Portal, PaperProvider, Text } from 'react-native-paper'; + * import { Button, Dialog, PaperProvider, Text } from 'react-native-paper'; * * const MyComponent = () => { * const [visible, setVisible] = React.useState(false); @@ -74,17 +73,15 @@ const DIALOG_ELEVATION: Elevation = 3; * * * - * - * - * Alert - * - * This is simple dialog - * - * - * - * - * - * + * + * Alert + * + * This is simple dialog + * + * + * + * + * * * * ); diff --git a/src/components/Dialog/DialogActions.tsx b/src/components/Dialog/DialogActions.tsx index 0a11970077..1314613820 100644 --- a/src/components/Dialog/DialogActions.tsx +++ b/src/components/Dialog/DialogActions.tsx @@ -24,7 +24,7 @@ export type Props = ViewProps & { * ## Usage * ```js * import * as React from 'react'; - * import { Button, Dialog, Portal } from 'react-native-paper'; + * import { Button, Dialog } from 'react-native-paper'; * * const MyComponent = () => { * const [visible, setVisible] = React.useState(false); @@ -32,14 +32,12 @@ export type Props = ViewProps & { * const hideDialog = () => setVisible(false); * * return ( - * - * - * - * - * - * - * - * + * + * + * + * + * + * * ); * }; * diff --git a/src/components/Dialog/DialogContent.tsx b/src/components/Dialog/DialogContent.tsx index a084188b32..9af1742608 100644 --- a/src/components/Dialog/DialogContent.tsx +++ b/src/components/Dialog/DialogContent.tsx @@ -16,7 +16,7 @@ export type Props = ViewProps & { * ## Usage * ```js * import * as React from 'react'; - * import { Dialog, Portal, Text } from 'react-native-paper'; + * import { Dialog, Text } from 'react-native-paper'; * * const MyComponent = () => { * const [visible, setVisible] = React.useState(false); @@ -24,13 +24,11 @@ export type Props = ViewProps & { * const hideDialog = () => setVisible(false); * * return ( - * - * - * - * This is simple dialog - * - * - * + * + * + * This is simple dialog + * + * * ); * }; * diff --git a/src/components/Dialog/DialogIcon.tsx b/src/components/Dialog/DialogIcon.tsx index 791544aefe..efdec2e6a5 100644 --- a/src/components/Dialog/DialogIcon.tsx +++ b/src/components/Dialog/DialogIcon.tsx @@ -33,7 +33,7 @@ export type Props = { * ```js * import * as React from 'react'; * import { StyleSheet } from 'react-native'; - * import { Dialog, Portal, Text } from 'react-native-paper'; + * import { Dialog, Text } from 'react-native-paper'; * * const MyComponent = () => { * const [visible, setVisible] = React.useState(false); @@ -41,15 +41,13 @@ export type Props = { * const hideDialog = () => setVisible(false); * * return ( - * - * - * - * This is a title - * - * This is simple dialog - * - * - * + * + * + * This is a title + * + * This is simple dialog + * + * * ); * }; * diff --git a/src/components/Dialog/DialogScrollArea.tsx b/src/components/Dialog/DialogScrollArea.tsx index c2446f6149..bc0e03792d 100644 --- a/src/components/Dialog/DialogScrollArea.tsx +++ b/src/components/Dialog/DialogScrollArea.tsx @@ -25,7 +25,7 @@ export type Props = ViewProps & { * ```js * import * as React from 'react'; * import { ScrollView } from 'react-native'; - * import { Dialog, Portal, Text } from 'react-native-paper'; + * import { Dialog, Text } from 'react-native-paper'; * * const MyComponent = () => { * const [visible, setVisible] = React.useState(false); @@ -33,15 +33,13 @@ export type Props = ViewProps & { * const hideDialog = () => setVisible(false); * * return ( - * - * - * - * - * This is a scrollable area - * - * - * - * + * + * + * + * This is a scrollable area + * + * + * * ); * }; * diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index beff3be7d9..af36209cdc 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -24,7 +24,7 @@ export type Props = React.ComponentPropsWithRef & { * ## Usage * ```js * import * as React from 'react'; - * import { Dialog, Portal, Text } from 'react-native-paper'; + * import { Dialog, Text } from 'react-native-paper'; * * const MyComponent = () => { * const [visible, setVisible] = React.useState(false); @@ -32,14 +32,12 @@ export type Props = React.ComponentPropsWithRef & { * const hideDialog = () => setVisible(false); * * return ( - * - * - * This is a title - * - * This is simple dialog - * - * - * + * + * This is a title + * + * This is simple dialog + * + * * ); * }; * diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index fbc1197630..71eb2a7914 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -9,6 +9,7 @@ import Animated, { import { useSafeAreaInsets } from 'react-native-safe-area-context'; import useLatestCallback from 'use-latest-callback'; +import Portal from './Portal/Portal'; import Surface from './Surface'; import type { Props as SurfaceProps, SurfaceStyle } from './Surface'; import { useInternalTheme } from '../core/theming'; @@ -88,13 +89,11 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); /** * The Modal component is a simple way to present content above an enclosing view. - * To render the `Modal` above other components, you'll need to wrap it with the [`Portal`](./Portal) component. - * Note that this modal is NOT accessible by default; if you need an accessible modal, please use the React Native Modal. * * ## Usage * ```js * import * as React from 'react'; - * import { Modal, Portal, Text, Button, PaperProvider } from 'react-native-paper'; + * import { Modal, Text, Button, PaperProvider } from 'react-native-paper'; * * const MyComponent = () => { * const [visible, setVisible] = React.useState(false); @@ -106,16 +105,14 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); * * return ( * - * - * - * Example Modal. Click outside this area to dismiss. - * - * + * + * Example Modal. Click outside this area to dismiss. + * * @@ -225,48 +222,50 @@ function Modal({ } return ( - - - + - + - {children} - - - + + {children} + +
    + + ); } diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index 633b1c9a05..b11c1c6434 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -16,6 +16,12 @@ export type Props = { * Content of the `Portal`. */ children: React.ReactNode; + /** + * Whether the portal hides items below it from screen readers and focus order. + * + * Ensure it's set to true only when the modal is open. + */ + modal?: boolean; /** * @optional */ @@ -42,7 +48,7 @@ export type Props = { * export default MyComponent; * ``` */ -const Portal = ({ children, theme: themeOverrides }: Props) => { +const Portal = ({ children, modal, theme: themeOverrides }: Props) => { const theme = useInternalTheme(themeOverrides); const { direction } = useLocale(); const settings = React.useContext(SettingsContext); @@ -50,7 +56,7 @@ const Portal = ({ children, theme: themeOverrides }: Props) => { const reduceMotion = React.useContext(ReduceMotionContext); return ( - + diff --git a/src/components/Portal/PortalConsumer.tsx b/src/components/Portal/PortalConsumer.tsx index e933d2b24e..e148265104 100644 --- a/src/components/Portal/PortalConsumer.tsx +++ b/src/components/Portal/PortalConsumer.tsx @@ -5,19 +5,20 @@ import type { PortalMethods } from './PortalHost'; type Props = { manager: PortalMethods; children: React.ReactNode; + modal?: boolean; }; export default class PortalConsumer extends React.Component { componentDidMount() { this.checkManager(); - this.key = this.props.manager.mount(this.props.children); + this.key = this.props.manager.mount(this.props.children, this.props.modal); } componentDidUpdate() { this.checkManager(); - this.props.manager.update(this.key, this.props.children); + this.props.manager.update(this.key, this.props.children, this.props.modal); } componentWillUnmount() { diff --git a/src/components/Portal/PortalHost.tsx b/src/components/Portal/PortalHost.tsx index 4238f48947..a095d8d58f 100644 --- a/src/components/Portal/PortalHost.tsx +++ b/src/components/Portal/PortalHost.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { View, StyleSheet } from 'react-native'; import PortalManager from './PortalManager'; @@ -8,13 +7,27 @@ export type Props = { }; type Operation = - | { type: 'mount'; key: number; children: React.ReactNode } - | { type: 'update'; key: number; children: React.ReactNode } + | { + type: 'mount'; + key: number; + children: React.ReactNode; + modal: boolean | undefined; + } + | { + type: 'update'; + key: number; + children: React.ReactNode; + modal: boolean | undefined; + } | { type: 'unmount'; key: number }; export type PortalMethods = { - mount: (children: React.ReactNode) => number; - update: (key: number, children: React.ReactNode) => void; + mount: (children: React.ReactNode, modal: boolean | undefined) => number; + update: ( + key: number, + children: React.ReactNode, + modal: boolean | undefined + ) => void; unmount: (key: number) => void; }; @@ -57,10 +70,10 @@ export default class PortalHost extends React.Component { if (action) { switch (action.type) { case 'mount': - manager.mount(action.key, action.children); + manager.mount(action.key, action.children, action.modal); break; case 'update': - manager.update(action.key, action.children); + manager.update(action.key, action.children, action.modal); break; case 'unmount': manager.unmount(action.key); @@ -74,27 +87,29 @@ export default class PortalHost extends React.Component { this.manager = manager; }; - private mount = (children: React.ReactNode) => { + private mount = (children: React.ReactNode, modal: boolean | undefined) => { const key = this.nextKey++; if (this.manager) { - this.manager.mount(key, children); + this.manager.mount(key, children, modal); } else { - this.queue.push({ type: 'mount', key, children }); + this.queue.push({ type: 'mount', key, children, modal }); } return key; }; - private update = (key: number, children: React.ReactNode) => { + private update = ( + key: number, + children: React.ReactNode, + modal: boolean | undefined + ) => { if (this.manager) { - this.manager.update(key, children); + this.manager.update(key, children, modal); } else { - const op: Operation = { type: 'mount', key, children }; + const op: Operation = { type: 'mount', key, children, modal }; const index = this.queue.findIndex( - (o) => - (o.type === 'mount' && o.key === key) || - (o.type === 'update' && o.key === key) + (o) => (o.type === 'mount' || o.type === 'update') && o.key === key ); if (index > -1) { @@ -126,22 +141,10 @@ export default class PortalHost extends React.Component { unmount: this.unmount, }} > - {/* Need collapsable=false here to clip the elevations, otherwise they appear above Portal components */} - + {this.props.children} - - + ); } } - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); diff --git a/src/components/Portal/PortalLayer.tsx b/src/components/Portal/PortalLayer.tsx new file mode 100644 index 0000000000..543685eded --- /dev/null +++ b/src/components/Portal/PortalLayer.tsx @@ -0,0 +1,19 @@ +import { Platform, View } from 'react-native'; +import type { ViewProps } from 'react-native'; + +export type Props = ViewProps & { + inert: boolean; +}; + +export default function PortalLayer({ inert, children, ...rest }: Props) { + return ( + + {children} + + ); +} diff --git a/src/components/Portal/PortalManager.tsx b/src/components/Portal/PortalManager.tsx index cc2bdbe1a7..307236f17f 100644 --- a/src/components/Portal/PortalManager.tsx +++ b/src/components/Portal/PortalManager.tsx @@ -1,33 +1,49 @@ import * as React from 'react'; -import { View, StyleSheet } from 'react-native'; +import { StyleSheet } from 'react-native'; + +import PortalLayer from './PortalLayer'; + +type Props = { + children: React.ReactNode; +}; type State = { portals: Array<{ key: number; children: React.ReactNode; + modal: boolean | undefined; }>; }; /** * Portal host is the component which actually renders all Portals. */ -export default class PortalManager extends React.PureComponent<{}, State> { +export default class PortalManager extends React.Component { state: State = { portals: [], }; - mount = (key: number, children: React.ReactNode) => { + mount = ( + key: number, + children: React.ReactNode, + modal: boolean | undefined + ) => { this.setState((state) => ({ - portals: [...state.portals, { key, children }], + portals: [...state.portals, { key, children, modal }], })); }; - update = (key: number, children: React.ReactNode) => + update = ( + key: number, + children: React.ReactNode, + modal: boolean | undefined + ) => this.setState((state) => ({ portals: state.portals.map((item) => { if (item.key === key) { - return { ...item, children }; + return { ...item, children, modal }; } + return item; }), })); @@ -38,17 +54,42 @@ export default class PortalManager extends React.PureComponent<{}, State> { })); render() { - return this.state.portals.map(({ key, children }) => ( - - {children} - - )); + const { portals } = this.state; + + const topmostModalIndex = portals.findLastIndex((portal) => portal.modal); + + return ( + <> + = 0} + style={styles.container} + collapsable={ + false /* Need collapsable=false here to clip the elevations, otherwise they appear above Portal components */ + } + pointerEvents="box-none" + > + {this.props.children} + + {portals.map(({ key, children }, index) => ( + + {children} + + ))} + + ); } } + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 48d22a587b..129cf596f2 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -12,6 +12,7 @@ import { act, userEvent } from '@testing-library/react-native'; import Dialog from '../../components/Dialog/Dialog'; import { render, screen } from '../../test-utils'; import Button from '../Button/Button'; +import Portal from '../Portal/Portal'; interface BackHandlerStatic extends RNBackHandlerStatic { mockPressBack(): void; @@ -23,9 +24,11 @@ const BackHandler = RNBackHandler as BackHandlerStatic; describe('Dialog', () => { it('should render passed children', async () => { await render( - - This is simple dialog - + + + This is simple dialog + + ); expect(screen.getByTestId('dialog')).toHaveTextContent( @@ -36,9 +39,11 @@ describe('Dialog', () => { it('should call onDismiss when dismissable', async () => { const onDismiss = jest.fn(); await render( - - This is simple dialog - + + + This is simple dialog + + ); await userEvent.press(screen.getByLabelText('Close modal')); @@ -52,9 +57,16 @@ describe('Dialog', () => { it('should not call onDismiss when dismissable is false', async () => { const onDismiss = jest.fn(); await render( - - This is simple dialog - + + + This is simple dialog + + ); await userEvent.press(screen.getByLabelText('Close modal')); @@ -69,15 +81,17 @@ describe('Dialog', () => { Platform.OS = 'android'; const onDismiss = jest.fn(); await render( - - This is simple dialog - + + + This is simple dialog + + ); await userEvent.press(screen.getByLabelText('Close modal')); @@ -96,11 +110,13 @@ describe('Dialog', () => { it('should apply top margin to the first child if the dialog is V3', async () => { await render( - - - Test Dialog Content - - + + + + Test Dialog Content + + + ); expect(screen.getByTestId('dialog-content')).toHaveStyle({ diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 657eaf0992..84c9087bd5 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -8,6 +8,7 @@ import { render, screen } from '../../test-utils'; import { LightTheme } from '../../theme/schemes'; import { tokens } from '../../theme/tokens'; import Modal from '../Modal'; +import Portal from '../Portal/Portal'; const scrimAlpha = tokens.md.sys.scrim.alpha; @@ -42,9 +43,11 @@ describe('Modal', () => { describe('by default', () => { it('should render passed children', async () => { await render( - - Children - + + + Children + + ); expect(screen.getByTestId('modal')).toHaveTextContent('Children'); @@ -52,9 +55,11 @@ describe('Modal', () => { it("should render a backdrop in default theme's color", async () => { await render( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -64,17 +69,19 @@ describe('Modal', () => { it('should render a custom backdrop color if specified', async () => { await render( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -84,9 +91,11 @@ describe('Modal', () => { it('should receive appropriate top and bottom insets', async () => { const { toJSON } = await render( - - {null} - + + + {null} + + ); expect(toJSON()).toMatchSnapshot(); @@ -97,9 +106,11 @@ describe('Modal', () => { it('should invoke the onDismiss function immediately', async () => { const onDismiss = jest.fn(); const { toJSON } = await render( - - {null} - + + + {null} + + ); expect(onDismiss).not.toHaveBeenCalled(); @@ -126,9 +137,11 @@ describe('Modal', () => { it('runs the closing animation if visible toggled', async () => { const { rerender, toJSON } = await render( - {}}> - {null} - + + {}}> + {null} + + ); expect(toJSON()).toMatchSnapshot(); @@ -136,9 +149,11 @@ describe('Modal', () => { await userEvent.press(screen.getByLabelText('Close modal')); await rerender( - {}}> - {null} - + + {}}> + {null} + + ); expect(toJSON()).toMatchSnapshot(); @@ -153,16 +168,18 @@ describe('Modal', () => { jest.runAllTimers(); }); - expect(toJSON()).toBeNull(); + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); }); describe('if closed via Android back button', () => { it('invokes onDismiss', async () => { const onDismiss = jest.fn(); const { toJSON } = await render( - - {null} - + + + {null} + + ); expect(toJSON()).toMatchSnapshot(); @@ -192,14 +209,16 @@ describe('Modal', () => { describe('if closed via touching backdrop', () => { it('will run the animation but not fade out', async () => { const { toJSON } = await render( - {}} - dismissable={false} - > - {null} - + + {}} + dismissable={false} + > + {null} + + ); expect(toJSON()).toMatchSnapshot(); @@ -222,14 +241,16 @@ describe('Modal', () => { it('should not invoke onDismiss', async () => { const onDismiss = jest.fn(); await render( - - {null} - + + + {null} + + ); expect(onDismiss).not.toHaveBeenCalled(); @@ -249,14 +270,16 @@ describe('Modal', () => { describe('if closed via Android back button', () => { it('will run the animation but not fade out', async () => { const { toJSON } = await render( - {}} - dismissable={false} - > - {null} - + + {}} + dismissable={false} + > + {null} + + ); expect(toJSON()).toMatchSnapshot(); @@ -282,14 +305,16 @@ describe('Modal', () => { const onDismiss = jest.fn(); await render( - - {null} - + + + {null} + + ); expect(onDismiss).not.toHaveBeenCalled(); @@ -313,17 +338,21 @@ describe('Modal', () => { describe('from false to true (closed to open)', () => { it('should run fade-in animation on opening', async () => { const { rerender, toJSON } = await render( - - {null} - + + + {null} + + ); expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); await rerender( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -345,9 +374,11 @@ describe('Modal', () => { describe('from true to false (open to closed)', () => { it('should run fade-out animation on closing', async () => { const { rerender, toJSON } = await render( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -356,9 +387,11 @@ describe('Modal', () => { expect(toJSON()).toMatchSnapshot(); await rerender( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -377,17 +410,21 @@ describe('Modal', () => { const onDismiss = jest.fn(); const { rerender } = await render( - - {null} - + + + {null} + + ); expect(onDismiss).not.toHaveBeenCalled(); await rerender( - - {null} - + + + {null} + + ); expect(onDismiss).not.toHaveBeenCalled(); @@ -401,9 +438,11 @@ describe('Modal', () => { it('should close even if the dialog is not dismissible', async () => { const { rerender, toJSON } = await render( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -412,9 +451,11 @@ describe('Modal', () => { expect(toJSON()).toMatchSnapshot(); await rerender( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -435,9 +476,11 @@ describe('Modal', () => { describe('while closing, back to true (visible)', () => { it('should keep the modal open', async () => { const { rerender, toJSON } = await render( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -446,9 +489,11 @@ describe('Modal', () => { expect(toJSON()).toMatchSnapshot(); await rerender( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -463,9 +508,11 @@ describe('Modal', () => { }); await rerender( - - {null} - + + + {null} + + ); await act(() => { @@ -482,17 +529,21 @@ describe('Modal', () => { describe('while opening, back to false (hidden)', () => { it('should keep the modal closed', async () => { const { rerender, toJSON } = await render( - - {null} - + + + {null} + + ); expect(screen.queryByLabelText('Close modal')).not.toBeOnTheScreen(); await rerender( - - {null} - + + + {null} + + ); expect(screen.getByLabelText('Close modal')).toHaveStyle({ @@ -509,9 +560,11 @@ describe('Modal', () => { expect(screen.getByLabelText('Close modal')).toBeOnTheScreen(); await rerender( - - {null} - + + + {null} + + ); await act(() => { diff --git a/src/components/__tests__/Portal.test.tsx b/src/components/__tests__/Portal.test.tsx index 7e8118a045..64ff1121bb 100644 --- a/src/components/__tests__/Portal.test.tsx +++ b/src/components/__tests__/Portal.test.tsx @@ -3,10 +3,12 @@ import { Text } from 'react-native'; import { expect, it, jest } from '@jest/globals'; import { LocaleProvider, useLocale } from '../../core/locale'; -import PaperProvider from '../../core/PaperProvider'; import { useInternalTheme } from '../../core/theming'; import { render, screen } from '../../test-utils'; -import { useReduceMotion } from '../../theme/accessibility/ReduceMotionContext'; +import { + ReduceMotionContext, + useReduceMotion, +} from '../../theme/accessibility/ReduceMotionContext'; import Dialog from '../Dialog/Dialog'; import Modal from '../Modal'; import Portal from '../Portal/Portal'; @@ -28,57 +30,48 @@ it('renders portal with siblings', async () => { expect(toJSON()).toMatchSnapshot(); }); -const PortalThemeContent = () => { - const theme = useInternalTheme(undefined); - const { direction } = useLocale(); +it('portal content reflects theme, direction, and reduced motion changes', async () => { + const PortalContent = () => { + const theme = useInternalTheme(undefined); + const { direction } = useLocale(); + const reduceMotion = useReduceMotion(); - return {`${theme.animation.scale} ${direction}`}; -}; + return ( + {`${theme.animation.scale} ${direction} ${reduceMotion}`} + ); + }; -it('passes local theme overrides and locale to portal content and updates them', async () => { const { rerender } = await render( - - - - - + + + + + + + ); - expect(screen.getByText('2 rtl')).toBeOnTheScreen(); + expect(screen.getByText('2 rtl true')).toBeOnTheScreen(); await rerender( - - - - - + + + + + + + ); - expect(screen.getByText('3 ltr')).toBeOnTheScreen(); - expect(screen.queryByText('2 rtl')).not.toBeOnTheScreen(); + expect(screen.getByText('3 ltr false')).toBeOnTheScreen(); + expect(screen.queryByText('2 rtl true')).not.toBeOnTheScreen(); }); -const PortalReduceMotionContent = () => ( - {`reduce motion: ${useReduceMotion()}`} -); - -it('passes the reduce motion preference to portal content', async () => { - await render( - - - - - - ); - - expect(await screen.findByText('reduce motion: true')).toBeOnTheScreen(); -}); - -it('renders portals in source order when mounted in the same commit', async () => { +it('renders portals in source order when mounted together', async () => { await render( @@ -101,25 +94,141 @@ it('renders portals in source order when mounted in the same commit', async () = expect(portals[2]).toHaveTextContent('third'); }); -it('stacks components mounted in the same commit in source order', async () => { +it('keeps dialog content accessible above a modal when both are visible', async () => { + await render( + + {}}> + modal content + + {}}> + dialog content + + + ); + + expect(screen.getByText('dialog content')).toBeVisible(); + expect( + screen.getByText('modal content', { includeHiddenElements: true }) + ).not.toBeVisible(); +}); + +it('hides the app content from assistive technology while a modal is open', async () => { await render( + page content + + modal content + + + ); + + expect(screen.getByText('modal content')).toBeVisible(); + + const pageContent = screen.getByText('page content', { + includeHiddenElements: true, + }); + + // Still mounted and painted - only hidden from assistive technology. + expect(pageContent).toBeOnTheScreen(); + expect(pageContent).not.toBeVisible(); +}); + +it('leaves the app content reachable for a portal that is not a modal', async () => { + await render( + + page content - {}}> - modal - + portal content + + + ); + + expect(screen.getByText('portal content')).toBeVisible(); + expect(screen.getByText('page content')).toBeVisible(); +}); + +it('keeps portal content accessible above a modal', async () => { + await render( + + + dialog content - {}}> - dialog - + menu content + + + ); + + expect(screen.getByText('menu content')).toBeVisible(); + expect(screen.getByText('dialog content')).toBeVisible(); +}); + +it('hides lower modal content from assistive technology', async () => { + await render( + + + lower dialog + + + upper dialog + + + ); + + expect(screen.getByText('upper dialog')).toBeVisible(); + expect( + screen.getByText('lower dialog', { includeHiddenElements: true }) + ).not.toBeVisible(); +}); + +it('restores access to app content when a portal stops being modal', async () => { + const { rerender } = await render( + + page content + + modal content ); - const layers = await screen.findAllByTestId('layer'); + expect(screen.getByText('modal content')).toBeVisible(); + expect( + screen.getByText('page content', { includeHiddenElements: true }) + ).not.toBeVisible(); + + await rerender( + + page content + + modal content + + + ); + + expect(screen.getByText('page content')).toBeVisible(); +}); + +it('makes the app content reachable again once the modal unmounts', async () => { + const { rerender } = await render( + + page content + + modal content + + + ); + + expect(screen.getByText('modal content')).toBeVisible(); + expect( + screen.getByText('page content', { includeHiddenElements: true }) + ).not.toBeVisible(); + + await rerender( + + page content + + ); - expect(layers).toHaveLength(2); - expect(layers[0]).toHaveTextContent('modal'); - expect(layers[1]).toHaveTextContent('dialog'); + expect(screen.queryByText('modal content')).not.toBeOnTheScreen(); + expect(screen.getByText('page content')).toBeVisible(); }); diff --git a/src/components/__tests__/__snapshots__/Modal.test.tsx.snap b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap index 5b36c6a27a..a3abc0943b 100644 --- a/src/components/__tests__/__snapshots__/Modal.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap @@ -1,4576 +1,5226 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Modal by default should receive appropriate top and bottom insets 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 3`] = ` - +<> + + > + + + + - + `; exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 3`] = ` - +<> + + > + + + + - + `; exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when open if closed via Android back button invokes onDismiss 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when open if closed via Android back button invokes onDismiss 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when open if closed via Android back button invokes onDismiss 3`] = ` - +<> + + > + + + + - + `; exports[`Modal when open runs the closing animation if visible toggled 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when open runs the closing animation if visible toggled 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when open runs the closing animation if visible toggled 3`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 3`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes again during the open/close animation while opening, back to false (hidden) should keep the modal closed 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 2`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 1`] = ` - +<> + + > + + + + - + `; exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 2`] = ` - +<> + + > + + + + - + `; From 179437e66c8510bc571ae0b79931cc1696940e1f Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Mon, 28 Sep 2026 17:39:45 +0200 Subject: [PATCH 80/94] chore: remove now outdated typescript lint script --- .github/workflows/ci.yml | 4 +- scripts/typescript-output-lint.ts | 110 ------------------------------ 2 files changed, 1 insertion(+), 113 deletions(-) delete mode 100644 scripts/typescript-output-lint.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81a3255b83..e3dccbb3d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,9 +77,7 @@ jobs: uses: ./.github/actions/setup - name: Build package - run: | - yarn prepack - node ./scripts/typescript-output-lint.ts + run: yarn prepack build-example: name: Build example diff --git a/scripts/typescript-output-lint.ts b/scripts/typescript-output-lint.ts deleted file mode 100644 index 3781890211..0000000000 --- a/scripts/typescript-output-lint.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { promises as fs } from 'node:fs'; -import { dirname, join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); -const output = join(root, 'lib', 'typescript'); - -/** - * List of React Native props not used by React Native Paper. - * Feel free to delete props from this list when - * React Native Paper has defined/uses one with the same name. - * This script was originally created to detect if TypeScript - * has exported a large union of props from ViewProps. - * More info: https://github.com/callstack/react-native-paper/pull/3603 - */ -const unusedViewProps = [ - 'nativeID', - 'accessibilityActions', - 'accessibilityValue', - 'onAccessibilityAction', - 'accessibilityLabelledBy', - 'accessibilityLiveRegion', - 'accessibilityLanguage', - 'accessibilityViewIsModal', - 'onAccessibilityEscape', - 'onAccessibilityTap', - 'onMagicTap', - 'accessibilityIgnoresInvertColors', - 'removeClippedSubviews', - 'collapsable', - 'needsOffscreenAlphaCompositing', - 'renderToHardwareTextureAndroid', - 'shouldRasterizeIOS', - 'isTVSelectable', - 'hasTVPreferredFocus', - 'tvParallaxProperties', - 'tvParallaxShiftDistanceX', - 'tvParallaxShiftDistanceY', - 'tvParallaxTiltAngle', - 'tvParallaxMagnification', - 'onStartShouldSetResponder', - 'onMoveShouldSetResponder', - 'onResponderEnd', - 'onResponderGrant', - 'onResponderReject', - 'onResponderMove', - 'onResponderRelease', - 'onResponderStart', - 'onResponderTerminationRequest', - 'onResponderTerminate', - 'onStartShouldSetResponderCapture', - 'onMoveShouldSetResponderCapture', - 'onTouchStart', - 'onTouchMove', - 'onTouchEnd', - 'onTouchCancel', - 'onTouchEndCapture', - 'onPointerEnter', - 'onPointerEnterCapture', - 'onPointerLeave', - 'onPointerLeaveCapture', - 'onPointerMove', - 'onPointerMoveCapture', - 'onPointerCancel', - 'onPointerCancelCapture', - 'onPointerDown', - 'onPointerDownCapture', - 'onPointerUp', - 'onPointerUpCapture', - 'cancelable', - 'delayHoverIn', - 'delayHoverOut', - 'pressRetentionOffset', - 'android_disableSound', - 'android_ripple', - 'testOnly_pressed', - 'unstable_pressDelay', -]; - -async function* getFiles(directory: string): AsyncGenerator { - const entries = await fs.readdir(directory, { withFileTypes: true }); - for (const entry of entries) { - const res = resolve(directory, entry.name); - if (entry.isDirectory()) { - yield* getFiles(res); - } else { - yield res; - } - } -} - -async function main() { - for await (const file of getFiles(output)) { - const content = await fs.readFile(file); - for (const prop of unusedViewProps) { - if (content.includes(prop)) { - throw new Error( - `Found text '${prop}' in '${file}'. Please use the wrapped 'forwardRef' in 'src/utils/forwardRef.ts', export some return types, or modify 'scripts/typescript-output-lint.ts'` - ); - } - } - } - - console.log('✅ No React Native props mentioned in TypeScript files'); -} - -main().catch((reason) => { - console.error(reason); - process.exit(1); -}); From 4201235a79c5c645f5668cbd6ccc04372e54677f Mon Sep 17 00:00:00 2001 From: konstmar Date: Mon, 28 Sep 2026 17:41:21 +0200 Subject: [PATCH 81/94] fix(modal): make Modal accessible (#5125) Co-authored-by: Konstantin Marushchak Co-authored-by: Satyajit Sahoo --- docs/6.x/docs/guides/migration.md | 56 +- example/src/Examples/DialogExample.tsx | 14 + .../DialogWithUndismissableBackButton.tsx | 29 + example/src/Examples/Dialogs/index.tsx | 1 + src/components/Dialog/Dialog.tsx | 38 +- src/components/Dialog/DialogTitle.tsx | 4 + src/components/Dialog/utils.ts | 5 + src/components/Menu/Menu.tsx | 6 +- src/components/Modal.tsx | 59 +- src/components/__tests__/Dialog.test.tsx | 81 +- src/components/__tests__/Modal.test.tsx | 1193 ++++++++++------- .../__snapshots__/Modal.test.tsx.snap | 1090 ++++++++++++--- 12 files changed, 1852 insertions(+), 724 deletions(-) create mode 100644 example/src/Examples/Dialogs/DialogWithUndismissableBackButton.tsx diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index 054288fec5..9b73d648ed 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -195,9 +195,35 @@ e.g.:
    ``` +The modal content now has the `dialog` role, so it needs an accessible name. You can provide one with the new `aria-label` prop: + +```diff +- ++ + Content + +``` + +The overlay behind the content is now hidden from assistive technology. Instead, when the modal is `dismissable`, screen reader users can dismiss it with a visually hidden button inside the dialog. + +The `overlayAccessibilityLabel` prop was renamed to `dismissAccessibilityLabel`, which is used for the button's accessibility label: + +```diff + + Content + +``` + +Previously, the Android back button dismissed the modal when `dismissable` was `true`, even if `dismissableBackButton` was `false`. The `dismissableBackButton` prop can now prevent the modal from being dismissed via the back button independently of the `dismissable` prop. + ### Dialog -`Dialog` now uses a `Modal` internally and doesn't require an explicit `Portal` wrapper. So you need to remove any existing `Portal` wrappers around `Dialog`: +`Dialog` now uses a `Portal` internally and doesn't require an explicit `Portal` wrapper. So you need to remove any existing `Portal` wrappers around `Dialog`: ```diff - @@ -210,9 +236,37 @@ e.g.: + ``` +The dialog now has the `dialog` role. On web, the dialog's accessible name is set automatically by `Dialog.Title`. You can specify a different name with the new `aria-label` prop, e.g. when the dialog has no title: + +```jsx + + + Are you sure? + + +``` + +When the dialog is `dismissable`, screen reader users can dismiss it with a visually hidden button inside the dialog. You can change the button's accessibility label with the new `dismissAccessibilityLabel` prop. + - The default elevation changed from level `1` to level `3`. - The `style` prop no longer configures the background color or border radius. You can override `theme.colors.surfaceContainerHigh` and `theme.shapes.corner.extraLarge` using the `theme` prop instead. +### Menu + +The `overlayAccessibilityLabel` prop was renamed to `dismissAccessibilityLabel`: + +```diff + + + +``` + ### Searchbar The misspelled `traileringIcon` props have been renamed: diff --git a/example/src/Examples/DialogExample.tsx b/example/src/Examples/DialogExample.tsx index d926ae6616..b12cbb5c84 100644 --- a/example/src/Examples/DialogExample.tsx +++ b/example/src/Examples/DialogExample.tsx @@ -10,6 +10,7 @@ import { DialogWithLoadingIndicator, DialogWithLongText, DialogWithRadioBtns, + DialogWithUndismissableBackButton, UndismissableDialog, } from './Dialogs'; import ScreenWrapper from '../ScreenWrapper'; @@ -79,6 +80,15 @@ const DialogExample = () => { Dismissable back button )} + {Platform.OS === 'android' && ( + + )} { visible={_getVisible('dialog7')} close={_toggleDialog('dialog7')} /> + ); }; diff --git a/example/src/Examples/Dialogs/DialogWithUndismissableBackButton.tsx b/example/src/Examples/Dialogs/DialogWithUndismissableBackButton.tsx new file mode 100644 index 0000000000..d775ba011c --- /dev/null +++ b/example/src/Examples/Dialogs/DialogWithUndismissableBackButton.tsx @@ -0,0 +1,29 @@ +import { Button, Dialog, Palette } from 'react-native-paper'; + +import { TextComponent } from './DialogTextComponent'; + +const DialogWithUndismissableBackButton = ({ + visible, + close, +}: { + visible: boolean; + close: () => void; +}) => ( + + Alert + + + This dialog can be dismissed by tapping outside, however the hardware + back button will not close it! + + + + + + + +); + +export default DialogWithUndismissableBackButton; diff --git a/example/src/Examples/Dialogs/index.tsx b/example/src/Examples/Dialogs/index.tsx index 7af735d036..5ae48db11f 100644 --- a/example/src/Examples/Dialogs/index.tsx +++ b/example/src/Examples/Dialogs/index.tsx @@ -5,3 +5,4 @@ export { default as DialogWithRadioBtns } from './DialogWithRadioBtns'; export { default as UndismissableDialog } from './UndismissableDialog'; export { default as DialogWithIcon } from './DialogWithIcon'; export { default as DialogWithDismissableBackButton } from './DialogWithDismissableBackButton'; +export { default as DialogWithUndismissableBackButton } from './DialogWithUndismissableBackButton'; diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 9c75d4c08a..5b273151ec 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -13,6 +13,7 @@ import { useInternalTheme } from '../../core/theming'; import type { Elevation, ThemeProp } from '../../theme/types'; import Modal from '../Modal'; import type { SurfaceStyle } from '../Surface'; +import { DialogTitleIdContext } from './utils'; import type { DialogChildProps } from './utils'; export type Props = { @@ -28,6 +29,14 @@ export type Props = { * Callback that is called when the user dismisses the dialog. */ onDismiss?: () => void; + /** + * Accessibility label for dismissing the dialog if it's `dismissable`. + */ + dismissAccessibilityLabel?: string; + /** + * Accessible name for the dialog. On web, defaults to the text of `Dialog.Title`. + */ + 'aria-label'?: string; /** * Determines Whether the dialog is visible. */ @@ -95,6 +104,8 @@ const Dialog = ({ dismissable = true, dismissableBackButton = dismissable, onDismiss, + dismissAccessibilityLabel, + 'aria-label': ariaLabel, visible = false, style, theme: themeOverrides, @@ -108,11 +119,16 @@ const Dialog = ({ const backgroundColor = theme.colors.surfaceContainerHigh; + const titleId = React.useId(); + return ( - {React.Children.toArray(children) - .filter((child) => child != null && typeof child !== 'boolean') - .map((child, i) => { - if (i === 0 && React.isValidElement(child)) { - return React.cloneElement(child, { - style: [{ marginTop: 24 }, child.props.style], - }); - } + + {React.Children.toArray(children) + .filter((child) => child != null && typeof child !== 'boolean') + .map((child, i) => { + if (i === 0 && React.isValidElement(child)) { + return React.cloneElement(child, { + style: [{ marginTop: 24 }, child.props.style], + }); + } - return child; - })} + return child; + })} + ); }; diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index af36209cdc..4dd8bf8f5c 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; import type { StyleProp, TextStyle } from 'react-native'; +import { DialogTitleIdContext } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../theme/types'; import Text from '../Typography/Text'; @@ -48,10 +49,12 @@ const DialogTitle = ({ children, theme: themeOverrides, style, + nativeID, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); const { colors, fonts } = theme; + const titleId = React.useContext(DialogTitleIdContext); const headerTextStyle = { color: colors.onSurface, @@ -62,6 +65,7 @@ const DialogTitle = ({ diff --git a/src/components/Dialog/utils.ts b/src/components/Dialog/utils.ts index 64ccec77cc..40ca761321 100644 --- a/src/components/Dialog/utils.ts +++ b/src/components/Dialog/utils.ts @@ -1,5 +1,10 @@ +import * as React from 'react'; import type { StyleProp, ViewStyle } from 'react-native'; +export const DialogTitleIdContext = React.createContext( + undefined +); + export type DialogChildProps = { style?: StyleProp; }; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 96dd4096b7..6044956c06 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -67,7 +67,7 @@ export type Props = { /** * Accessibility label for the overlay. This is read by the screen reader when the user taps outside the menu. */ - overlayAccessibilityLabel?: string; + dismissAccessibilityLabel?: string; /** * testID for the overlay that is displayed behind the menu. */ @@ -189,7 +189,7 @@ const isCoordinate = (anchor: any): anchor is { x: number; y: number } => const Menu = ({ visible, statusBarHeight, - overlayAccessibilityLabel = 'Close menu', + dismissAccessibilityLabel = 'Close menu', overlayTestID, testID, anchor, @@ -681,7 +681,7 @@ const Menu = ({ {rendered ? ( void; /** - * Accessibility label for the overlay. This is read by the screen reader when the user taps outside the modal. + * Accessibility label for dismissing the modal if it's `dismissable`. */ - overlayAccessibilityLabel?: string; + dismissAccessibilityLabel?: string; + /** + * Accessible name for the modal. + */ + 'aria-label'?: string; + /** + * `nativeID` of the element which provides the accessible name for the modal, + * such as a title. Supported on web. + */ + 'aria-labelledby'?: string; /** * testID for the overlay that is displayed behind the modal content. */ @@ -89,6 +98,7 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); /** * The Modal component is a simple way to present content above an enclosing view. + * Give the modal an accessible name with `aria-label`. * * ## Usage * ```js @@ -108,6 +118,7 @@ const AnimatedPressable = Animated.createAnimatedComponent(Pressable); * @@ -127,7 +138,9 @@ function Modal({ dismissable = true, dismissableBackButton = dismissable, visible = false, - overlayAccessibilityLabel = 'Close modal', + dismissAccessibilityLabel = 'Close modal', + 'aria-label': ariaLabel, + 'aria-labelledby': ariaLabelledBy, overlayTestID, onDismiss = () => {}, children, @@ -179,7 +192,7 @@ function Modal({ } const onHardwareBackPress = () => { - if (dismissable || dismissableBackButton) { + if (dismissableBackButton) { onDismissCallback(); } @@ -193,7 +206,7 @@ function Modal({ ); return () => subscription.remove(); - }, [dismissable, dismissableBackButton, onDismissCallback, visible]); + }, [dismissableBackButton, onDismissCallback, visible]); const transitionTimingFunction = cubicBezier(1 / 3, 1, 2 / 3, 1); @@ -225,18 +238,17 @@ function Modal({ @@ -249,6 +261,15 @@ function Modal({ pointerEvents="box-none" > {children} + {dismissable ? ( + // The backdrop is hidden for screen reader users, + // so we provide a visually hidden dismiss button. + + ) : null} @@ -282,4 +314,9 @@ const styles = StyleSheet.create({ content: { justifyContent: 'center', }, + dismiss: { + position: 'absolute', + width: 1, + height: 1, + }, }); diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 129cf596f2..368bd9727f 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -6,7 +6,7 @@ import { } from 'react-native'; import type { BackHandlerStatic as RNBackHandlerStatic } from 'react-native'; -import { describe, expect, it, jest } from '@jest/globals'; +import { afterEach, describe, expect, it, jest } from '@jest/globals'; import { act, userEvent } from '@testing-library/react-native'; import Dialog from '../../components/Dialog/Dialog'; @@ -21,8 +21,12 @@ interface BackHandlerStatic extends RNBackHandlerStatic { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const BackHandler = RNBackHandler as BackHandlerStatic; +afterEach(() => { + jest.restoreAllMocks(); +}); + describe('Dialog', () => { - it('should render passed children', async () => { + it('renders passed children', async () => { await render( @@ -36,82 +40,94 @@ describe('Dialog', () => { ); }); - it('should call onDismiss when dismissable', async () => { + it('invokes onDismiss when the visually hidden dismiss button is pressed', async () => { const onDismiss = jest.fn(); + await render( - + This is simple dialog ); - await userEvent.press(screen.getByLabelText('Close modal')); + await userEvent.press(screen.getByRole('button', { name: 'Close modal' })); await act(() => { jest.runAllTimers(); }); + expect(onDismiss).toHaveBeenCalledTimes(1); }); - it('should not call onDismiss when dismissable is false', async () => { + it('does not invoke onDismiss for a non-dismissible dialog when the backdrop is pressed', async () => { const onDismiss = jest.fn(); + await render( This is simple dialog ); - await userEvent.press(screen.getByLabelText('Close modal')); + await userEvent.press( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ); await act(() => { jest.runAllTimers(); }); + expect(onDismiss).toHaveBeenCalledTimes(0); }); - it('should call onDismiss on Android back button when dismissable is false but dismissableBackButton is true', async () => { - Platform.OS = 'android'; + it('invokes onDismiss on Android back button press when only dismissableBackButton is true', async () => { + jest.replaceProperty(Platform, 'OS', 'android'); + const onDismiss = jest.fn(); + await render( This is simple dialog ); - await userEvent.press(screen.getByLabelText('Close modal')); + await userEvent.press( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ); await act(() => { jest.runAllTimers(); }); + expect(onDismiss).toHaveBeenCalledTimes(0); await act(() => { BackHandler.mockPressBack(); jest.runAllTimers(); }); + expect(onDismiss).toHaveBeenCalledTimes(1); }); - it('should apply top margin to the first child if the dialog is V3', async () => { + it('applies top margin to the first child', async () => { await render( - + Test Dialog Content @@ -123,10 +139,41 @@ describe('Dialog', () => { marginTop: 24, }); }); + + it('uses the title as the accessible name on web', async () => { + jest.replaceProperty(Platform, 'OS', 'web'); + + await render( + + + Alert + This is simple dialog + + + ); + + expect(screen.getByLabelText('Alert')).toHaveProp('role', 'dialog'); + }); + + it('uses aria-label over the title as the accessible name', async () => { + await render( + + + Alert + + + ); + + expect(screen.getByLabelText('Confirm deletion')).toHaveProp( + 'role', + 'dialog' + ); + expect(screen.queryByLabelText('Alert')).not.toBeOnTheScreen(); + }); }); describe('DialogActions', () => { - it('should render passed children', async () => { + it('renders passed children', async () => { await render( @@ -138,7 +185,7 @@ describe('DialogActions', () => { expect(screen.getByTestId('button-ok')).toBeOnTheScreen(); }); - it('should apply default styles', async () => { + it('applies default styles', async () => { await render( @@ -157,7 +204,7 @@ describe('DialogActions', () => { expect(dialogActionButtons[1]).toHaveStyle({ marginRight: 0 }); }); - it('should apply custom styles', async () => { + it('applies custom styles', async () => { await render( diff --git a/src/components/__tests__/Modal.test.tsx b/src/components/__tests__/Modal.test.tsx index 84c9087bd5..6a071893c2 100644 --- a/src/components/__tests__/Modal.test.tsx +++ b/src/components/__tests__/Modal.test.tsx @@ -2,7 +2,7 @@ import { BackHandler as RNBackHandler, Text } from 'react-native'; import type { BackHandlerStatic as RNBackHandlerStatic } from 'react-native'; import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'; -import { act, userEvent } from '@testing-library/react-native'; +import { act, fireEvent, userEvent } from '@testing-library/react-native'; import { render, screen } from '../../test-utils'; import { LightTheme } from '../../theme/schemes'; @@ -40,539 +40,744 @@ describe('Modal', () => { /* eslint-enable @typescript-eslint/no-unsafe-type-assertion */ }); - describe('by default', () => { - it('should render passed children', async () => { - await render( - - - Children - - - ); - - expect(screen.getByTestId('modal')).toHaveTextContent('Children'); - }); - - it("should render a backdrop in default theme's color", async () => { - await render( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - backgroundColor: LightTheme.colors.scrim, - }); - }); - - it('should render a custom backdrop color if specified', async () => { - await render( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - backgroundColor: 'transparent', - }); - }); - - it('should receive appropriate top and bottom insets', async () => { - const { toJSON } = await render( - - - {null} - - - ); - - expect(toJSON()).toMatchSnapshot(); + it('renders passed children', async () => { + await render( + + + Children + + + ); + + expect(screen.getByTestId('modal')).toHaveTextContent('Children'); + }); + + it("renders a backdrop in default theme's color", async () => { + await render( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + backgroundColor: LightTheme.colors.scrim, + }); + }); + + it('renders a custom backdrop color if specified', async () => { + await render( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + backgroundColor: 'transparent', }); }); - describe('when open', () => { - describe('if backdrop touched', () => { - it('should invoke the onDismiss function immediately', async () => { - const onDismiss = jest.fn(); - const { toJSON } = await render( - - - {null} - - - ); - expect(onDismiss).not.toHaveBeenCalled(); + it('receives appropriate top and bottom insets', async () => { + const { toJSON } = await render( + + + {null} + + + ); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('exposes the modal as a dialog with an accessible name', async () => { + await render( + + + Modal content + + + ); + + // Role queries only match accessibility elements, and the dialog + // container must not be one or it would swallow its children on iOS. + expect(screen.getByLabelText('Example modal')).toHaveProp('role', 'dialog'); + }); + + it('hides the backdrop from assistive technology', async () => { + await render( + + + {null} + + + ); + + expect(screen.queryByTestId('backdrop')).not.toBeOnTheScreen(); + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toBeOnTheScreen(); + }); - await userEvent.press(screen.getByLabelText('Close modal')); + it('invokes onDismiss immediately when the backdrop is pressed', async () => { + const onDismiss = jest.fn(); - expect(onDismiss).toHaveBeenCalled(); + const { toJSON } = await render( + + + {null} + + + ); - expect(toJSON()).toMatchSnapshot(); + expect(onDismiss).not.toHaveBeenCalled(); - await act(() => { - jest.runAllTimers(); - }); + await userEvent.press( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ); - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); + expect(onDismiss).toHaveBeenCalled(); - expect(toJSON()).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); - expect(onDismiss).toHaveBeenCalledTimes(1); - }); + await act(() => { + jest.runAllTimers(); }); - it('runs the closing animation if visible toggled', async () => { - const { rerender, toJSON } = await render( - - {}}> - {null} - - - ); + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); - expect(toJSON()).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); - await userEvent.press(screen.getByLabelText('Close modal')); + expect(onDismiss).toHaveBeenCalledTimes(1); + }); - await rerender( - - {}}> - {null} - - - ); + it('runs the closing animation when visible changes to false after dismissing', async () => { + const { rerender, toJSON } = await render( + + {}} + > + {null} + + + ); + + expect(toJSON()).toMatchSnapshot(); + + await userEvent.press(screen.getByRole('button', { name: 'Close modal' })); + + await rerender( + + {}} + > + {null} + + + ); + + expect(toJSON()).toMatchSnapshot(); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); - expect(toJSON()).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); + await act(() => { + jest.runAllTimers(); + }); - expect(toJSON()).toMatchSnapshot(); + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); + }); - await act(() => { - jest.runAllTimers(); - }); + it('invokes onDismiss when the visually hidden dismiss button is pressed', async () => { + const onDismiss = jest.fn(); + + await render( + + + {null} + + + ); + + await userEvent.press( + screen.getByRole('button', { name: 'Dismiss dialog' }) + ); + + expect(onDismiss).toHaveBeenCalledTimes(1); + }); - expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); + it('invokes onDismiss on the accessibility escape gesture', async () => { + const onDismiss = jest.fn(); + + await render( + + + {null} + + + ); + + await fireEvent(screen.getByTestId('modal'), 'accessibilityEscape'); + + expect(onDismiss).toHaveBeenCalledTimes(1); + }); + + it('invokes onDismiss when the Android back button is pressed', async () => { + const onDismiss = jest.fn(); + + const { toJSON } = await render( + + + {null} + + + ); + + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + BackHandler.mockPressBack(); }); - describe('if closed via Android back button', () => { - it('invokes onDismiss', async () => { - const onDismiss = jest.fn(); - const { toJSON } = await render( - - - {null} - - - ); + expect(toJSON()).toMatchSnapshot(); - expect(toJSON()).toMatchSnapshot(); + await act(() => { + jest.runAllTimers(); + }); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); - await act(() => { - BackHandler.mockPressBack(); - }); + expect(toJSON()).toMatchSnapshot(); - expect(toJSON()).toMatchSnapshot(); + expect(onDismiss).toHaveBeenCalledTimes(1); + }); - await act(() => { - jest.runAllTimers(); - }); + it('does not invoke onDismiss on Android back button press when dismissableBackButton is false', async () => { + const onDismiss = jest.fn(); + + await render( + + + {null} + + + ); + + await act(() => { + BackHandler.mockPressBack(); + jest.runAllTimers(); + }); - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); + expect(onDismiss).not.toHaveBeenCalled(); - expect(toJSON()).toMatchSnapshot(); + await userEvent.press(screen.getByRole('button', { name: 'Close modal' })); - expect(onDismiss).toHaveBeenCalledTimes(1); - }); + await act(() => { + jest.runAllTimers(); }); + + expect(onDismiss).toHaveBeenCalledTimes(1); }); - describe('when open as non-dismissible modal', () => { - describe('if closed via touching backdrop', () => { - it('will run the animation but not fade out', async () => { - const { toJSON } = await render( - - {}} - dismissable={false} - > - {null} - - - ); - - expect(toJSON()).toMatchSnapshot(); - - await userEvent.press(screen.getByLabelText('Close modal')); - - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('should not invoke onDismiss', async () => { - const onDismiss = jest.fn(); - await render( - - - {null} - - - ); - - expect(onDismiss).not.toHaveBeenCalled(); - - await userEvent.press(screen.getByLabelText('Close modal')); - - expect(onDismiss).not.toHaveBeenCalled(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(onDismiss).not.toHaveBeenCalled(); - }); - }); - - describe('if closed via Android back button', () => { - it('will run the animation but not fade out', async () => { - const { toJSON } = await render( - - {}} - dismissable={false} - > - {null} - - - ); - - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - BackHandler.mockPressBack(); - }); - - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - - expect(toJSON()).toMatchSnapshot(); - }); - - it('should not invoke onDismiss', async () => { - const onDismiss = jest.fn(); - - await render( - - - {null} - - - ); - - expect(onDismiss).not.toHaveBeenCalled(); + it('does not render the visually hidden dismiss button for a non-dismissible modal', async () => { + await render( + + {}}> + {null} + + + ); + + expect( + screen.queryByRole('button', { name: 'Close modal' }) + ).not.toBeOnTheScreen(); + }); - await act(() => { - BackHandler.mockPressBack(); - }); + it('keeps a non-dismissible modal visible when the backdrop is pressed', async () => { + const { toJSON } = await render( + + {}} + dismissable={false} + > + {null} + + + ); + + expect(toJSON()).toMatchSnapshot(); + + await userEvent.press( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ); + + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + jest.runAllTimers(); + }); - expect(onDismiss).not.toHaveBeenCalled(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(onDismiss).not.toHaveBeenCalled(); - }); + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, }); + + expect(toJSON()).toMatchSnapshot(); }); - describe('when visible prop changes', () => { - describe('from false to true (closed to open)', () => { - it('should run fade-in animation on opening', async () => { - const { rerender, toJSON } = await render( - - - {null} - - - ); - - expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); - - await rerender( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: 0, - }); - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - }); - }); - - describe('from true to false (open to closed)', () => { - it('should run fade-out animation on closing', async () => { - const { rerender, toJSON } = await render( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - - await rerender( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); - }); - - it('should not invoke onDismiss', async () => { - const onDismiss = jest.fn(); - - const { rerender } = await render( - - - {null} - - - ); - - expect(onDismiss).not.toHaveBeenCalled(); - - await rerender( - - - {null} - - - ); - - expect(onDismiss).not.toHaveBeenCalled(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(onDismiss).not.toHaveBeenCalled(); - }); - - it('should close even if the dialog is not dismissible', async () => { - const { rerender, toJSON } = await render( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - - await rerender( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); - }); + it('does not invoke onDismiss for a non-dismissible modal when the backdrop is pressed', async () => { + const onDismiss = jest.fn(); + + await render( + + + {null} + + + ); + + expect(onDismiss).not.toHaveBeenCalled(); + + await userEvent.press( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ); + + expect(onDismiss).not.toHaveBeenCalled(); + + await act(() => { + jest.runAllTimers(); }); + + expect(onDismiss).not.toHaveBeenCalled(); + }); + + it('keeps a non-dismissible modal on screen on the accessibility escape gesture', async () => { + const onDismiss = jest.fn(); + + await render( + + + {null} + + + ); + + await fireEvent(screen.getByTestId('modal'), 'accessibilityEscape'); + + expect(onDismiss).not.toHaveBeenCalled(); + expect(screen.getByTestId('modal')).toBeOnTheScreen(); }); - describe('when visible prop changes again during the open/close animation', () => { - describe('while closing, back to true (visible)', () => { - it('should keep the modal open', async () => { - const { rerender, toJSON } = await render( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - - await rerender( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - // Not a real seconds, this depends on how frequently - // requestAnimationFrame is called - jest.advanceTimersToNextTimer(1000); - }); - - await rerender( - - - {null} - - - ); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: scrimAlpha, - }); - expect(toJSON()).toMatchSnapshot(); - }); - }); - - describe('while opening, back to false (hidden)', () => { - it('should keep the modal closed', async () => { - const { rerender, toJSON } = await render( - - - {null} - - - ); - - expect(screen.queryByLabelText('Close modal')).not.toBeOnTheScreen(); - - await rerender( - - - {null} - - - ); - - expect(screen.getByLabelText('Close modal')).toHaveStyle({ - opacity: 0, - }); - expect(toJSON()).toMatchSnapshot(); - - await act(() => { - // Not a real seconds, this depends on how frequently - // requestAnimationFrame is called - jest.advanceTimersToNextTimer(1000); - }); - - expect(screen.getByLabelText('Close modal')).toBeOnTheScreen(); - - await rerender( - - - {null} - - - ); - - await act(() => { - jest.runAllTimers(); - }); - - expect(screen.queryByLabelText('Close modal')).not.toBeOnTheScreen(); - }); + it('keeps a non-dismissible modal visible when the Android back button is pressed', async () => { + const { toJSON } = await render( + + {}} + dismissable={false} + > + {null} + + + ); + + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + BackHandler.mockPressBack(); + }); + + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + jest.runAllTimers(); }); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('does not invoke onDismiss for a non-dismissible modal when the Android back button is pressed', async () => { + const onDismiss = jest.fn(); + + await render( + + + {null} + + + ); + + expect(onDismiss).not.toHaveBeenCalled(); + + await act(() => { + BackHandler.mockPressBack(); + }); + + expect(onDismiss).not.toHaveBeenCalled(); + + await act(() => { + jest.runAllTimers(); + }); + + expect(onDismiss).not.toHaveBeenCalled(); + }); + + it('runs the fade-in animation when visible changes from false to true', async () => { + const { rerender, toJSON } = await render( + + + {null} + + + ); + + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); + + await rerender( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: 0, + }); + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + jest.runAllTimers(); + }); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + }); + + it('runs the fade-out animation when visible changes from true to false', async () => { + const { rerender, toJSON } = await render( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + + await rerender( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + jest.runAllTimers(); + }); + + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); + }); + + it('does not invoke onDismiss when visible changes from true to false', async () => { + const onDismiss = jest.fn(); + + const { rerender } = await render( + + + {null} + + + ); + + expect(onDismiss).not.toHaveBeenCalled(); + + await rerender( + + + {null} + + + ); + + expect(onDismiss).not.toHaveBeenCalled(); + + await act(() => { + jest.runAllTimers(); + }); + + expect(onDismiss).not.toHaveBeenCalled(); + }); + + it('closes a non-dismissible modal when visible changes from true to false', async () => { + const { rerender, toJSON } = await render( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + + await rerender( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + jest.runAllTimers(); + }); + + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); + }); + + it('keeps the modal open when visible changes back to true while closing', async () => { + const { rerender, toJSON } = await render( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + + await rerender( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + // Not a real seconds, this depends on how frequently + // requestAnimationFrame is called + jest.advanceTimersToNextTimer(1000); + }); + + await rerender( + + + {null} + + + ); + + await act(() => { + jest.runAllTimers(); + }); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: scrimAlpha, + }); + expect(toJSON()).toMatchSnapshot(); + }); + + it('keeps the modal closed when visible changes back to false while opening', async () => { + const { rerender, toJSON } = await render( + + + {null} + + + ); + + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); + + await rerender( + + + {null} + + + ); + + expect( + screen.getByTestId('backdrop', { includeHiddenElements: true }) + ).toHaveStyle({ + opacity: 0, + }); + expect(toJSON()).toMatchSnapshot(); + + await act(() => { + // Not a real seconds, this depends on how frequently + // requestAnimationFrame is called + jest.advanceTimersToNextTimer(1000); + }); + + expect(screen.getByTestId('modal')).toBeOnTheScreen(); + + await rerender( + + + {null} + + + ); + + await act(() => { + jest.runAllTimers(); + }); + + expect(screen.queryByTestId('modal')).not.toBeOnTheScreen(); }); }); diff --git a/src/components/__tests__/__snapshots__/Modal.test.tsx.snap b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap index a3abc0943b..8cf23e1346 100644 --- a/src/components/__tests__/__snapshots__/Modal.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/Modal.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Modal by default should receive appropriate top and bottom insets 1`] = ` +exports[`Modal closes a non-dismissible modal when visible changes from true to false 1`] = ` <> `; -exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 1`] = ` +exports[`Modal closes a non-dismissible modal when visible changes from true to false 2`] = ` <> `; -exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 2`] = ` +exports[`Modal invokes onDismiss immediately when the backdrop is pressed 1`] = ` <> + @@ -627,7 +669,7 @@ exports[`Modal when open as non-dismissible modal if closed via Android back but `; -exports[`Modal when open as non-dismissible modal if closed via Android back button will run the animation but not fade out 3`] = ` +exports[`Modal invokes onDismiss immediately when the backdrop is pressed 2`] = ` <> + @@ -836,7 +920,7 @@ exports[`Modal when open as non-dismissible modal if closed via Android back but `; -exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 1`] = ` +exports[`Modal invokes onDismiss when the Android back button is pressed 1`] = ` <> + @@ -1045,7 +1171,7 @@ exports[`Modal when open as non-dismissible modal if closed via touching backdro `; -exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 2`] = ` +exports[`Modal invokes onDismiss when the Android back button is pressed 2`] = ` <> + @@ -1254,7 +1422,7 @@ exports[`Modal when open as non-dismissible modal if closed via touching backdro `; -exports[`Modal when open as non-dismissible modal if closed via touching backdrop will run the animation but not fade out 3`] = ` +exports[`Modal invokes onDismiss when the Android back button is pressed 3`] = ` <> + @@ -1463,7 +1673,7 @@ exports[`Modal when open as non-dismissible modal if closed via touching backdro `; -exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 1`] = ` +exports[`Modal keeps a non-dismissible modal visible when the Android back button is pressed 1`] = ` <> `; -exports[`Modal when open if backdrop touched should invoke the onDismiss function immediately 2`] = ` +exports[`Modal keeps a non-dismissible modal visible when the Android back button is pressed 2`] = ` <> `; -exports[`Modal when open if closed via Android back button invokes onDismiss 1`] = ` +exports[`Modal keeps a non-dismissible modal visible when the Android back button is pressed 3`] = ` <> `; -exports[`Modal when open if closed via Android back button invokes onDismiss 2`] = ` +exports[`Modal keeps a non-dismissible modal visible when the backdrop is pressed 1`] = ` <> `; -exports[`Modal when open if closed via Android back button invokes onDismiss 3`] = ` +exports[`Modal keeps a non-dismissible modal visible when the backdrop is pressed 2`] = ` <> `; -exports[`Modal when open runs the closing animation if visible toggled 1`] = ` +exports[`Modal keeps a non-dismissible modal visible when the backdrop is pressed 3`] = ` <> `; -exports[`Modal when open runs the closing animation if visible toggled 2`] = ` +exports[`Modal keeps the modal closed when visible changes back to false while opening 1`] = ` <> + @@ -2926,7 +3178,7 @@ exports[`Modal when open runs the closing animation if visible toggled 2`] = ` `; -exports[`Modal when open runs the closing animation if visible toggled 3`] = ` +exports[`Modal keeps the modal open when visible changes back to true while closing 1`] = ` <> + @@ -3135,7 +3429,7 @@ exports[`Modal when open runs the closing animation if visible toggled 3`] = ` `; -exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 1`] = ` +exports[`Modal keeps the modal open when visible changes back to true while closing 2`] = ` <> + @@ -3344,7 +3680,7 @@ exports[`Modal when visible prop changes again during the open/close animation w `; -exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 2`] = ` +exports[`Modal keeps the modal open when visible changes back to true while closing 3`] = ` <> + @@ -3553,7 +3931,7 @@ exports[`Modal when visible prop changes again during the open/close animation w `; -exports[`Modal when visible prop changes again during the open/close animation while closing, back to true (visible) should keep the modal open 3`] = ` +exports[`Modal receives appropriate top and bottom insets 1`] = ` <> + @@ -3762,7 +4182,7 @@ exports[`Modal when visible prop changes again during the open/close animation w `; -exports[`Modal when visible prop changes again during the open/close animation while opening, back to false (hidden) should keep the modal closed 1`] = ` +exports[`Modal runs the closing animation when visible changes to false after dismissing 1`] = ` <> + @@ -3971,7 +4433,7 @@ exports[`Modal when visible prop changes again during the open/close animation w `; -exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 1`] = ` +exports[`Modal runs the closing animation when visible changes to false after dismissing 2`] = ` <> + @@ -4180,7 +4684,7 @@ exports[`Modal when visible prop changes from false to true (closed to open) sho `; -exports[`Modal when visible prop changes from false to true (closed to open) should run fade-in animation on opening 2`] = ` +exports[`Modal runs the closing animation when visible changes to false after dismissing 3`] = ` <> + @@ -4389,7 +4935,7 @@ exports[`Modal when visible prop changes from false to true (closed to open) sho `; -exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 1`] = ` +exports[`Modal runs the fade-in animation when visible changes from false to true 1`] = ` <> + @@ -4598,7 +5186,7 @@ exports[`Modal when visible prop changes from true to false (open to closed) sho `; -exports[`Modal when visible prop changes from true to false (open to closed) should close even if the dialog is not dismissible 2`] = ` +exports[`Modal runs the fade-in animation when visible changes from false to true 2`] = ` <> + @@ -4807,7 +5437,7 @@ exports[`Modal when visible prop changes from true to false (open to closed) sho `; -exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 1`] = ` +exports[`Modal runs the fade-out animation when visible changes from true to false 1`] = ` <> + @@ -5016,7 +5688,7 @@ exports[`Modal when visible prop changes from true to false (open to closed) sho `; -exports[`Modal when visible prop changes from true to false (open to closed) should run fade-out animation on closing 2`] = ` +exports[`Modal runs the fade-out animation when visible changes from true to false 2`] = ` <> + From 4ffc20ae4ac463cff70f50e22bc627531ec59467 Mon Sep 17 00:00:00 2001 From: alcpereira <48070464+alcpereira@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:03:02 +0100 Subject: [PATCH 82/94] ci: build and upload example app (#5131) --- .github/workflows/build-example-apps.yml | 149 +++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 .github/workflows/build-example-apps.yml diff --git a/.github/workflows/build-example-apps.yml b/.github/workflows/build-example-apps.yml new file mode 100644 index 0000000000..64bc9fc819 --- /dev/null +++ b/.github/workflows/build-example-apps.yml @@ -0,0 +1,149 @@ +name: Build example apps + +on: + push: + branches: + - main + paths: + - '**' + - '!docs/**' + - '!**/*.md' + - '!**/__tests__/**' + - '!jest/**' + - '!.github/workflows/**' + - '.github/workflows/build-example-apps.yml' + pull_request: + paths: + - '**' + - '!docs/**' + - '!**/*.md' + - '!**/__tests__/**' + - '!jest/**' + - '!.github/workflows/**' + - '.github/workflows/build-example-apps.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + build-android: + name: Build Android + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Calculate fingerprint + id: fingerprint + working-directory: example + run: | + fingerprint=$(yarn expo-updates fingerprint:generate --platform android | jq -er .hash) + echo "fingerprint=$fingerprint" >> "$GITHUB_OUTPUT" + + - name: Cache release build + id: cache + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: react-native-paper-example.apk + key: android-${{ steps.fingerprint.outputs.fingerprint }}-${{ hashFiles('.github/workflows/build-example-apps.yml') }} + + - name: Setup JDK + uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + with: + distribution: temurin + java-version: 17 + + - name: Build Android app + if: steps.cache.outputs.cache-hit != 'true' + working-directory: example + run: | + yarn expo prebuild --platform android --no-install + cd android + ./gradlew :app:assembleRelease -Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=1g -PreactNativeArchitectures=arm64-v8a,x86_64 + mv app/build/outputs/apk/release/app-release.apk ../../react-native-paper-example.apk + + - name: Repack cached build + if: steps.cache.outputs.cache-hit == 'true' + working-directory: example + run: | + yarn dlx @expo/repack-app@0.10.3 \ + --platform android \ + --source-app ../react-native-paper-example.apk \ + --output ../react-native-paper-example.apk + + - name: Upload APK + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + path: react-native-paper-example.apk + archive: false + retention-days: 30 + + build-ios: + name: Build iOS + runs-on: macos-latest + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup + uses: ./.github/actions/setup + + - name: Calculate fingerprint + id: fingerprint + working-directory: example + run: | + fingerprint=$(yarn expo-updates fingerprint:generate --platform ios | jq -er .hash) + echo "fingerprint=$fingerprint" >> "$GITHUB_OUTPUT" + + - name: Cache release build + id: cache + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: react-native-paper-example.app + key: ios-${{ steps.fingerprint.outputs.fingerprint }}-${{ hashFiles('.github/workflows/build-example-apps.yml') }} + + - name: Build iOS app + if: steps.cache.outputs.cache-hit != 'true' + working-directory: example + run: | + yarn expo prebuild --platform ios --no-install + cd ios + pod install + xcodebuild build \ + -workspace ReactNativePaperExample.xcworkspace \ + -scheme ReactNativePaperExample \ + -configuration Release \ + -destination 'generic/platform=iOS Simulator' \ + -derivedDataPath build \ + ARCHS=arm64 + mv build/Build/Products/Release-iphonesimulator/ReactNativePaperExample.app ../../react-native-paper-example.app + + - name: Repack cached build + if: steps.cache.outputs.cache-hit == 'true' + working-directory: example + run: | + mv ../react-native-paper-example.app "$RUNNER_TEMP/cached.app" + yarn dlx @expo/repack-app@0.10.3 \ + --platform ios \ + --source-app "$RUNNER_TEMP/cached.app" \ + --output ../react-native-paper-example.app + + - name: Archive app + run: ditto -c -k --keepParent react-native-paper-example.app react-native-paper-example.app.zip + + - name: Upload app + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + path: react-native-paper-example.app.zip + archive: false + retention-days: 30 From ce72155b2c8ce867aced1eedc17b3ca4698b27b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Sajko=20=28sai=20=C2=B7=20ko=29?= Date: Mon, 12 Oct 2026 13:35:30 +0200 Subject: [PATCH 83/94] refactor: replace React.Children usage with context and typed props (#5076) --- docs/6.x/docs/guides/migration.md | 136 ++++++++++++++++++ example/src/Examples/CardExample.tsx | 16 ++- .../SegmentedButtonMultiselectRealCase.tsx | 26 ++-- .../SegmentedButtonRealCase.tsx | 30 ++-- example/src/Examples/TeamDetails.tsx | 16 ++- src/components/Card/Card.tsx | 71 +++++---- src/components/Card/CardActions.tsx | 47 +++--- src/components/Card/CardContent.tsx | 69 +-------- src/components/Card/CardContext.tsx | 16 +++ src/components/Card/CardCover.tsx | 47 +++--- src/components/Card/CardTitle.tsx | 33 +---- src/components/Card/utils.tsx | 4 - src/components/Dialog/Dialog.tsx | 14 +- src/components/Dialog/DialogActions.tsx | 29 ++-- src/components/Dialog/DialogIcon.tsx | 10 +- src/components/Dialog/DialogTitle.tsx | 2 +- src/components/List/ListAccordion.tsx | 32 ++--- src/components/List/ListAccordionContext.tsx | 13 ++ src/components/List/ListItem.tsx | 12 +- src/components/__tests__/Card/Card.test.tsx | 92 +++++++++++- .../Card/__snapshots__/Card.test.tsx.snap | 33 +++++ src/components/__tests__/Dialog.test.tsx | 111 ++++++++++++-- .../__tests__/ListAccordion.test.tsx | 30 +++- src/components/__tests__/Tooltip.test.tsx | 2 +- 24 files changed, 600 insertions(+), 291 deletions(-) create mode 100644 src/components/Card/CardContext.tsx create mode 100644 src/components/List/ListAccordionContext.tsx diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index 9b73d648ed..72aee6d00f 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -251,6 +251,24 @@ When the dialog is `dismissable`, screen reader users can dismiss it with a visu - The default elevation changed from level `1` to level `3`. - The `style` prop no longer configures the background color or border radius. You can override `theme.colors.surfaceContainerHigh` and `theme.shapes.corner.extraLarge` using the `theme` prop instead. +#### `Dialog.Actions` + +`Dialog.Actions` no longer injects `compact` on its buttons. To keep the previous behavior, you need to add the `compact` prop to each button: + +```tsx +// Before (v5) + + + + +// After (v6) + + + +``` + ### Menu The `overlayAccessibilityLabel` prop was renamed to `dismissAccessibilityLabel`: @@ -410,6 +428,124 @@ const theme = { /> ``` +### Card + +#### `Card` layout + +`Card` and its related components have been reworked. Instead of injecting padding to its children, the `Card` component now adds a padding (`16dp`) around its content as well as a gap (`16dp`) between its children. + +For the related components: + +- `Card.Cover` and `Card.Actions` now apply negative margins to extend into the Card's edges: + - `Card.Cover` applies top, left and right margins for vertical cards and left, top, and bottom margins for horizontal cards. + - `Card.Actions` applies bottom, left, and right margins for vertical cards and left, bottom, and right margins for horizontal cards. +- `Card.Title` and `Card.Content` no longer apply padding around them. + +This means, in a typical card layout, the Card will automatically apply the necessary spacing between these sections: + +```tsx + + + + + Card content + + + + + + +``` + +So your existing layouts with the following structure will continue to work as before: + +- `Card` without `Card.Cover` or `Card.Actions`. +- `Card` with `Card.Cover` at the top and/or `Card.Actions` at the bottom. + +If you have `Card.Cover` or `Card.Actions` in positions other than the top and bottom respectively, you will need to adjust their margins. For example, for a `Card.Cover` in the middle of the Card, you would need to set its top and bottom margins to zero: + +```tsx + + + + + Card content + + +``` + +We have also added a `direction` prop to the `Card` component, which lets you arrange its items horizontally instead of the default vertical layout. So if you have custom `flexDirection` styles on the Card, you should replace them with the `direction` prop. + +```tsx +// Before (v5) + + + + + Card content + + + + + + + +// After (v6) + + + + + Card content + + + + + + +``` + +#### `Card.Actions` + +`Card.Actions` no longer injects `mode` on the buttons. To keep the previous behavior, you need to set `mode="outlined"` on the first button and `mode="contained"` on the rest: + +```tsx +// Before (v5) + + + + + +// After (v6) + + + + +``` + +### List + +#### `List.Accordion` + +An accordion with a `left` element now indents only its `List.Item` children. Anything else you put inside keeps its own padding, so indent it yourself to line it up with the items. + +```tsx +// Before (v5) + }> + + Custom row + + + +// After (v6) + }> + + Custom row + + +``` + +`theme` set on `List.Accordion` no longer reaches its children either. Pass it to the child that needs the override. + ### ToggleButton `ToggleButton`, `ToggleButton.Group` and `ToggleButton.Row` were removed. For an diff --git a/example/src/Examples/CardExample.tsx b/example/src/Examples/CardExample.tsx index 016bef3e21..9c4e04f8e2 100644 --- a/example/src/Examples/CardExample.tsx +++ b/example/src/Examples/CardExample.tsx @@ -74,8 +74,12 @@ const CardExample = () => { - - + + @@ -104,12 +108,14 @@ const CardExample = () => { - + + @@ -110,8 +114,12 @@ const News = () => { - - + + diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx index 712110883b..75a3941bab 100644 --- a/src/components/Card/Card.tsx +++ b/src/components/Card/Card.tsx @@ -11,6 +11,7 @@ import useLatestCallback from 'use-latest-callback'; import CardActions from './CardActions'; import CardContent from './CardContent'; +import { CardContext } from './CardContext'; import CardCover from './CardCover'; import CardTitle from './CardTitle'; import { getCardColors } from './utils'; @@ -36,6 +37,7 @@ type ContainedCardProps = { }; type Mode = 'elevated' | 'outlined' | 'contained'; +type Direction = 'vertical' | 'horizontal'; export type Props = Omit & { /** @@ -45,6 +47,12 @@ export type Props = Omit & { * - `outlined` - Card with an outline. */ mode?: Mode; + /** + * Direction of the Card's content. + * - `vertical` + * - `horizontal` + */ + direction?: Direction; /** * Content of the `Card`. */ @@ -78,7 +86,7 @@ export type Props = Omit & { */ elevation?: Elevation; /** - * Style of card's inner content. + * Style of card's content. */ contentStyle?: StyleProp; style?: StyleProp; @@ -100,6 +108,9 @@ export type Props = Omit & { ref?: React.Ref; }; +const DEFAULT_CARD_GAP = 16; +const DEFAULT_CARD_PADDING = 16; + /** * A card is a sheet of material that serves as an entry point to more detailed information. * @@ -112,15 +123,15 @@ export type Props = Omit & { * * const MyComponent = () => ( * + * * * * Card title * Card content * - * * - * - * + * + * * * * ); @@ -128,7 +139,6 @@ export type Props = Omit & { * export default MyComponent; * ``` */ - const Card = ({ elevation: cardElevation = 1, delayLongPress, @@ -137,6 +147,7 @@ const Card = ({ onPressOut, onPressIn, mode: cardMode = 'elevated', + direction: cardDirection = 'vertical', children, style, contentStyle, @@ -182,15 +193,6 @@ const Card = ({ } }); - const total = React.Children.count(children); - const siblings = React.Children.map(children, (child) => - React.isValidElement(child) && child.type - ? typeof child.type !== 'string' && 'displayName' in child.type - ? child.type.displayName - : null - : null - ); - const { backgroundColor, borderColor: themedBorderColor } = getCardColors({ theme, mode: cardMode, @@ -203,18 +205,28 @@ const Card = ({ const borderRadius = theme.shapes.corner.medium; + const cardContext = React.useMemo( + () => ({ padding: DEFAULT_CARD_PADDING, direction: cardDirection }), + [cardDirection] + ); + const content = ( - - {React.Children.map(children, (child, index) => - React.isValidElement(child) - ? React.cloneElement(child as React.ReactElement, { - index, - total, - siblings, - }) - : child - )} - + + + {children} + + ); return ( @@ -274,8 +286,15 @@ Card.Cover = CardCover; Card.Title = CardTitle; const styles = StyleSheet.create({ - innerContainer: { + content: { flexShrink: 1, + overflow: 'hidden', + }, + horizontal: { + flexDirection: 'row', + }, + vertical: { + flexDirection: 'column', }, outline: { borderWidth: 1, diff --git a/src/components/Card/CardActions.tsx b/src/components/Card/CardActions.tsx index d541c691bc..8246a7ec65 100644 --- a/src/components/Card/CardActions.tsx +++ b/src/components/Card/CardActions.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; -import type { CardActionChildProps } from './utils'; +import { CardContext } from './CardContext'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../theme/types'; @@ -26,8 +26,8 @@ export type Props = ViewProps & { * const MyComponent = () => ( * * - * - * + * + * * * * ); @@ -37,32 +37,32 @@ export type Props = ViewProps & { */ const CardActions = ({ theme, style, children, ...rest }: Props) => { useInternalTheme(theme); + const cardContext = React.useContext(CardContext); + + const cardMarginStyle = cardContext + ? cardContext.direction === 'horizontal' + ? { + marginTop: -cardContext.padding, + marginRight: -cardContext.padding, + marginBottom: -cardContext.padding, + } + : { + marginLeft: -cardContext.padding, + marginRight: -cardContext.padding, + marginBottom: -cardContext.padding, + } + : null; const containerStyle = [ styles.container, + cardMarginStyle, { justifyContent: 'flex-end' } satisfies ViewStyle, style, ]; return ( - {React.Children.map(children, (child, index) => { - if (!React.isValidElement(child)) { - return child; - } - - const compact = child.props.compact; - const mode = - child.props.mode ?? (index === 0 ? 'outlined' : 'contained'); - const childStyle = [styles.button, child.props.style]; - - return React.cloneElement(child, { - ...child.props, - compact, - mode, - style: childStyle, - }); - })} + {children} ); }; @@ -73,10 +73,9 @@ const styles = StyleSheet.create({ container: { flexDirection: 'row', alignItems: 'center', - padding: 8, - }, - button: { - marginLeft: 8, + columnGap: 8, + paddingHorizontal: 8, + paddingBottom: 8, }, }); diff --git a/src/components/Card/CardContent.tsx b/src/components/Card/CardContent.tsx index bde25ddb25..fea1e519da 100644 --- a/src/components/Card/CardContent.tsx +++ b/src/components/Card/CardContent.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { StyleSheet, View } from 'react-native'; +import { View } from 'react-native'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; export type Props = ViewProps & { @@ -7,18 +7,6 @@ export type Props = ViewProps & { * Items inside the `Card.Content`. */ children: React.ReactNode; - /** - * @internal - */ - index?: number; - /** - * @internal - */ - total?: number; - /** - * @internal - */ - siblings?: Array; style?: StyleProp; }; @@ -42,59 +30,10 @@ export type Props = ViewProps & { * export default MyComponent; * ``` */ -const CardContent = ({ index, total, siblings, style, ...rest }: Props) => { - const cover = 'Card.Cover'; - const title = 'Card.Title'; - - let contentStyle, prev, next; - - if (typeof index === 'number' && siblings) { - prev = siblings[index - 1]; - next = siblings[index + 1]; - } - - if ( - (prev === cover && next === cover) || - (prev === title && next === title) || - total === 1 - ) { - contentStyle = styles.only; - } else if (index === 0) { - if (next === cover || next === title) { - contentStyle = styles.only; - } else { - contentStyle = styles.first; - } - } else if (typeof total === 'number' && index === total - 1) { - if (prev === cover || prev === title) { - contentStyle = styles.only; - } else { - contentStyle = styles.last; - } - } else if (prev === cover || prev === title) { - contentStyle = styles.first; - } else if (next === cover || next === title) { - contentStyle = styles.last; - } - - return ; -}; +const CardContent = ({ style, ...rest }: Props) => ( + +); CardContent.displayName = 'Card.Content'; -const styles = StyleSheet.create({ - container: { - paddingHorizontal: 16, - }, - first: { - paddingTop: 16, - }, - last: { - paddingBottom: 16, - }, - only: { - paddingVertical: 16, - }, -}); - export default CardContent; diff --git a/src/components/Card/CardContext.tsx b/src/components/Card/CardContext.tsx new file mode 100644 index 0000000000..1cf9bf0173 --- /dev/null +++ b/src/components/Card/CardContext.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; + +type CardContextType = { + /** + * Padding applied to the Card content. + */ + padding: number; + /** + * Direction of the Card layout. + */ + direction: 'vertical' | 'horizontal'; +}; + +export const CardContext = React.createContext(null); + +CardContext.displayName = 'CardContext'; diff --git a/src/components/Card/CardCover.tsx b/src/components/Card/CardCover.tsx index 4542aa7c99..266ad045ea 100644 --- a/src/components/Card/CardCover.tsx +++ b/src/components/Card/CardCover.tsx @@ -1,6 +1,8 @@ +import * as React from 'react'; import { Image, StyleSheet, View } from 'react-native'; import type { ImageProps, StyleProp, ViewStyle } from 'react-native'; +import { CardContext } from './CardContext'; import { getCardCoverStyle } from './utils'; import { useInternalTheme } from '../../core/theming'; import { grey200 } from '../../theme/colors'; @@ -8,14 +10,6 @@ import type { ThemeProp } from '../../theme/types'; import { splitStyles } from '../../utils/splitStyles'; export type Props = ImageProps & { - /** - * @internal - */ - index?: number; - /** - * @internal - */ - total?: number; style?: StyleProp; /** * @optional @@ -42,14 +36,9 @@ export type Props = ImageProps & { * * @extends Image props https://reactnative.dev/docs/image#props */ -const CardCover = ({ - index, - total, - style, - theme: themeOverrides, - ...rest -}: Props) => { +const CardCover = ({ style, theme: themeOverrides, ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const cardContext = React.useContext(CardContext); const flattenedStyles = StyleSheet.flatten(style) || {}; const [, borderRadiusStyles] = splitStyles( @@ -59,33 +48,47 @@ const CardCover = ({ const coverStyle = getCardCoverStyle({ theme, - index, - total, borderRadiusStyles, }); + const cardMarginStyle = cardContext + ? cardContext.direction === 'horizontal' + ? { + marginTop: -cardContext.padding, + marginLeft: -cardContext.padding, + marginBottom: -cardContext.padding, + } + : { + marginTop: -cardContext.padding, + marginLeft: -cardContext.padding, + marginRight: -cardContext.padding, + } + : null; + return ( - + ); }; CardCover.displayName = 'Card.Cover'; + const styles = StyleSheet.create({ container: { - height: 195, + height: 194, backgroundColor: grey200, overflow: 'hidden', }, image: { flex: 1, - height: undefined, - width: undefined, + height: 'auto', + width: 'auto', justifyContent: 'flex-end', }, }); diff --git a/src/components/Card/CardTitle.tsx b/src/components/Card/CardTitle.tsx index 15477d3033..ed8289cde5 100644 --- a/src/components/Card/CardTitle.tsx +++ b/src/components/Card/CardTitle.tsx @@ -81,14 +81,6 @@ export type Props = ViewProps & { * Style for the right element wrapper. */ rightStyle?: StyleProp; - /** - * @internal - */ - index?: number; - /** - * @internal - */ - total?: number; /** * Specifies the largest possible scale a title font can reach. */ @@ -151,11 +143,8 @@ const CardTitle = ({ }: Props) => { useInternalTheme(themeOverrides); - const minHeight = subtitle || left || right ? 72 : 50; - const marginBottom = subtitle ? 0 : 2; - return ( - + {left ? ( {left({ @@ -167,7 +156,7 @@ const CardTitle = ({ {title && ( { if (Object.keys(borderRadiusStyles).length > 0) { return { diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 5b273151ec..218353d5e8 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -14,7 +14,6 @@ import type { Elevation, ThemeProp } from '../../theme/types'; import Modal from '../Modal'; import type { SurfaceStyle } from '../Surface'; import { DialogTitleIdContext } from './utils'; -import type { DialogChildProps } from './utils'; export type Props = { /** @@ -145,17 +144,7 @@ const Dialog = ({ overlayTestID={overlayTestID} > - {React.Children.toArray(children) - .filter((child) => child != null && typeof child !== 'boolean') - .map((child, i) => { - if (i === 0 && React.isValidElement(child)) { - return React.cloneElement(child, { - style: [{ marginTop: 24 }, child.props.style], - }); - } - - return child; - })} + {children} ); @@ -183,6 +172,7 @@ const styles = StyleSheet.create({ */ marginVertical: Platform.OS === 'android' ? 44 : 0, justifyContent: 'flex-start', + paddingTop: 24, }, }); diff --git a/src/components/Dialog/DialogActions.tsx b/src/components/Dialog/DialogActions.tsx index 1314613820..fb8e0c56e4 100644 --- a/src/components/Dialog/DialogActions.tsx +++ b/src/components/Dialog/DialogActions.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { StyleSheet, View } from 'react-native'; import type { StyleProp, ViewProps, ViewStyle } from 'react-native'; -import type { DialogActionChildProps } from './utils'; import { useInternalTheme } from '../../core/theming'; import type { ThemeProp } from '../../theme/types'; @@ -44,26 +43,17 @@ export type Props = ViewProps & { * export default MyComponent; * ``` */ -const DialogActions = (props: Props) => { - useInternalTheme(props.theme); - const actionsLength = React.Children.toArray(props.children).length; +const DialogActions = ({ + theme: themeOverrides, + style, + children, + ...rest +}: Props) => { + useInternalTheme(themeOverrides); return ( - - {React.Children.map(props.children, (child, i) => - React.isValidElement(child) - ? React.cloneElement(child, { - compact: true, - uppercase: false, - style: [ - { - marginRight: i + 1 === actionsLength ? 0 : 8, - }, - child.props.style, - ], - }) - : child - )} + + {children} ); }; @@ -76,6 +66,7 @@ const styles = StyleSheet.create({ flexGrow: 1, alignItems: 'center', justifyContent: 'flex-end', + columnGap: 8, paddingBottom: 24, paddingHorizontal: 24, }, diff --git a/src/components/Dialog/DialogIcon.tsx b/src/components/Dialog/DialogIcon.tsx index efdec2e6a5..56cdfa0d64 100644 --- a/src/components/Dialog/DialogIcon.tsx +++ b/src/components/Dialog/DialogIcon.tsx @@ -23,6 +23,10 @@ export type Props = { * @optional */ theme?: ThemeProp; + /** + * testID to be used on tests. + */ + testID?: string; }; /** @@ -65,6 +69,7 @@ const DialogIcon = ({ color, icon, theme: themeOverrides, + testID, }: Props) => { const theme = useInternalTheme(themeOverrides); const { colors } = theme; @@ -73,7 +78,7 @@ const DialogIcon = ({ const iconColor = color || colors.secondary; return ( - + ); @@ -85,7 +90,8 @@ const styles = StyleSheet.create({ wrapper: { alignItems: 'center', justifyContent: 'center', - paddingTop: 24, + marginBottom: 16, + paddingTop: 0, }, }); diff --git a/src/components/Dialog/DialogTitle.tsx b/src/components/Dialog/DialogTitle.tsx index 4dd8bf8f5c..f60672f78e 100644 --- a/src/components/Dialog/DialogTitle.tsx +++ b/src/components/Dialog/DialogTitle.tsx @@ -83,7 +83,7 @@ const styles = StyleSheet.create({ marginHorizontal: 24, }, v3Text: { - marginTop: 16, + marginTop: 0, marginBottom: 16, }, }); diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 7c552fce36..ff26a075a4 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -12,8 +12,9 @@ import type { ViewStyle, } from 'react-native'; +import { ListAccordionContext } from './ListAccordionContext'; import { ListAccordionGroupContext } from './ListAccordionGroup'; -import type { ListChildProps, Style } from './utils'; +import type { Style } from './utils'; import { getAccordionColors, getLeftStyles } from './utils'; import { useLocale } from '../../core/locale'; import { useInternalTheme } from '../../core/theming'; @@ -241,6 +242,10 @@ const ListAccordion = ({ groupContext && id !== undefined ? () => groupContext.onAccordionPress(id) : handlePressAction; + + const hasLeft = left != null; + const accordionContext = React.useMemo(() => ({ hasLeft }), [hasLeft]); + return ( @@ -324,23 +329,11 @@ const ListAccordion = ({ - {isExpanded - ? React.Children.map(children, (child) => { - if ( - left && - React.isValidElement(child) && - !child.props.left && - !child.props.right - ) { - return React.cloneElement(child, { - style: [styles.child, child.props.style], - theme, - }); - } - - return child; - }) - : null} + {isExpanded ? ( + + {children} + + ) : null} ); }; @@ -374,9 +367,6 @@ const styles = StyleSheet.create({ marginVertical: 6, paddingLeft: 8, }, - child: { - paddingLeft: 40, - }, content: { flex: 1, justifyContent: 'center', diff --git a/src/components/List/ListAccordionContext.tsx b/src/components/List/ListAccordionContext.tsx new file mode 100644 index 0000000000..142c25ff65 --- /dev/null +++ b/src/components/List/ListAccordionContext.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; + +export type ListAccordionContextType = { + /** + * Whether the accordion renders a `left` element. + */ + hasLeft: boolean; +}; + +export const ListAccordionContext = + React.createContext({ hasLeft: false }); + +ListAccordionContext.displayName = 'ListAccordionContext'; diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index feb0f19f12..038ef86a58 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -11,6 +11,7 @@ import type { ViewStyle, } from 'react-native'; +import { ListAccordionContext } from './ListAccordionContext'; import { getLeftStyles, getRightStyles } from './utils'; import type { Style } from './utils'; import { useInternalTheme } from '../../core/theming'; @@ -166,6 +167,8 @@ const ListItem = ({ ...rest }: Props) => { const theme = useInternalTheme(themeOverrides); + const { hasLeft } = React.useContext(ListAccordionContext); + const shouldIndent = hasLeft && !left && !right; const [alignToTop, setAlignToTop] = React.useState(false); const onDescriptionTextLayout = ( @@ -233,7 +236,11 @@ const ListItem = ({ { expect(screen.getByText('Content').parent).toHaveStyle(styles.contentStyle); }); + it('clips inner content to the card shape', async () => { + await render( + + Content + + ); + + expect(screen.getByText('Content').parent).toHaveStyle({ + borderRadius: LightTheme.shapes.corner.medium, + overflow: 'hidden', + }); + }); + it('does not render a disabled accessibility state', async () => { await render({null}); @@ -124,18 +138,67 @@ describe('CardCover', () => { describe('CardActions', () => { it('renders button with passed mode', async () => { + const buttonProps = jest.fn(); + const ProbeButton = (props: ComponentProps) => { + buttonProps(props); + + return + + ); - expect( - // eslint-disable-next-line no-restricted-syntax -- TODO: replace TestInstance props access with a user-visible assertion. - screen.getByTestId('card-actions').props.children[0].props.mode - ).toBe('contained'); + expect(screen.getByTestId('card-actions')).toHaveStyle({ + flexDirection: 'row', + justifyContent: 'flex-end', + columnGap: 8, + }); }); }); @@ -201,3 +264,22 @@ describe('getCardCoverStyle - border radius', () => { ).toMatchObject({ borderRadius: LightTheme.shapes.corner.medium }); }); }); + +describe('CardContent', () => { + it('uses the Card padding when it follows a cover and a title', async () => { + await render( + + + + + Card content + + + ); + + expect(screen.getByTestId('card-content').parent).toHaveStyle({ + padding: 16, + gap: 16, + }); + }); +}); diff --git a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap index 216e5b5d08..2f7cee90ba 100644 --- a/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap +++ b/src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap @@ -114,8 +114,19 @@ exports[`Card renders an outlined card 1`] = ` [ { "flexShrink": 1, + "overflow": "hidden", + }, + { + "borderRadius": 12, + }, + { + "flexDirection": "column", }, undefined, + { + "gap": 16, + "padding": 16, + }, ] } /> @@ -237,8 +248,19 @@ exports[`Card renders an outlined card with a custom outline color 1`] = ` [ { "flexShrink": 1, + "overflow": "hidden", + }, + { + "borderRadius": 12, + }, + { + "flexDirection": "column", }, undefined, + { + "gap": 16, + "padding": 16, + }, ] } /> @@ -362,8 +384,19 @@ exports[`Card renders an outlined card with custom border color 1`] = ` [ { "flexShrink": 1, + "overflow": "hidden", + }, + { + "borderRadius": 12, + }, + { + "flexDirection": "column", }, undefined, + { + "gap": 16, + "padding": 16, + }, ] } /> diff --git a/src/components/__tests__/Dialog.test.tsx b/src/components/__tests__/Dialog.test.tsx index 368bd9727f..bae85cb874 100644 --- a/src/components/__tests__/Dialog.test.tsx +++ b/src/components/__tests__/Dialog.test.tsx @@ -1,3 +1,4 @@ +import type { ComponentProps } from 'react'; import { Text, StyleSheet, @@ -124,19 +125,70 @@ describe('Dialog', () => { expect(onDismiss).toHaveBeenCalledTimes(1); }); - it('applies top margin to the first child', async () => { + it('does not add a top margin to a title-first dialog', async () => { await render( - + Test Dialog Content ); + expect(screen.getByTestId('dialog-title')).toHaveStyle({ + marginTop: 0, + }); + }); + + it('should keep the bottom padding on a content-first dialog', async () => { + await render( + + + + Test Dialog Content + + + + ); + expect(screen.getByTestId('dialog-content')).toHaveStyle({ - marginTop: 24, + paddingBottom: 24, + }); + }); + + it('should not add a top padding to an icon-first dialog', async () => { + await render( + + + + + + ); + + expect(screen.getByTestId('dialog-icon')).toHaveStyle({ + paddingTop: 0, + }); + }); + + it('should preserve the icon-to-title spacing for an icon dialog', async () => { + await render( + + + + + Test Dialog Content + + + + ); + + expect(screen.getByTestId('dialog-icon')).toHaveStyle({ + marginBottom: 16, + paddingTop: 0, + }); + expect(screen.getByTestId('dialog-title')).toHaveStyle({ + marginTop: 0, }); }); @@ -188,35 +240,64 @@ describe('DialogActions', () => { it('applies default styles', async () => { await render( - - + + ); const dialogActionsContainer = screen.getByTestId('dialog-actions'); - const dialogActionButtons = dialogActionsContainer.children; expect(dialogActionsContainer).toHaveStyle({ paddingBottom: 24, paddingHorizontal: 24, + columnGap: 8, }); - expect(dialogActionButtons[0]).toHaveStyle({ marginRight: 8 }); - expect(dialogActionButtons[1]).toHaveStyle({ marginRight: 0 }); }); - it('applies custom styles', async () => { + it('does not inject button props into actions', async () => { + const buttonProps = jest.fn(); + const ProbeButton = (props: ComponentProps) => { + buttonProps(props); + + return - + Cancel + Ok ); - const dialogActionsContainer = screen.getByTestId('dialog-actions'); - const dialogActionButtons = dialogActionsContainer.children; + const [cancelButtonProps] = buttonProps.mock.calls[0]; + const [okButtonProps] = buttonProps.mock.calls[1]; - expect(dialogActionButtons[0]).toHaveStyle({ margin: 10 }); - expect(dialogActionButtons[1]).toHaveStyle({ margin: 0 }); + expect(cancelButtonProps).toHaveProperty('style', styles.spacing); + expect(okButtonProps).toHaveProperty('style', styles.noSpacing); }); }); diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index 59d221f9dc..ce387e343a 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -2,7 +2,7 @@ import { StyleSheet, View } from 'react-native'; import { describe, expect, it } from '@jest/globals'; -import { render } from '../../test-utils'; +import { render, screen } from '../../test-utils'; import { red500 } from '../../theme/colors'; import { LightTheme } from '../../theme/schemes'; import ListAccordion from '../List/ListAccordion'; @@ -94,6 +94,34 @@ it('renders list accordion with custom title and description styles', async () = expect(tree).toMatchSnapshot(); }); +it('indents expanded accordion children without their own left/right when the accordion has a left icon', async () => { + await render( + } + title="Accordion with indented children" + expanded + > + + + ); + + expect(screen.getByTestId('accordion-child')).toHaveStyle({ + paddingLeft: 40, + }); +}); + +it('does not indent expanded accordion children when the accordion has no left icon', async () => { + await render( + + + + ); + + expect(screen.getByTestId('accordion-child')).not.toHaveStyle({ + paddingLeft: 40, + }); +}); + describe('ListAccordion', () => { it('should not throw an error when id={0}', async () => { const ListAccordionTest = () => ( diff --git a/src/components/__tests__/Tooltip.test.tsx b/src/components/__tests__/Tooltip.test.tsx index d6b037e10c..2888803613 100644 --- a/src/components/__tests__/Tooltip.test.tsx +++ b/src/components/__tests__/Tooltip.test.tsx @@ -163,7 +163,7 @@ describe('Tooltip', () => { it('hides the tooltip when the user stop pressing the component', async () => { const { wrapper: { queryByText, getByText, findByText }, - } = await setup({ enterTouchDelay: 50, leaveTouchDelay: 0 }); + } = await setup({ enterTouchDelay: 50, leaveTouchDelay: 100 }); await userEvent.longPress(getTrigger(getByText)); From eb4fcbebc234fcde02da02f91ba6bbc8e8c20ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Nowacki?= Date: Wed, 21 Oct 2026 18:23:32 +0200 Subject: [PATCH 84/94] chore: automate example app publishing to app stores (#5118) * chore: automate example app publishing The example app in the stores drifts away from the library: it was released eighteen times in nine years against eighty-three library releases, all by hand, and the last one was twenty months ago. Ship it from the release that prompts it instead. What it ships depends on whether native code changed. `runtimeVersion` uses the fingerprint policy, so the workflow can compare the project against the builds already out there: unchanged means the new JavaScript runs on what people already have, and goes out as an update; changed means a build and a trip through the stores. Only a build carries a version to a store, so only a build bumps one, and that bump comes back as a pull request. This replaces the updates workflow, which published updates when a labelled pull request merged. It never ran once: it triggers on push, where the pull_request it tests does not exist, so all sixty-nine runs skipped. Tying both outcomes to the release removes the label nobody remembered to add. Prereleases and stable releases build against separate channels so an update published for an alpha cannot reach the stable app. Where a build lands is decided by the tag rather than by the release's prerelease flag, because .release-it.json pins every release to `preRelease: true` for the 6.0 alpha cycle, and reading that flag would route a stable 6.0.0 to the internal track. Publishing runs after a release already exists, so it cannot fail, revoke or roll back one. A run that does not succeed says so on its own run page, where the mail GitHub already sends the publisher leads, and says the release is unaffected. Store credentials live in EAS rather than here, so EXPO_TOKEN is the only secret this needs. Setup and the known limitations are in CONTRIBUTING.md. Co-Authored-By: Claude Opus 5 (1M context) * feat(ci): add a dry run to the example app workflow Whether EXPO_TOKEN still works could otherwise only be found out by a real run, which builds and submits. A dry run authenticates, works out whether the release would build or update, reports that, and stops. Also check authentication in its own step, so a missing or expired token fails in seconds with a message that says so rather than several minutes later inside a build. Co-Authored-By: Claude Opus 5 (1M context) * fix: keep the example app fingerprint stable across pod install ExpoModulesJSI.podspec builds an xcframework into its own source directory from a prepare_command, and that directory is fingerprinted. EAS fingerprints after installing pods and the CLI fingerprints before, so the two disagreed and the build stopped with a runtime version mismatch. Ignore the generated products. The sources they are built from are fingerprinted already, so nothing that changes the native runtime stops being noticed. Without this the workflow would also never publish an update: the fingerprint it computes on a runner could never match the one EAS recorded. Drop the build number and version code from the app config as well, now that EAS assigns both remotely and warns on every command that the values in the config are ignored. Co-Authored-By: Claude Opus 5 (1M context) * refactor: drop the dry run from the example app workflow The dry run existed to answer whether EXPO_TOKEN still worked and whether the store credentials would resolve, without spending a real run. Both have since been answered by building and submitting for real, so what is left is a second path through the workflow kept alive to derisk a first run whose worst outcome is that a demo app does not update. Trim what publishing adds to CONTRIBUTING.md down to what would otherwise go wrong quietly: that nothing reaches the App Store on its own, that the version bump pull request has to be merged before the next release, where the credentials live, and which generated files have to stay out of the fingerprint. The run page and the issue a failed run opens already say the rest. Co-Authored-By: Claude Opus 5 (1M context) * chore: replace AI prose with human comments, trim tests * chore: simplify deployment output logs * fix: address review feedback on example app deployment * refactor: set example app version from the release tag * fix: harden the example app deployment workflow * fix: address second round of example app deployment review Pass the release tag from the check job, pin eas-cli, build only the platforms whose native code changed, queue preview and production runs separately, and read the EAS Node version from .nvmrc. * fix: tighten the example app deployment workflow Publish updates before builds so a slow build can't hold them up, fail on a missing fingerprint or commit lookup, work out the profile once in the check job, and drop the 2-hour timeout in favour of the default. --------- Co-authored-by: Radoslaw Nowacki Co-authored-by: Claude Opus 5 (1M context) --- .github/workflows/publish-example-app.yml | 206 ++++++++++++++++++++++ .github/workflows/updates.yml | 27 --- CONTRIBUTING.md | 9 + example/.fingerprintignore | 2 + example/.nvmrc | 1 + example/app.json | 7 +- example/eas.json | 34 ++++ example/fingerprint.config.js | 7 + 8 files changed, 263 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/publish-example-app.yml delete mode 100644 .github/workflows/updates.yml create mode 100644 example/.fingerprintignore create mode 120000 example/.nvmrc create mode 100644 example/eas.json create mode 100644 example/fingerprint.config.js diff --git a/.github/workflows/publish-example-app.yml b/.github/workflows/publish-example-app.yml new file mode 100644 index 0000000000..7c9a86531f --- /dev/null +++ b/.github/workflows/publish-example-app.yml @@ -0,0 +1,206 @@ +name: Publish example app + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: Release tag to publish the example app for, e.g. v6.0.0 + required: true + platform: + description: Platform to build. Defaults to all. + required: false + default: all + type: choice + options: + - all + - ios + - android + +permissions: + contents: read + +defaults: + run: + shell: bash + +jobs: + check: + name: Check release is on main + runs-on: ubuntu-latest + env: + TAG: ${{ github.event.release.tag_name || inputs.tag }} + outputs: + on-main: ${{ steps.check.outputs.on-main }} + sha: ${{ steps.check.outputs.sha }} + tag: ${{ env.TAG }} + profile: ${{ steps.check.outputs.profile }} + steps: + - id: check + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + run: | + status=$(gh api "repos/$GH_REPO/compare/main...$TAG" --jq .status) + + if [ "$status" = "identical" ] || [ "$status" = "behind" ]; then + sha=$(gh api "repos/$GH_REPO/commits/$TAG" --jq .sha) + echo "on-main=true" >> "$GITHUB_OUTPUT" + echo "sha=$sha" >> "$GITHUB_OUTPUT" + else + echo "on-main=false" >> "$GITHUB_OUTPUT" + echo "::warning::$TAG is not on main ($status), nothing to deploy." + echo "$TAG is not on main ($status), nothing to deploy." >> "$GITHUB_STEP_SUMMARY" + fi + + if [ "${TAG#*-}" != "$TAG" ]; then + echo "profile=preview" >> "$GITHUB_OUTPUT" + else + echo "profile=production" >> "$GITHUB_OUTPUT" + fi + + publish: + name: Build and submit example app + needs: check + if: needs.check.outputs.on-main == 'true' + runs-on: ubuntu-latest + concurrency: + # One queue per channel, so a pending preview run doesn't cancel a production one + group: publish-example-app-${{ needs.check.outputs.profile }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.check.outputs.sha }} + + - name: Setup + uses: ./.github/actions/setup + + - name: Setup Expo + uses: expo/expo-github-action@c7b66a9c327a43a8fa7c0158e7f30d6040d2481e # v8.2.1 + with: + eas-version: 24.12.1 + token: ${{ secrets.EXPO_TOKEN }} + + - name: Check Expo authentication + working-directory: ./example + run: | + if ! eas whoami; then + echo "::error::Could not authenticate with Expo, check EXPO_TOKEN." + exit 1 + fi + + - name: Set example app version from the release tag + working-directory: ./example + env: + TAG: ${{ needs.check.outputs.tag }} + run: | + version="${TAG#v}" + version="${version%%-*}" + + if ! echo "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Could not read a major.minor.patch version from $TAG." + exit 1 + fi + + jq --arg version "$version" '.expo.version = $version' app.json > app.json.tmp + mv app.json.tmp app.json + + - name: Decide between an update and a build + id: decide + working-directory: ./example + env: + PROFILE: ${{ needs.check.outputs.profile }} + PLATFORM: ${{ inputs.platform || 'all' }} + run: | + platforms="$PLATFORM" + if [ "$PLATFORM" = "all" ]; then + platforms="ios android" + fi + + build=() + update=() + for platform in $platforms; do + hash=$(npx expo-updates fingerprint:generate --platform "$platform" | jq -er .hash) + + existing=$(eas build:list \ + --platform "$platform" \ + --channel "$PROFILE" \ + --fingerprint-hash "$hash" \ + --status finished \ + --limit 1 \ + --json \ + --non-interactive | jq 'if type == "array" then length else "notanarray" end') + + case "$existing" in + ''|*[!0-9]*) + echo "::error::Could not read builds for $platform." + exit 1 + ;; + esac + + echo "$platform: fingerprint $hash, $existing matching build(s)" + if [ "$existing" -eq 0 ]; then + build+=("$platform") + else + update+=("$platform") + fi + done + + # EAS takes a single platform or "all" + to_platform() { + case $# in + 0) echo "" ;; + 1) echo "$1" ;; + *) echo all ;; + esac + } + + echo "build=$(to_platform "${build[@]}")" >> "$GITHUB_OUTPUT" + echo "update=$(to_platform "${update[@]}")" >> "$GITHUB_OUTPUT" + echo "Building: ${build[*]:-none}. Updating: ${update[*]:-none}." + + - name: Publish an update + # Before the build, so a slow or timed-out build can't hold up the update + if: steps.decide.outputs.update != '' + working-directory: ./example + env: + PROFILE: ${{ needs.check.outputs.profile }} + PLATFORM: ${{ steps.decide.outputs.update }} + TAG: ${{ needs.check.outputs.tag }} + run: | + eas update \ + --channel "$PROFILE" \ + --platform "$PLATFORM" \ + --message "$TAG" \ + --non-interactive + + echo "Published an OTA update for $TAG for $PLATFORM on the $PROFILE channel." >> "$GITHUB_STEP_SUMMARY" + + - name: Build and submit to the app stores + # Don't let a failed update for one platform skip the other's build + if: ${{ !cancelled() && steps.decide.outputs.build != '' }} + working-directory: ./example + env: + PROFILE: ${{ needs.check.outputs.profile }} + PLATFORM: ${{ steps.decide.outputs.build }} + TAG: ${{ needs.check.outputs.tag }} + run: | + eas build \ + --platform "$PLATFORM" \ + --profile "$PROFILE" \ + --auto-submit-with-profile "$PROFILE" \ + --non-interactive + + echo "Built and submitted $TAG for $PLATFORM using the $PROFILE profile." >> "$GITHUB_STEP_SUMMARY" + + - name: Report failure + if: failure() || cancelled() + env: + TAG: ${{ needs.check.outputs.tag }} + run: | + echo "::error::Example app deployment failed for $TAG. The release itself is fine, only the demo app did not update." + echo "Check EAS and the stores before re-running, a build may still be running or already submitted. Re-running before it finishes starts a second build." >> "$GITHUB_STEP_SUMMARY" + echo "If the build finished but the store upload failed, resubmit that build with \`eas submit --id \`. Re-running this workflow will publish an OTA update instead." >> "$GITHUB_STEP_SUMMARY" + echo "If only the update failed, re-running is safe." >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/updates.yml b/.github/workflows/updates.yml deleted file mode 100644 index 336a01184e..0000000000 --- a/.github/workflows/updates.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Expo EAS Updates -on: - push: - branches: - - main -jobs: - publish: - if: ${{ (contains(github.event.pull_request.labels.*.name, 'example app')) }} - name: Install and publish - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Setup - uses: ./.github/actions/setup - - - name: Setup Expo - uses: expo/expo-github-action@c7b66a9c327a43a8fa7c0158e7f30d6040d2481e # v8.2.1 - with: - expo-version: latest - eas-version: latest - token: ${{ secrets.EXPO_TOKEN }} - - - name: Publish Expo app update - working-directory: ./example - run: eas update --auto diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62c3b9c59b..3e3fa7278f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,6 +109,15 @@ yarn release NOTE: You must have a `GITHUB_TOKEN` environment variable available. You can create a GitHub access token with the "repo" access [here](https://github.com/settings/tokens). +Releases from main also deploy the example app via EAS. If there were no native code changes, it'll ship an OTA update. + +Otherwise, for stable releases: + +- The Android build is automatically submitted to Google Play for review +- The iOS build is automatically uploaded to App Store Connect and needs to be manually submitted for review + +Pre-release builds are shipped to the internal track on Google Play for Android and TestFlight for iOS. They shouldn't be submitted to the App Store for review, or the stable build with the same version would be rejected. + ## Reporting issues You can report issues on our [bug tracker](https://github.com/callstack/react-native-paper/issues). Please follow the issue template when opening an issue. diff --git a/example/.fingerprintignore b/example/.fingerprintignore new file mode 100644 index 0000000000..b3c5af777d --- /dev/null +++ b/example/.fingerprintignore @@ -0,0 +1,2 @@ +# built by pod install, would make the fingerprint differ from the one EAS computes +**/expo-modules-jsi/apple/Products/** diff --git a/example/.nvmrc b/example/.nvmrc new file mode 120000 index 0000000000..3d48ffa461 --- /dev/null +++ b/example/.nvmrc @@ -0,0 +1 @@ +../.nvmrc \ No newline at end of file diff --git a/example/app.json b/example/app.json index d8b2b6debb..4dd835b98f 100644 --- a/example/app.json +++ b/example/app.json @@ -16,11 +16,12 @@ "owner": "react-native-paper", "ios": { "bundleIdentifier": "com.callstack.reactnativepaperexample", - "buildNumber": "26.0.5" + "infoPlist": { + "ITSAppUsesNonExemptEncryption": false + } }, "android": { "package": "com.callstack.reactnativepaperexample", - "versionCode": 38, "permissions": [ "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE" @@ -37,7 +38,7 @@ } }, "runtimeVersion": { - "policy": "sdkVersion" + "policy": "fingerprint" }, "updates": { "url": "https://u.expo.dev/ba8f5139-58fe-48f0-b2f2-2d675b6eb2d4" diff --git a/example/eas.json b/example/eas.json new file mode 100644 index 0000000000..482e583d8e --- /dev/null +++ b/example/eas.json @@ -0,0 +1,34 @@ +{ + "cli": { + "appVersionSource": "remote" + }, + "build": { + "production": { + "autoIncrement": true, + "android": { + "buildType": "app-bundle" + }, + "channel": "production" + }, + "preview": { + "extends": "production", + "channel": "preview" + } + }, + "submit": { + "production": { + "android": { + "track": "production" + }, + "ios": { + "ascAppId": "1548934513" + } + }, + "preview": { + "extends": "production", + "android": { + "track": "internal" + } + } + } +} diff --git a/example/fingerprint.config.js b/example/fingerprint.config.js new file mode 100644 index 0000000000..501ccf5ba3 --- /dev/null +++ b/example/fingerprint.config.js @@ -0,0 +1,7 @@ +/** @type {import('expo/fingerprint').Config} */ +module.exports = { + sourceSkips: [ + 'ExpoConfigVersions', + 'PackageJsonAndroidAndIosScriptsIfNotContainRun', + ], +}; From 56c907ad5e32c4c75f9442cc624060c20869d971 Mon Sep 17 00:00:00 2001 From: John Fanidis Date: Thu, 29 Oct 2026 16:53:22 +0200 Subject: [PATCH 85/94] chore: pass the release version through and infer the npm tag (#5149) The release script ran `release-it --only-version`. release-it 13.7.2 has no such option and parses only --dry-run and --ci as boolean flags, so `yarn release 6.0.0-alpha.1` took the version as the flag's value and released the recommended bump instead: 6.0.0. .release-it.json pinned npm.tag to "alpha" and github.preRelease to true. A configured tag always wins, so every release, 6.0.0 included, would go to the alpha tag as a GitHub prerelease and leave 5.15.3 on latest. - Run plain `release-it`, so the version passed is the version released. - Drop npm.tag and github.preRelease: release-it takes the tag from the prerelease id (alpha, beta, rc) or uses latest for a stable version, and marks prereleases on GitHub by itself. - Create the GitHub release as a draft, so the example app pipeline (which runs when a release is published) waits until npm has the package. - Rewrite "Publishing a release" in CONTRIBUTING.md to match. --- .release-it.json | 5 ++--- CONTRIBUTING.md | 18 +++++++++++++++--- package.json | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.release-it.json b/.release-it.json index 141b5d1193..bd70df14cf 100644 --- a/.release-it.json +++ b/.release-it.json @@ -4,12 +4,11 @@ "tagName": "v${version}" }, "npm": { - "publish": true, - "tag": "alpha" + "publish": true }, "github": { "release": true, - "preRelease": true + "draft": true }, "plugins": { "@release-it/conventional-changelog": { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e3fa7278f..99aef2a692 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,15 +101,27 @@ To preview the documentation, run `yarn docs dev` in the project root. ### Publishing a release -We use [release-it](https://github.com/webpro/release-it) to automate our release. If you have publish access to the NPM package, run the following from the main branch to publish a new release: +We use [release-it](https://github.com/webpro/release-it) to automate our release. If you have publish access to the NPM package, run the following from the main branch, always with the exact version you want to publish: ```sh -yarn release +yarn release 6.0.0-beta.0 --dry-run +yarn release 6.0.0-beta.0 +``` + +- Always pass the version. A bare `yarn release` applies the bump the commit history recommends, which can be a stable or major version you did not intend. npm never accepts the same version twice, so a wrong version cannot be taken back. +- Start with the dry run. Before running it for real, check the compare range in the changelog heading it prints (for example `v6.0.0-alpha.0...v6.0.0-alpha.1`) and the `--tag` on the `npm publish` line. +- The npm tag and the GitHub prerelease flag follow the version: `6.0.0-beta.0` is published under the `beta` tag as a GitHub prerelease, `6.0.0` under `latest` as a regular release. +- The GitHub release is created as a draft. Once `npm publish` has succeeded, add the release notes and publish the draft: + +```sh +gh release edit v6.0.0-beta.0 --notes-file notes.md --draft=false ``` NOTE: You must have a `GITHUB_TOKEN` environment variable available. You can create a GitHub access token with the "repo" access [here](https://github.com/settings/tokens). -Releases from main also deploy the example app via EAS. If there were no native code changes, it'll ship an OTA update. +You also need to be logged in to npm with an account that can publish the package: check with `npm whoami`, and run `npm login` if it fails. + +Publishing the GitHub release also deploys the example app via EAS, so keep the release a draft until npm has the package. If there were no native code changes, it'll ship an OTA update. Otherwise, for stable releases: diff --git a/package.json b/package.json index c0c83323ee..11e2f3f411 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "test": "jest", "prepack": "bob build", "generate-mappings": "node ./scripts/generate-mappings.ts", - "release": "release-it --only-version", + "release": "release-it", "docs": "yarn --cwd docs", "example": "yarn --cwd example" }, From cc7a9027affbe0e38d7db5a0d7a6826f676365ed Mon Sep 17 00:00:00 2001 From: Doberjohn Date: Thu, 29 Oct 2026 17:18:37 +0200 Subject: [PATCH 86/94] chore: release 6.0.0-alpha.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11e2f3f411..1e095a3829 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-paper", - "version": "6.0.0-alpha.0", + "version": "6.0.0-alpha.1", "description": "Material design for React Native", "main": "lib/module/index.js", "react-native-paper-source": "src/index.tsx", From a8943b91d7c48628ab4fa258fc6b8c7b9396a11c Mon Sep 17 00:00:00 2001 From: Toyeshh Medikonda Date: Thu, 29 Oct 2026 10:30:42 -0500 Subject: [PATCH 87/94] docs: fix broken default theme links and typos in guides (#5141) The v6 getting-started and theming guides link to src/styles/themes/v3/LightTheme.tsx, which no longer exists on main. Point them at src/theme/schemes/LightTheme.tsx. Also correct spelling in the v5 guides: platfrom, Additionaly, oficially, follwing. --- docs/5.x/docs/guides/fonts.md | 2 +- docs/5.x/docs/guides/getting-started.md | 2 +- docs/5.x/docs/guides/migration-guide-to-5.0.md | 8 ++++---- docs/6.x/docs/guides/getting-started.mdx | 2 +- docs/6.x/docs/guides/theming.mdx | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/5.x/docs/guides/fonts.md b/docs/5.x/docs/guides/fonts.md index 906159f99a..ffab445e0a 100644 --- a/docs/5.x/docs/guides/fonts.md +++ b/docs/5.x/docs/guides/fonts.md @@ -152,7 +152,7 @@ If you're using TypeScript use `as const` when defining `fontConfig`. In the latest version fonts in theme are structured based on the `variant` keys e.g. `displayLarge` or `bodyMedium` which are then used in `Text`'s component throughout the whole library. :::info -The default `fontFamily` is different per particular platfrom: +The default `fontFamily` is different per particular platform: ```js Platform.select({ diff --git a/docs/5.x/docs/guides/getting-started.md b/docs/5.x/docs/guides/getting-started.md index 68071c4d25..554f3ceb60 100644 --- a/docs/5.x/docs/guides/getting-started.md +++ b/docs/5.x/docs/guides/getting-started.md @@ -18,7 +18,7 @@ npm install react-native-paper npm install react-native-safe-area-context ``` -Additionaly for `iOS` platform there is a requirement to link the native parts of the library: +Additionally for `iOS` platform there is a requirement to link the native parts of the library: ```bash npx pod-install diff --git a/docs/5.x/docs/guides/migration-guide-to-5.0.md b/docs/5.x/docs/guides/migration-guide-to-5.0.md index 2e47a8773a..21bb96c2f7 100644 --- a/docs/5.x/docs/guides/migration-guide-to-5.0.md +++ b/docs/5.x/docs/guides/migration-guide-to-5.0.md @@ -14,7 +14,7 @@ Below you can find the most important information about the components whose API ### Installation -- The `v5` has been oficially released as a stable version, which means it will be installed by default from the `npm`. In order to do that, open a Terminal in your project's folder and run: +- The `v5` has been officially released as a stable version, which means it will be installed by default from the `npm`. In order to do that, open a Terminal in your project's folder and run: ```bash npm2yarn npm install react-native-paper @@ -26,7 +26,7 @@ npm install react-native-paper npm install react-native-safe-area-context ``` -Additionaly for `iOS` platform there is a requirement to link the native parts of the library: +Additionally for `iOS` platform there is a requirement to link the native parts of the library: ```bash npx pod-install @@ -165,7 +165,7 @@ Take a look at the suggested replacement diff: ### Configure fonts -The existing utility called `configureFonts` was adjusted to help users configure their theme fonts in both version, that's why that function, as of today, is going to accept the object with the follwing properties as an argument: +The existing utility called `configureFonts` was adjusted to help users configure their theme fonts in both version, that's why that function, as of today, is going to accept the object with the following properties as an argument: ```ts configureFonts(params); @@ -476,7 +476,7 @@ With the latest version, there is a possibility to specify whether `Drawer.Secti #### FAB -Additionaly `FAB` may be applied in one of three available sizes, thanks to new prop `size`: +Additionally `FAB` may be applied in one of three available sizes, thanks to new prop `size`: - `small` - FAB with small height (40), - `medium` - Appbar with default medium height (56), diff --git a/docs/6.x/docs/guides/getting-started.mdx b/docs/6.x/docs/guides/getting-started.mdx index f5c438c080..89cc84788d 100644 --- a/docs/6.x/docs/guides/getting-started.mdx +++ b/docs/6.x/docs/guides/getting-started.mdx @@ -162,7 +162,7 @@ export default function Main() { ## Customization -You can provide a custom theme to customize the colors, typescales etc. with the `Provider` component. Check the [Material Design 3 default theme](https://github.com/callstack/react-native-paper/blob/main/src/styles/themes/v3/LightTheme.tsx) to see what customization options are supported. +You can provide a custom theme to customize the colors, typescales etc. with the `Provider` component. Check the [Material Design 3 default theme](https://github.com/callstack/react-native-paper/blob/main/src/theme/schemes/LightTheme.tsx) to see what customization options are supported. Example: diff --git a/docs/6.x/docs/guides/theming.mdx b/docs/6.x/docs/guides/theming.mdx index c7eed1703d..b58bf0702d 100644 --- a/docs/6.x/docs/guides/theming.mdx +++ b/docs/6.x/docs/guides/theming.mdx @@ -28,7 +28,7 @@ export default function Main() { } ``` -By default React Native Paper will apply the [Material You theme (MD3)](https://github.com/callstack/react-native-paper/blob/main/src/styles/themes/v3/LightTheme.tsx) if no `theme` or `version` prop is passed to the `PaperProvider`. +By default React Native Paper will apply the [Material You theme (MD3)](https://github.com/callstack/react-native-paper/blob/main/src/theme/schemes/LightTheme.tsx) if no `theme` or `version` prop is passed to the `PaperProvider`. ## Accessing theme properties From 95eacc8e7dfc9ec0d7065457c2ea5e703b9647dd Mon Sep 17 00:00:00 2001 From: fatihcvs <105765934+fatihcvs@users.noreply.github.com> Date: Thu, 29 Oct 2026 19:19:22 +0300 Subject: [PATCH 88/94] docs: prevent dynamic color picker clipping (#5128) Co-authored-by: John Fanidis --- docs/src/css/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 03288b6bcd..2aae0f8b90 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -231,7 +231,7 @@ a .badge .badge-text { overflow: visible; } -.color-picker { +table.color-picker { display: table; width: 100%; overflow: visible; From c2d8a6131d5f922733d6a27fd712f4bffb655aac Mon Sep 17 00:00:00 2001 From: E-HAZMATs Date: Thu, 29 Oct 2026 19:21:34 +0300 Subject: [PATCH 89/94] docs: add missing Platform import in MD3 font section (#5085) The customVariant example under "Material Design 3 -> Using configureFonts helper" uses Platform.select() but never imports Platform from react-native. This throws a "ReferenceError: : Platform is not defined". Added the missing import. Co-authored-by: John Fanidis --- docs/5.x/docs/guides/fonts.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/5.x/docs/guides/fonts.md b/docs/5.x/docs/guides/fonts.md index ffab445e0a..661d6bbf6b 100644 --- a/docs/5.x/docs/guides/fonts.md +++ b/docs/5.x/docs/guides/fonts.md @@ -418,6 +418,7 @@ import { MD3LightTheme, PaperProvider, } from 'react-native-paper'; +import { Platform } from 'react-native'; import App from './App'; const fontConfig = { From 376eebdee0019db73a0f5656b099689ce56ba954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=E1=BA=A7n=20=C4=90=C3=ACnh=20Huy?= Date: Thu, 29 Oct 2026 23:31:47 +0700 Subject: [PATCH 90/94] fix: keep navigation theme type when adapting a single theme (#5136) The light-only and dark-only overloads of adaptNavigationTheme returned the narrow NavigationTheme type, dropping `fonts` and custom colors from the input theme even though they are preserved at runtime. This made the result unassignable to React Navigation's Theme. Return the input theme type, as the overload for both themes already does. Fixes #4751 --- src/core/__tests__/theming.test.tsx | 20 ++++++++++++++++++++ src/theme/provider.tsx | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/core/__tests__/theming.test.tsx b/src/core/__tests__/theming.test.tsx index cb61aef20d..3ae5d44691 100644 --- a/src/core/__tests__/theming.test.tsx +++ b/src/core/__tests__/theming.test.tsx @@ -264,6 +264,26 @@ describe('adaptNavigationTheme', () => { }); }); + it('should keep fonts and custom colors when adapting a single theme', () => { + const { LightTheme: navLight } = adaptNavigationTheme({ + reactNavigationLight: { + ...NavigationThemeWithFonts, + colors: NavigationCustomLightTheme.colors, + }, + }); + const { DarkTheme: navDark } = adaptNavigationTheme({ + reactNavigationDark: { ...NavigationThemeWithFonts, dark: true }, + }); + + expect(navLight.colors.secondary).toBe('rgb(150,45,85)'); + expect(navLight.fonts.regular.fontFamily).toBe( + LightTheme.fonts.bodyMedium.fontFamily + ); + expect(navDark.fonts.regular.fontFamily).toBe( + DarkTheme.fonts.bodyMedium.fontFamily + ); + }); + it('should not expect fonts on theme without fonts', () => { const { LightTheme: navLight, DarkTheme: navDark } = adaptNavigationTheme({ reactNavigationLight: NavigationLightTheme, diff --git a/src/theme/provider.tsx b/src/theme/provider.tsx index 8f7e77681e..2c1f8ca9f9 100644 --- a/src/theme/provider.tsx +++ b/src/theme/provider.tsx @@ -75,14 +75,14 @@ export function adaptNavigationTheme(themes: { reactNavigationLight: T; materialLight?: Theme; }): { - LightTheme: NavigationTheme; + LightTheme: T; }; // eslint-disable-next-line no-redeclare export function adaptNavigationTheme(themes: { reactNavigationDark: T; materialDark?: Theme; }): { - DarkTheme: NavigationTheme; + DarkTheme: T; }; // eslint-disable-next-line no-redeclare export function adaptNavigationTheme< From 9e095e61a8768df59a8de9938d878ec84987c8f6 Mon Sep 17 00:00:00 2001 From: Raphael Fakhri Date: Thu, 29 Oct 2026 20:47:06 +0200 Subject: [PATCH 91/94] fix(list): expand accordion with id 0 in a controlled AccordionGroup (#5145) --- src/components/List/ListAccordionGroup.tsx | 2 +- src/components/__tests__/ListAccordion.test.tsx | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/List/ListAccordionGroup.tsx b/src/components/List/ListAccordionGroup.tsx index 15e148ae6e..f153eb557f 100644 --- a/src/components/List/ListAccordionGroup.tsx +++ b/src/components/List/ListAccordionGroup.tsx @@ -74,7 +74,7 @@ const ListAccordionGroup = ({ return ( diff --git a/src/components/__tests__/ListAccordion.test.tsx b/src/components/__tests__/ListAccordion.test.tsx index ce387e343a..4d594e7bd0 100644 --- a/src/components/__tests__/ListAccordion.test.tsx +++ b/src/components/__tests__/ListAccordion.test.tsx @@ -135,6 +135,22 @@ describe('ListAccordion', () => { await expect(render()).resolves.toBeDefined(); }); + it('expands the accordion with id={0} when it is the controlled expandedId', async () => { + await render( + {}}> + + + + + + + + ); + + expect(screen.getByText('First accordion item')).toBeOnTheScreen(); + expect(screen.queryByText('Second accordion item')).not.toBeOnTheScreen(); + }); + it('should throw an error when id={""}', async () => { const ListAccordionTest = () => ( From 4dbf7d606d4353f32208975cd13fa2517a4672ec Mon Sep 17 00:00:00 2001 From: KuroGeo <32381088+KuroGeo@users.noreply.github.com> Date: Fri, 30 Oct 2026 03:25:39 +0800 Subject: [PATCH 92/94] fix(menu): avoid focusing hidden anchors on mount (#5139) * fix(menu): avoid focusing hidden anchors on mount * test(menu): name animation timer duration --- src/components/Menu/Menu.tsx | 2 +- src/components/__tests__/Menu.test.tsx | 113 ++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 6044956c06..b862a5de88 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -226,7 +226,7 @@ const Menu = ({ const scaleY = useSharedValue(0); const keyboardHeightRef = React.useRef(0); - const prevVisible = React.useRef(null); + const prevVisible = React.useRef(visible); const anchorRef = React.useRef(null); const menuRef = React.useRef(null); const isShownRef = React.useRef(false); diff --git a/src/components/__tests__/Menu.test.tsx b/src/components/__tests__/Menu.test.tsx index c35880b48f..7b02890f78 100644 --- a/src/components/__tests__/Menu.test.tsx +++ b/src/components/__tests__/Menu.test.tsx @@ -1,6 +1,13 @@ -import { Dimensions, StyleSheet, View } from 'react-native'; - -import { expect, it, jest } from '@jest/globals'; +import { Dimensions, Platform, StyleSheet, View } from 'react-native'; + +import { + afterEach, + beforeEach, + describe, + expect, + it, + jest, +} from '@jest/globals'; import { act, screen, waitFor } from '@testing-library/react-native'; import { render } from '../../test-utils'; @@ -10,6 +17,106 @@ import Button from '../Button/Button'; import Menu from '../Menu/Menu'; import Portal from '../Portal/Portal'; +describe('web focus', () => { + const PAST_ANIMATION_MS = 300; + const focus = jest.fn(); + const originalPlatform = Platform.OS; + const originalHTMLElement = Object.getOwnPropertyDescriptor( + globalThis, + 'HTMLElement' + ); + const originalDocument = Object.getOwnPropertyDescriptor( + globalThis, + 'document' + ); + const originalQuerySelector = Object.getOwnPropertyDescriptor( + View.prototype, + 'querySelector' + ); + const originalMeasureInWindow = Object.getOwnPropertyDescriptor( + View.prototype, + 'measureInWindow' + ); + + beforeEach(() => { + Platform.OS = 'web'; + focus.mockClear(); + // Native View refs stand in for DOM elements in the native test renderer. + Object.defineProperty(globalThis, 'HTMLElement', { + configurable: true, + value: View, + }); + Object.defineProperty(globalThis, 'document', { + configurable: true, + value: { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }, + }); + Object.defineProperty(View.prototype, 'querySelector', { + configurable: true, + value: () => ({ focus }), + }); + const measureInWindow: View['measureInWindow'] = (fn) => + fn(100, 100, 80, 32); + Object.defineProperty(View.prototype, 'measureInWindow', { + configurable: true, + value: measureInWindow, + }); + }); + + afterEach(() => { + Platform.OS = originalPlatform; + for (const [target, key, descriptor] of [ + [globalThis, 'HTMLElement', originalHTMLElement], + [globalThis, 'document', originalDocument], + [View.prototype, 'querySelector', originalQuerySelector], + [View.prototype, 'measureInWindow', originalMeasureInWindow], + ] as const) { + if (descriptor) { + Object.defineProperty(target, key, descriptor); + } else { + Reflect.deleteProperty(target, key); + } + } + }); + + const makeMenu = (visible: boolean) => ( + + Open menu}> + + + + ); + + it('does not move focus when mounted hidden', async () => { + await render(makeMenu(false)); + await act(() => jest.advanceTimersByTime(PAST_ANIMATION_MS)); + + expect(focus).not.toHaveBeenCalled(); + }); + + it('moves focus when opened and restores it when closed', async () => { + const { rerender } = await render(makeMenu(false)); + await rerender(makeMenu(true)); + await act(() => jest.advanceTimersByTime(PAST_ANIMATION_MS)); + + expect(focus).toHaveBeenCalledTimes(1); + + await rerender(makeMenu(false)); + await act(() => jest.advanceTimersByTime(PAST_ANIMATION_MS)); + + expect(focus).toHaveBeenCalledTimes(2); + }); + + it('moves focus when mounted visible', async () => { + await render(makeMenu(true)); + await act(() => jest.advanceTimersByTime(PAST_ANIMATION_MS)); + + expect(focus).toHaveBeenCalledTimes(1); + }); +}); + it('renders visible menu', async () => { const tree = ( await render( From f7d25efbbcf2e95543686537b4cf7887441748db Mon Sep 17 00:00:00 2001 From: John Fanidis Date: Mon, 2 Nov 2026 18:10:58 +0200 Subject: [PATCH 93/94] chore: let the contributor label workflow label pull requests (#5153) --- .github/workflows/contributor-label.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/contributor-label.yml b/.github/workflows/contributor-label.yml index 7edb303d97..f5fcb36767 100644 --- a/.github/workflows/contributor-label.yml +++ b/.github/workflows/contributor-label.yml @@ -6,6 +6,7 @@ on: permissions: issues: write + pull-requests: write jobs: label: From 4200dea1573c314ad547a92f0ce9ab86794d17af Mon Sep 17 00:00:00 2001 From: John Fanidis Date: Tue, 3 Nov 2026 09:16:01 +0200 Subject: [PATCH 94/94] docs: install 6.x from the alpha tag in getting started (#5152) The install command now uses the alpha tag, because the untagged package still installs 5.x. The page also states that 6.x requires React 19 and the New Architecture, and the Customization example imports LightTheme instead of the removed MD3LightTheme. The Expo tab now installs @react-native-vector-icons/material-design-icons, since Paper no longer falls back to @expo/vector-icons. --- docs/6.x/docs/guides/getting-started.mdx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/6.x/docs/guides/getting-started.mdx b/docs/6.x/docs/guides/getting-started.mdx index 89cc84788d..45ccaf2c01 100644 --- a/docs/6.x/docs/guides/getting-started.mdx +++ b/docs/6.x/docs/guides/getting-started.mdx @@ -9,12 +9,16 @@ import Tabs from '@docs/components/Tabs'; ## Installation +React Native Paper 6.x requires React 19 and React Native's New Architecture. + Open a Terminal in your project's folder and install the library: ```bash npm2yarn -npm install react-native-paper +npm install react-native-paper@alpha ``` +React Native Paper 6.x is a prerelease published under the npm `alpha` tag, and `npm install react-native-paper` without the tag still installs 5.x. + React Native Paper uses [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) for handling safe area, [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/) and [react-native-worklets](https://docs.swmansion.com/react-native-worklets/) for animations. @@ -32,7 +36,13 @@ Minimum requirements: Follow the [React Native Reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/). -If you use Expo, you don't need to install vector icons - those are the part of the expo package. However, if you have a `babel.config.js` or `.babelrc` file, make sure that it includes `babel-preset-expo`. +You also need to install [@react-native-vector-icons/material-design-icons](https://github.com/oblador/react-native-vector-icons/tree/master/packages/material-design-icons), because some components use these icons internally: + +```bash +npx expo install @react-native-vector-icons/material-design-icons +``` + +If you have a `babel.config.js` or `.babelrc` file, make sure that it includes `babel-preset-expo`. @@ -168,16 +178,13 @@ Example: ```js import * as React from 'react'; -import { - MD3LightTheme as DefaultTheme, - PaperProvider, -} from 'react-native-paper'; +import { LightTheme, PaperProvider } from 'react-native-paper'; import App from './src/App'; const theme = { - ...DefaultTheme, + ...LightTheme, colors: { - ...DefaultTheme.colors, + ...LightTheme.colors, primary: 'tomato', secondary: 'yellow', },
    + Toggle to grab more examples + +