Skip to content

Commit

Permalink
feat: create expo plugin to customize native style (#801)
Browse files Browse the repository at this point in the history
* feat: create expo plugin to customize native style

* Update docs/android-styling.md

Co-authored-by: Gabriel Belgamo <19699724+belgamo@users.noreply.github.com>

* refactor: reduce some duplicity

* refactor: change the config plugin interface

* refactor: remove npmignore

* chore: build plugin in CI

* doc updates

* make images readable on dark bg

* fix lint

* chore: update eslint

* chore: update resource_class

---------

Co-authored-by: Vojtech Novak <vonovak@gmail.com>
Co-authored-by: Gabriel Belgamo <19699724+belgamo@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 9, 2024
1 parent ecd680e commit 66235db
Show file tree
Hide file tree
Showing 13 changed files with 5,790 additions and 171 deletions.
4 changes: 4 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
executor:
name: rn/linux_js
node_version: 'lts'
resource_class: large
steps:
- checkout
# - rn/yarn_install
Expand Down Expand Up @@ -180,6 +181,9 @@ jobs:
- run:
command: yarn install --immutable
name: yarn install
- run:
command: yarn plugin:build
name: build expo config plugin
- run:
command: npx semantic-release
name: Publish to NPM
Expand Down
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
root: true,
extends: '@react-native',
extends: ['@react-native', 'plugin:jest/recommended'],
globals: {
expect: true,
element: true,
Expand All @@ -16,5 +16,6 @@ module.exports = {
},
rules: {
'no-var': 2,
'jest/no-conditional-expect': 0,
},
};
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ React Native date & time picker component for iOS, Android and Windows (please n
- [React Native Support](#react-native-support)
- [Localization note](#localization-note)
- [Android imperative API](#android-imperative-api)
- [Android styling](#android-styling)
- [Props / params](#component-props--params-of-the-android-imperative-api)
- [`mode` (`optional`)](#mode-optional)
- [`display` (`optional`)](#display-optional)
Expand Down Expand Up @@ -288,6 +289,12 @@ DateTimePickerAndroid.dismiss(mode: AndroidNativeProps['mode'])

The reason we recommend the imperative API is: on Android, the date/time picker opens in a dialog, similar to `ReactNative.alert()` from core react native. The imperative api models this behavior better than the declarative component api. While the component approach is perfectly functional, based on the issue tracker history, it appears to be more prone to introducing bugs.

### Android styling

Styling of the dialogs on Android can be easily customized by using the provided config plugin, provided that you use a [Expo development build](https://docs.expo.dev/develop/development-builds/introduction/). The plugin allows you to configure color properties that cannot be set at runtime and requires building a new app binary to take effect.

Refer to this documentation for more information: [android-styling.md](/docs/android-styling.md).

## Component props / params of the Android imperative api

> Please note that this library currently exposes functionality from [`UIDatePicker`](https://developer.apple.com/documentation/uikit/uidatepicker?language=objc) on iOS and [DatePickerDialog](https://developer.android.com/reference/android/app/DatePickerDialog) + [TimePickerDialog](https://developer.android.com/reference/android/app/TimePickerDialog) on Android, and [`CalendarDatePicker`](https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/calendar-date-picker) + [TimePicker](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.timepicker?view=winrt-19041) on Windows.
Expand Down
1 change: 1 addition & 0 deletions app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./plugin/build/withDateTimePickerStyles');
71 changes: 71 additions & 0 deletions docs/android-styling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
## Android Styling

To use the features documented here, you need to use a [Expo Development build](https://docs.expo.dev/develop/development-builds/introduction/).

Make changes as documented below and then run the following commands to see the updated colors:

- `npx expo prebuild -p android --clean` (apply configuration to the native code)
- `expo run:android` (build the native code)

### Configuration in app.json / app.config.js

```json
{
"expo": {
"plugins": [
"@react-native-community/datetimepicker",
{
"android": {
"datePicker": {
"colorAccent": {
"light": "#FF5722"
},
"textColorPrimary": {
"light": "#FF5722"
}
},
"timePicker": {
"background": {"light": "#FF5722", "dark": "#383838"},
"numbersBackgroundColor": {"light": "#FF5722", "dark": "#383838"}
}
}
}
]
}
}
```

It's not possible to specify a color only for dark mode. If you wish to influence dark mode color you must declare a value for both the `light` and `dark` modes. Plugin will throw an error otherwise. Plugin also validates that the color names you specify (e.g. `textColorPrimary`) are valid.

### Configurable properties

The following illustrations show the different styles that can be applied to the date and time pickers.

| DatePickerDialog | TimePickerDialog |
| -------------------------------------------------------------------------- | ------------------------------------------------------------ |
| ![Date picker dialog breakdown](./images/date_picker_dialog_breakdown.png) | ![Time picker breakdown](./images/time_picker_breakdown.png) |

#### DatePickerDialog

| Property | Attribute Name |
| ---------------------------------- | ------------------------------------------ |
| colorAccent | colorAccent |
| colorControlActivated | colorControlActivated |
| colorControlHighlight | colorControlHighlight |
| selectableItemBackgroundBorderless | android:selectableItemBackgroundBorderless |
| textColor | android:textColor |
| textColorPrimary | android:textColorPrimary |
| textColorPrimaryInverse | android:textColorPrimaryInverse |
| textColorSecondary | android:textColorSecondary |
| textColorSecondaryInverse | android:textColorSecondaryInverse |
| windowBackground | android:windowBackground |

#### TimePickerDialog

| Property | Attribute Name |
| ---------------------- | ------------------------------ |
| background | android:background |
| headerBackground | android:headerBackground |
| numbersBackgroundColor | android:numbersBackgroundColor |
| numbersSelectorColor | android:numbersSelectorColor |
| numbersTextColor | android:numbersTextColor |
Binary file added docs/images/date_picker_dialog_breakdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/time_picker_breakdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 23 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"!ios/build",
"!**/__tests__",
"!**/__fixtures__",
"!**/__mocks__"
"!**/__mocks__",
"plugin/build",
"app.plugin.js"
],
"publishConfig": {
"access": "public"
Expand All @@ -39,7 +41,8 @@
"detox:android:test:debug": "adb shell service call alarm 3 s16 Europe/Prague && detox test -c android.emu.debug -l verbose",
"detox:android:build:release": "detox build -c android.emu.release",
"detox:android:test:release": "adb shell service call alarm 3 s16 Europe/Prague && detox test -c android.emu.release --record-videos all --record-logs all --headless -l verbose",
"detox:clean": "rimraf example/android/build && rimraf example/android/app/build && rimraf example/android/.gradle && rimraf example/ios/build"
"detox:clean": "rimraf example/android/build && rimraf example/android/app/build && rimraf example/android/.gradle && rimraf example/ios/build",
"plugin:build": "expo-module build plugin"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -76,9 +79,12 @@
"@testing-library/react-native": "9.1.0",
"babel-jest": "^29.5.0",
"detox": "^20.19.3",
"eslint": "^8.42.0",
"eslint": "^8.56.0",
"eslint-plugin-ft-flow": "^2.0.1",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-prettier": "^4.2.1",
"expo": "^51.0.18",
"expo-module-scripts": "^3.5.2",
"flow-bin": "^0.217.0",
"flow-typed": "^3.9.0",
"jest": "^29.5.0",
Expand All @@ -91,28 +97,33 @@
"react-native-test-app": "^3.5.3",
"react-native-windows": "^0.73.0",
"react-test-renderer": "18.2.0",
"semantic-release": "^19.0.3"
"semantic-release": "^19.0.3",
"typescript": "^5.5.3"
},
"dependencies": {
"invariant": "^2.2.4"
},
"codegenConfig": {
"name": "RNDateTimePickerCGen",
"type": "all",
"jsSrcsDir": "src/specs",
"android": {
"javaPackageName": "com.reactcommunity.rndatetimepicker"
}
},
"peerDependencies": {
"expo": ">=50.0.0",
"react": "*",
"react-native": "*",
"react-native-windows": "*"
},
"peerDependenciesMeta": {
"expo": {
"optional": true
},
"react-native-windows": {
"optional": true
}
},
"codegenConfig": {
"name": "RNDateTimePickerCGen",
"type": "all",
"jsSrcsDir": "src/specs",
"android": {
"javaPackageName": "com.reactcommunity.rndatetimepicker"
}
},
"packageManager": "yarn@4.1.1"
}
Loading

0 comments on commit 66235db

Please sign in to comment.