Skip to content

Commit 5365f22

Browse files
authored
Fix support for setting color scheme manually (#1561)
* Create Scheme class to handle all color scheme logic * Refactor change listener methods name * Fix support for changing schemes manually * Add a test for loadSchemes keys mismatch * Fix TS issues in DarkMode example screen * Fix TS properly
1 parent b753357 commit 5365f22

File tree

12 files changed

+331
-96
lines changed

12 files changed

+331
-96
lines changed

demo/src/configurations.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Spacings.loadSpacings({
3636
/* Dark Mode Schemes */
3737
Colors.loadSchemes({
3838
light: {
39-
screenBG: 'transparent',
39+
screenBG: Colors.white,
4040
textColor: Colors.grey10,
4141
moonOrSun: Colors.yellow30,
4242
mountainForeground: Colors.green30,

demo/src/screens/foundationScreens/DarkModeScreen.tsx

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
11
import React, {Component} from 'react';
22
import {StyleSheet} from 'react-native';
3-
import {View, Text, Constants} from 'react-native-ui-lib';
3+
import {View, Text, Colors, Constants, SegmentedControl, SchemeType} from 'react-native-ui-lib';
4+
5+
const SCHEME_TYPES: {label: string; value: SchemeType}[] = [
6+
{label: 'device (default)', value: 'default'},
7+
{label: 'dark', value: 'dark'},
8+
{label: 'light', value: 'light'}
9+
];
10+
11+
const DEVICE_DARK_MODE_MESSAGE = Constants.isIOS
12+
? 'Change to dark mode in simulator by pressing Cmd+Shift+A'
13+
: 'Change to dark mode';
14+
const MANUAL_DARK_MODE_MESSAGE = 'Setting the scheme manually will ignore device settings';
415

516
class DarkModeScreen extends Component {
6-
state = {};
17+
state = {
18+
selectedSchemeType: 'default'
19+
};
20+
21+
changeSchemeType = (index: number) => {
22+
this.setState({selectedSchemeType: SCHEME_TYPES[index].value});
23+
Colors.setScheme(SCHEME_TYPES[index].value);
24+
};
25+
726
render() {
27+
const {selectedSchemeType} = this.state;
28+
const message = selectedSchemeType === 'default' ? DEVICE_DARK_MODE_MESSAGE : MANUAL_DARK_MODE_MESSAGE;
29+
830
return (
931
<View flex padding-page bg-screenBG>
1032
<Text h1 textColor>
1133
Dark Mode
1234
</Text>
13-
{Constants.isIOS ? (
14-
<Text marginT-s2 body textColor>
15-
Change to dark mode in simulator by pressing Cmd+Shift+A
16-
</Text>
17-
) : (
18-
<Text marginT-s2 body textColor>Change to dark mode</Text>
19-
)}
35+
<SegmentedControl
36+
containerStyle={{alignSelf: 'flex-start', marginTop: 20}}
37+
segments={SCHEME_TYPES}
38+
onChangeIndex={this.changeSchemeType}
39+
/>
40+
41+
<Text marginT-s2 body textColor>
42+
{message}
43+
</Text>
2044

2145
<View style={styles.moonOrSun} bg-moonOrSun/>
2246
<View style={[styles.mountain, styles.mountainBackground]} bg-mountainBackground/>

generatedTypes/src/components/connectionStatusBar/index.d.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,6 @@ declare class ConnectionStatusBar extends PureComponent<ConnectionStatusBarProps
3030
}
3131
export { ConnectionStatusBar };
3232
declare const _default: React.ComponentClass<ConnectionStatusBarProps & {
33-
/**
34-
* @description: Top bar to show a "no internet" connection status. Note: Run on real device for best results
35-
* @image: https://user-images.githubusercontent.com/33805983/34683190-f3b1904c-f4a9-11e7-9d46-9a340bd35448.png, https://user-images.githubusercontent.com/33805983/34484206-edc6c6e4-efcb-11e7-88b2-cd394c19dd5e.png
36-
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/ConnectionStatusBarScreen.tsx
37-
* @notes: The component requires installing the '@react-native-community/netinfo' native library
38-
*/
3933
useCustomTheme?: boolean | undefined;
4034
}, any> & typeof ConnectionStatusBar;
4135
export default _default;

generatedTypes/src/style/colors.d.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import _ from 'lodash';
22
import tinycolor from 'tinycolor2';
3-
declare type Schemes = {
4-
light: {
5-
[key: string]: string;
6-
};
7-
dark: {
8-
[key: string]: string;
9-
};
10-
};
11-
declare type SchemeType = 'default' | 'light' | 'dark';
3+
import { Schemes, SchemeType } from './scheme';
124
export declare class Colors {
135
[key: string]: any;
14-
schemes: Schemes;
15-
currentScheme: SchemeType;
166
constructor();
177
/**
188
* Load custom set of colors
@@ -107,6 +97,9 @@ declare const colorObject: Colors & {
10797
orange30: string;
10898
orange40: string;
10999
orange50: string;
100+
/**
101+
* Get app's current color scheme
102+
*/
110103
orange60: string;
111104
orange70: string;
112105
orange80: string;
@@ -120,6 +113,14 @@ declare const colorObject: Colors & {
120113
red80: string;
121114
purple10: string;
122115
purple20: string;
116+
/**
117+
* Add alpha to hex or rgb color
118+
* arguments:
119+
* p1 - hex color / R part of RGB
120+
* p2 - opacity / G part of RGB
121+
* p3 - B part of RGB
122+
* p4 - opacity
123+
*/
123124
purple30: string;
124125
purple40: string;
125126
purple50: string;
@@ -138,11 +139,6 @@ declare const colorObject: Colors & {
138139
black: string;
139140
transparent: string;
140141
} & {
141-
/**
142-
* Set color scheme for app
143-
* arguments:
144-
* scheme - color scheme e.g light/dark/default
145-
*/
146142
primary: string;
147143
};
148144
export default colorObject;

generatedTypes/src/style/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { default as Colors } from './colors';
2+
export { default as Scheme, SchemeType, Schemes, SchemeChangeListener } from './scheme';
23
export { default as Typography } from './typography';
34
export { default as BorderRadiuses } from './borderRadiuses';
45
export { default as Shadows } from './shadows';

generatedTypes/src/style/scheme.d.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
export declare type Schemes = {
2+
light: {
3+
[key: string]: string;
4+
};
5+
dark: {
6+
[key: string]: string;
7+
};
8+
};
9+
export declare type SchemeType = 'default' | 'light' | 'dark';
10+
export declare type SchemeChangeListener = (schemeType?: 'light' | 'dark') => void;
11+
declare class Scheme {
12+
currentScheme: SchemeType;
13+
schemes: Schemes;
14+
changeListeners: SchemeChangeListener[];
15+
constructor();
16+
private broadcastSchemeChange;
17+
/**
18+
* Get app's current color scheme
19+
*/
20+
getSchemeType(): 'light' | 'dark';
21+
/**
22+
* Set color scheme for app
23+
* arguments:
24+
* scheme - color scheme e.g light/dark/default
25+
*/
26+
setScheme(scheme: SchemeType): void;
27+
/**
28+
* Load set of schemes for light/dark mode
29+
* arguments:
30+
* schemes - two sets of map of colors e.g {light: {screen: 'white'}, dark: {screen: 'black'}}
31+
*/
32+
loadSchemes(schemes: Schemes): void;
33+
/**
34+
* Retrieve scheme by current scheme type
35+
*/
36+
getScheme(): {
37+
[key: string]: string;
38+
} | {
39+
[key: string]: string;
40+
};
41+
/**
42+
* Add a change scheme event listener
43+
*/
44+
addChangeListener(listener: SchemeChangeListener): void;
45+
/**
46+
* Remove a change scheme event listener
47+
* arguments:
48+
* listener - listener reference to remove
49+
*/
50+
removeChangeListener(listener: SchemeChangeListener): void;
51+
}
52+
declare const _default: Scheme;
53+
export default _default;

src/commons/asBaseComponent.tsx

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
2-
import {Appearance, EventSubscription} from 'react-native';
32
import hoistStatics from 'hoist-non-react-statics';
43
import * as Modifiers from './modifiers';
4+
import {Scheme} from '../style';
55
import forwardRef from './forwardRef';
66
import UIComponent from './UIComponent';
77

@@ -27,32 +27,19 @@ function asBaseComponent<PROPS, STATICS = {}>(WrappedComponent: React.ComponentT
2727
error: false
2828
};
2929

30-
appearanceListenerSubscription?: EventSubscription = undefined;
31-
3230
componentDidMount() {
33-
// < RN64 - void
34-
// >= RN65 - EventSubscription
35-
const subscription = Appearance.addChangeListener(this.appearanceListener);
36-
if (subscription !== undefined) {
37-
// @ts-ignore
38-
this.appearanceListenerSubscription = subscription;
39-
}
31+
Scheme.addChangeListener(this.appearanceListener);
4032
}
4133

4234
componentWillUnmount() {
43-
if (this.appearanceListenerSubscription) {
44-
// >=RN65
45-
this.appearanceListenerSubscription?.remove();
46-
} else {
47-
// <RN65
48-
Appearance.removeChangeListener(this.appearanceListener);
49-
}
35+
Scheme.removeChangeListener(this.appearanceListener);
5036
}
5137

52-
appearanceListener: Appearance.AppearanceListener = () => {
38+
appearanceListener = () => {
5339
// iOS 13 and above will trigger this call with the wrong colorScheme value. So just ignore returned colorScheme for now
5440
// https://github.com/facebook/react-native/issues/28525
55-
this.setState({colorScheme: Appearance.getColorScheme()});
41+
// this.setState({colorScheme: Appearance.getColorScheme()});
42+
this.setState({colorScheme: Scheme.getSchemeType()});
5643
};
5744

5845
static getThemeProps = (props: any, context: any) => {

src/commons/modifiers.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import _ from 'lodash';
22
import {StyleSheet} from 'react-native';
3-
import {Typography, Colors, BorderRadiuses, Spacings, ThemeManager} from '../style';
3+
import {Typography, Colors, Scheme, BorderRadiuses, Spacings, ThemeManager} from '../style';
44
import {BorderRadiusesLiterals} from '../style/borderRadiuses';
55
import TypographyPresets from '../style/typographyPresets';
66
import {SpacingLiterals} from '../style/spacings';
@@ -58,7 +58,6 @@ const STYLE_KEY_CONVERTERS = {
5858
flexS: 'flexShrink'
5959
} as const;
6060

61-
6261
export type PaddingLiterals = keyof typeof PADDING_VARIATIONS;
6362
export type NativePaddingKeyType = typeof PADDING_VARIATIONS[PaddingLiterals];
6463
export type MarginLiterals = keyof typeof MARGIN_VARIATIONS;
@@ -69,12 +68,18 @@ export type ColorLiterals = keyof typeof colorsPalette;
6968
export type TypographyLiterals = keyof typeof TypographyPresets;
7069
export type BorderRadiusLiterals = keyof typeof BorderRadiusesLiterals;
7170
export type AlignmentLiterals =
72-
| 'row' | 'spread'
73-
| 'center' | 'centerH' | 'centerV'
74-
| 'left' | 'right' | 'top' | 'bottom';
71+
| 'row'
72+
| 'spread'
73+
| 'center'
74+
| 'centerH'
75+
| 'centerV'
76+
| 'left'
77+
| 'right'
78+
| 'top'
79+
| 'bottom';
7580
export type PositionLiterals = 'absF' | 'absL' | 'absR' | 'absT' | 'absB' | 'absV' | 'absH';
7681

77-
export type Modifier<T extends string> = Partial<Record<T, boolean>>
82+
export type Modifier<T extends string> = Partial<Record<T, boolean>>;
7883
export type CustomModifier = {[key: string]: boolean};
7984

8085
export type TypographyModifiers = Modifier<TypographyLiterals> | CustomModifier;
@@ -87,8 +92,7 @@ export type MarginModifiers = Modifier<MarginLiterals>;
8792
export type FlexModifiers = Modifier<FlexLiterals>;
8893
export type BorderRadiusModifiers = Modifier<BorderRadiusLiterals>;
8994

90-
export type ContainerModifiers =
91-
AlignmentModifiers &
95+
export type ContainerModifiers = AlignmentModifiers &
9296
PositionModifiers &
9397
PaddingModifiers &
9498
MarginModifiers &
@@ -97,8 +101,7 @@ export type ContainerModifiers =
97101
BackgroundColorModifier;
98102

99103
export function extractColorValue(props: Dictionary<any>) {
100-
const scheme = Colors.getScheme();
101-
const schemeColors = Colors.schemes[scheme];
104+
const schemeColors = Scheme.getScheme();
102105
const allColorsKeys: Array<keyof typeof Colors> = [..._.keys(Colors), ..._.keys(schemeColors)];
103106
const colorPropsKeys = _.chain(props)
104107
.keys()
@@ -110,8 +113,8 @@ export function extractColorValue(props: Dictionary<any>) {
110113

111114
export function extractBackgroundColorValue(props: Dictionary<any>) {
112115
let backgroundColor;
113-
const scheme = Colors.getScheme();
114-
const schemeColors = Colors.schemes[scheme];
116+
117+
const schemeColors = Scheme.getScheme();
115118

116119
const keys = Object.keys(props);
117120
const bgProp = _.findLast(keys, prop => Colors.getBackgroundKeysPattern().test(prop) && !!props[prop])!;
@@ -243,7 +246,7 @@ export function extractPositionStyle(props: Dictionary<any>) {
243246
}
244247
style = {...style, ...styles.absolute};
245248
});
246-
249+
247250
return _.isEmpty(style) ? undefined : style;
248251
}
249252

@@ -366,8 +369,8 @@ export function generateModifiersStyle(options = {
366369
alignments: true,
367370
flex: true,
368371
position: true
369-
}, props: Dictionary<any>) {
370-
372+
},
373+
props: Dictionary<any>) {
371374
//@ts-ignore
372375
const boundProps = props || this.props;
373376
const style: ExtractedStyle = {};

0 commit comments

Comments
 (0)