Skip to content

Commit 66235db

Browse files
brunohkbxvonovakbelgamo
authored
feat: create expo plugin to customize native style (#801)
* feat: create expo plugin to customize native style * Update docs/android-styling.md Co-authored-by: Gabriel Belgamo <[email protected]> * 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 <[email protected]> Co-authored-by: Gabriel Belgamo <[email protected]>
1 parent ecd680e commit 66235db

File tree

13 files changed

+5790
-171
lines changed

13 files changed

+5790
-171
lines changed

.circleci/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jobs:
1313
executor:
1414
name: rn/linux_js
1515
node_version: 'lts'
16+
resource_class: large
1617
steps:
1718
- checkout
1819
# - rn/yarn_install
@@ -180,6 +181,9 @@ jobs:
180181
- run:
181182
command: yarn install --immutable
182183
name: yarn install
184+
- run:
185+
command: yarn plugin:build
186+
name: build expo config plugin
183187
- run:
184188
command: npx semantic-release
185189
name: Publish to NPM

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module.exports = {
22
root: true,
3-
extends: '@react-native',
3+
extends: ['@react-native', 'plugin:jest/recommended'],
44
globals: {
55
expect: true,
66
element: true,
@@ -16,5 +16,6 @@ module.exports = {
1616
},
1717
rules: {
1818
'no-var': 2,
19+
'jest/no-conditional-expect': 0,
1920
},
2021
};

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ React Native date & time picker component for iOS, Android and Windows (please n
7777
- [React Native Support](#react-native-support)
7878
- [Localization note](#localization-note)
7979
- [Android imperative API](#android-imperative-api)
80+
- [Android styling](#android-styling)
8081
- [Props / params](#component-props--params-of-the-android-imperative-api)
8182
- [`mode` (`optional`)](#mode-optional)
8283
- [`display` (`optional`)](#display-optional)
@@ -288,6 +289,12 @@ DateTimePickerAndroid.dismiss(mode: AndroidNativeProps['mode'])
288289

289290
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.
290291

292+
### Android styling
293+
294+
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.
295+
296+
Refer to this documentation for more information: [android-styling.md](/docs/android-styling.md).
297+
291298
## Component props / params of the Android imperative api
292299

293300
> 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.

app.plugin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./plugin/build/withDateTimePickerStyles');

docs/android-styling.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
## Android Styling
2+
3+
To use the features documented here, you need to use a [Expo Development build](https://docs.expo.dev/develop/development-builds/introduction/).
4+
5+
Make changes as documented below and then run the following commands to see the updated colors:
6+
7+
- `npx expo prebuild -p android --clean` (apply configuration to the native code)
8+
- `expo run:android` (build the native code)
9+
10+
### Configuration in app.json / app.config.js
11+
12+
```json
13+
{
14+
"expo": {
15+
"plugins": [
16+
"@react-native-community/datetimepicker",
17+
{
18+
"android": {
19+
"datePicker": {
20+
"colorAccent": {
21+
"light": "#FF5722"
22+
},
23+
"textColorPrimary": {
24+
"light": "#FF5722"
25+
}
26+
},
27+
"timePicker": {
28+
"background": {"light": "#FF5722", "dark": "#383838"},
29+
"numbersBackgroundColor": {"light": "#FF5722", "dark": "#383838"}
30+
}
31+
}
32+
}
33+
]
34+
}
35+
}
36+
```
37+
38+
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.
39+
40+
### Configurable properties
41+
42+
The following illustrations show the different styles that can be applied to the date and time pickers.
43+
44+
| DatePickerDialog | TimePickerDialog |
45+
| -------------------------------------------------------------------------- | ------------------------------------------------------------ |
46+
| ![Date picker dialog breakdown](./images/date_picker_dialog_breakdown.png) | ![Time picker breakdown](./images/time_picker_breakdown.png) |
47+
48+
#### DatePickerDialog
49+
50+
| Property | Attribute Name |
51+
| ---------------------------------- | ------------------------------------------ |
52+
| colorAccent | colorAccent |
53+
| colorControlActivated | colorControlActivated |
54+
| colorControlHighlight | colorControlHighlight |
55+
| selectableItemBackgroundBorderless | android:selectableItemBackgroundBorderless |
56+
| textColor | android:textColor |
57+
| textColorPrimary | android:textColorPrimary |
58+
| textColorPrimaryInverse | android:textColorPrimaryInverse |
59+
| textColorSecondary | android:textColorSecondary |
60+
| textColorSecondaryInverse | android:textColorSecondaryInverse |
61+
| windowBackground | android:windowBackground |
62+
63+
#### TimePickerDialog
64+
65+
| Property | Attribute Name |
66+
| ---------------------- | ------------------------------ |
67+
| background | android:background |
68+
| headerBackground | android:headerBackground |
69+
| numbersBackgroundColor | android:numbersBackgroundColor |
70+
| numbersSelectorColor | android:numbersSelectorColor |
71+
| numbersTextColor | android:numbersTextColor |
79 KB
Loading
60.4 KB
Loading

package.json

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
"!ios/build",
1717
"!**/__tests__",
1818
"!**/__fixtures__",
19-
"!**/__mocks__"
19+
"!**/__mocks__",
20+
"plugin/build",
21+
"app.plugin.js"
2022
],
2123
"publishConfig": {
2224
"access": "public"
@@ -39,7 +41,8 @@
3941
"detox:android:test:debug": "adb shell service call alarm 3 s16 Europe/Prague && detox test -c android.emu.debug -l verbose",
4042
"detox:android:build:release": "detox build -c android.emu.release",
4143
"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",
42-
"detox:clean": "rimraf example/android/build && rimraf example/android/app/build && rimraf example/android/.gradle && rimraf example/ios/build"
44+
"detox:clean": "rimraf example/android/build && rimraf example/android/app/build && rimraf example/android/.gradle && rimraf example/ios/build",
45+
"plugin:build": "expo-module build plugin"
4346
},
4447
"repository": {
4548
"type": "git",
@@ -76,9 +79,12 @@
7679
"@testing-library/react-native": "9.1.0",
7780
"babel-jest": "^29.5.0",
7881
"detox": "^20.19.3",
79-
"eslint": "^8.42.0",
82+
"eslint": "^8.56.0",
8083
"eslint-plugin-ft-flow": "^2.0.1",
84+
"eslint-plugin-jest": "^28.6.0",
8185
"eslint-plugin-prettier": "^4.2.1",
86+
"expo": "^51.0.18",
87+
"expo-module-scripts": "^3.5.2",
8288
"flow-bin": "^0.217.0",
8389
"flow-typed": "^3.9.0",
8490
"jest": "^29.5.0",
@@ -91,28 +97,33 @@
9197
"react-native-test-app": "^3.5.3",
9298
"react-native-windows": "^0.73.0",
9399
"react-test-renderer": "18.2.0",
94-
"semantic-release": "^19.0.3"
100+
"semantic-release": "^19.0.3",
101+
"typescript": "^5.5.3"
95102
},
96103
"dependencies": {
97104
"invariant": "^2.2.4"
98105
},
99-
"codegenConfig": {
100-
"name": "RNDateTimePickerCGen",
101-
"type": "all",
102-
"jsSrcsDir": "src/specs",
103-
"android": {
104-
"javaPackageName": "com.reactcommunity.rndatetimepicker"
105-
}
106-
},
107106
"peerDependencies": {
107+
"expo": ">=50.0.0",
108108
"react": "*",
109109
"react-native": "*",
110110
"react-native-windows": "*"
111111
},
112112
"peerDependenciesMeta": {
113+
"expo": {
114+
"optional": true
115+
},
113116
"react-native-windows": {
114117
"optional": true
115118
}
116119
},
120+
"codegenConfig": {
121+
"name": "RNDateTimePickerCGen",
122+
"type": "all",
123+
"jsSrcsDir": "src/specs",
124+
"android": {
125+
"javaPackageName": "com.reactcommunity.rndatetimepicker"
126+
}
127+
},
117128
"packageManager": "[email protected]"
118129
}

0 commit comments

Comments
 (0)