@codeandmoney/soelma 0.0.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.eslintignore +1 -0
  2. package/.eslintrc.js +31 -0
  3. package/.github/workflows/nodejs.yml +33 -0
  4. package/.size-limit.json +6 -0
  5. package/CHANGELOG.md +90 -0
  6. package/LICENSE +21 -0
  7. package/README.md +169 -0
  8. package/babel.config.js +3 -0
  9. package/docs/api.md +103 -0
  10. package/docs/appearance.md +54 -0
  11. package/docs/dark-mode.md +46 -0
  12. package/docs/dimensions.md +29 -0
  13. package/docs/i18n.md +67 -0
  14. package/docs/logo.png +0 -0
  15. package/docs/media-query.md +274 -0
  16. package/docs/orientation.md +44 -0
  17. package/docs/safe-area.md +62 -0
  18. package/docs/testting.md +51 -0
  19. package/docs/ts.md +127 -0
  20. package/example/AppStyleX/.watchmanconfig +1 -0
  21. package/example/AppStyleX/android/build.gradle +26 -0
  22. package/example/AppStyleX/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  23. package/example/AppStyleX/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  24. package/example/AppStyleX/android/gradle.properties +53 -0
  25. package/example/AppStyleX/android/gradlew +249 -0
  26. package/example/AppStyleX/android/gradlew.bat +92 -0
  27. package/example/AppStyleX/android/settings.gradle +12 -0
  28. package/example/AppStyleX/app.json +28 -0
  29. package/example/AppStyleX/babel.config.js +3 -0
  30. package/example/AppStyleX/index.js +9 -0
  31. package/example/AppStyleX/ios/Podfile +7 -0
  32. package/example/AppStyleX/ios/Podfile.lock +1252 -0
  33. package/example/AppStyleX/metro.config.js +54 -0
  34. package/example/AppStyleX/package.json +43 -0
  35. package/example/AppStyleX/react-native.config.js +23 -0
  36. package/example/AppStyleX/src/App.tsx +25 -0
  37. package/example/AppStyleX/src/BottomNav/index.tsx +32 -0
  38. package/example/AppStyleX/src/BottomNav/styles.ts +42 -0
  39. package/example/AppStyleX/src/Circle/index.tsx +53 -0
  40. package/example/AppStyleX/src/Circle/styles.ts +22 -0
  41. package/example/AppStyleX/src/Root/index.tsx +41 -0
  42. package/example/AppStyleX/src/Root/styles.ts +18 -0
  43. package/example/AppStyleX/src/ToggleButton/index.tsx +66 -0
  44. package/example/AppStyleX/src/ToggleButton/styles.ts +69 -0
  45. package/example/AppStyleX/src/style-system/hooks/useAnimatedBgColor.ts +5 -0
  46. package/example/AppStyleX/src/style-system/hooks/useAnimatedTextColor.ts +5 -0
  47. package/example/AppStyleX/src/style-system/hooks/useIsDark.ts +8 -0
  48. package/example/AppStyleX/src/style-system/palette.ts +11 -0
  49. package/example/AppStyleX/src/style-system/theme.ts +14 -0
  50. package/example/AppStyleX/src/style-system/utils.ts +11 -0
  51. package/example/AppStyleX/src/stylex.d.ts +6 -0
  52. package/example/AppStyleX/tsconfig.json +3 -0
  53. package/example/AppStyleX/yarn.lock +6767 -0
  54. package/jest.config.js +19 -0
  55. package/package.json +59 -0
  56. package/src/DefaultTheme.ts +4 -0
  57. package/src/__tests__/createBreakpoints.test.ts +152 -0
  58. package/src/__tests__/createBreakpointsMatcher.test.ts +188 -0
  59. package/src/__tests__/createBreakpointsMatcher.types-test.ts +81 -0
  60. package/src/__tests__/createEventEmitter.test.ts +37 -0
  61. package/src/__tests__/dark-mode.test.ts +56 -0
  62. package/src/__tests__/dependencyRegistry.test.ts +16 -0
  63. package/src/__tests__/dependencyUsage.test.ts +13 -0
  64. package/src/__tests__/dimensions.test.ts +36 -0
  65. package/src/__tests__/makeUseStyles.types-test.ts +69 -0
  66. package/src/__tests__/media-query.test.ts +204 -0
  67. package/src/__tests__/orientation.test.ts +61 -0
  68. package/src/__tests__/useTheme.test.ts +26 -0
  69. package/src/__tests__/withStyles.types-test.tsx +173 -0
  70. package/src/appearance/consts.ts +1 -0
  71. package/src/appearance/index.ts +37 -0
  72. package/src/appearance/init.ts +12 -0
  73. package/src/context.ts +9 -0
  74. package/src/createEventEmitter.ts +26 -0
  75. package/src/dark-mode/consts.ts +1 -0
  76. package/src/dark-mode/index.ts +29 -0
  77. package/src/dark-mode/init.ts +19 -0
  78. package/src/dark-mode/state.ts +5 -0
  79. package/src/dependencyRegistry.ts +21 -0
  80. package/src/dependencyUsage.ts +31 -0
  81. package/src/dimensions/consts.ts +2 -0
  82. package/src/dimensions/index.ts +20 -0
  83. package/src/dimensions/init.ts +37 -0
  84. package/src/dimensions/utils.ts +11 -0
  85. package/src/i18n.ts +18 -0
  86. package/src/index.ts +7 -0
  87. package/src/makeUseStyles/createUseStylesTheme.js +42 -0
  88. package/src/makeUseStyles/createUseStylesTheme.test.js +137 -0
  89. package/src/makeUseStyles/createUseStylesWithoutTheme.js +38 -0
  90. package/src/makeUseStyles/createUseStylesWithoutTheme.test.js +63 -0
  91. package/src/makeUseStyles/index.d.ts +7 -0
  92. package/src/makeUseStyles/index.js +12 -0
  93. package/src/makeUseStyles/index.test.js +28 -0
  94. package/src/makeUseStyles/test-type.js +28 -0
  95. package/src/makeUseStyles/utils.js +67 -0
  96. package/src/media-query/base.ts +43 -0
  97. package/src/media-query/breakpoints.ts +121 -0
  98. package/src/media-query/index.ts +12 -0
  99. package/src/orientation.ts +17 -0
  100. package/src/safe-area/SafeAreaProvider.tsx +17 -0
  101. package/src/safe-area/StylexSaveAreaConsumer.ts +23 -0
  102. package/src/safe-area/consts.ts +1 -0
  103. package/src/safe-area/eventEmitter.ts +4 -0
  104. package/src/safe-area/index.tsx +16 -0
  105. package/src/safe-area/init.tsx +6 -0
  106. package/src/safe-area/state.ts +12 -0
  107. package/src/safe-area/types.ts +10 -0
  108. package/src/useColorTransition.ts +50 -0
  109. package/src/useTheme.ts +16 -0
  110. package/src/withStyles.tsx +35 -0
  111. package/tsconfig.json +30 -0
@@ -0,0 +1,69 @@
1
+ import { makeUseStyles } from "../makeUseStyles";
2
+
3
+ {
4
+ const useStyles = makeUseStyles(() => ({
5
+ root: {
6
+ textAlign: "center",
7
+ },
8
+ }));
9
+
10
+ const a: "center" = useStyles().root.textAlign;
11
+
12
+ // @ts-expect-error: expect literal type
13
+ const b: "not center" = useStyles().root.textAlign;
14
+ }
15
+
16
+ {
17
+ interface Theme {
18
+ color: string;
19
+ }
20
+
21
+ const useStyles = makeUseStyles(({ color }: Theme) => ({
22
+ root: {
23
+ color,
24
+ textAlign: "center",
25
+ },
26
+ }));
27
+
28
+ const a: "center" = useStyles().root.textAlign;
29
+
30
+ // @ts-expect-error: expect literal type
31
+ const b: "not center" = useStyles().root.textAlign;
32
+ }
33
+
34
+ {
35
+ const useStyles = makeUseStyles(() => ({
36
+ root: {
37
+ // @ts-expect-error: invalid color value
38
+ color: [],
39
+ },
40
+ }));
41
+
42
+ const {
43
+ root,
44
+ // @ts-expect-error: 'otherProps' not exist
45
+ otherProp,
46
+ } = useStyles();
47
+ }
48
+
49
+ {
50
+ interface MyTheme {
51
+ color: {
52
+ red: string;
53
+ };
54
+ }
55
+
56
+ const useStyles = makeUseStyles((theme: MyTheme) => ({
57
+ root: {
58
+ color: theme.color.red,
59
+ // @ts-expect-error: 'blue' not exist in passed theme
60
+ borderColor: theme.color.blue,
61
+ },
62
+ }));
63
+
64
+ const {
65
+ root,
66
+ // @ts-expect-error: 'otherProps' not exist
67
+ otherProp,
68
+ } = useStyles();
69
+ }
@@ -0,0 +1,204 @@
1
+ import { Dimensions } from "react-native";
2
+ import {
3
+ maxHeight,
4
+ maxWidth,
5
+ minHeight,
6
+ minWidth,
7
+ aspectRatio,
8
+ minAspectRatio,
9
+ maxAspectRatio,
10
+ } from "../media-query";
11
+
12
+ const mockStyle = { color: "black" };
13
+
14
+ const mockGetDimensions = (dimensions: any) => {
15
+ Dimensions.get = () => dimensions;
16
+ };
17
+
18
+ describe("max*", () => {
19
+ describe("maxHeight", () => {
20
+ it("should return style when device height less than passed", () => {
21
+ const dimensions = { height: 319 };
22
+
23
+ mockGetDimensions(dimensions);
24
+
25
+ expect(maxHeight(320, mockStyle)).toEqual(mockStyle);
26
+ });
27
+
28
+ it("should return null when device height more than passed value", () => {
29
+ const dimensions = { height: 321 };
30
+
31
+ mockGetDimensions(dimensions);
32
+
33
+ expect(maxHeight(320, mockStyle)).toEqual(null);
34
+ });
35
+
36
+ it("should return null when device height equal a passed value", () => {
37
+ const dimensions = { height: 321 };
38
+
39
+ mockGetDimensions(dimensions);
40
+
41
+ expect(maxHeight(320, mockStyle)).toEqual(null);
42
+ });
43
+ });
44
+
45
+ describe("maxWidth", () => {
46
+ it("should return style when device width less than passed", () => {
47
+ const dimensions = { width: 319 };
48
+
49
+ mockGetDimensions(dimensions);
50
+
51
+ expect(maxWidth(320, mockStyle)).toEqual(mockStyle);
52
+ });
53
+
54
+ it("should return null when device width more than passed value", () => {
55
+ const dimensions = { width: 321 };
56
+
57
+ mockGetDimensions(dimensions);
58
+
59
+ expect(maxWidth(320, mockStyle)).toEqual(null);
60
+ });
61
+
62
+ it("should return null when device width equal a passed value", () => {
63
+ const dimensions = { width: 321 };
64
+
65
+ mockGetDimensions(dimensions);
66
+
67
+ expect(maxWidth(320, mockStyle)).toEqual(null);
68
+ });
69
+ });
70
+ });
71
+
72
+ describe("min*", () => {
73
+ describe("minHeight", () => {
74
+ it("should return null when device height less than passed", () => {
75
+ const dimensions = { height: 319 };
76
+
77
+ mockGetDimensions(dimensions);
78
+
79
+ expect(minHeight(320, mockStyle)).toEqual(null);
80
+ });
81
+
82
+ it("should return style when device height more than passed value", () => {
83
+ const dimensions = { height: 321 };
84
+
85
+ mockGetDimensions(dimensions);
86
+
87
+ expect(minHeight(320, mockStyle)).toEqual(mockStyle);
88
+ });
89
+
90
+ it("should return null when device height equal a passed value", () => {
91
+ const dimensions = { height: 319 };
92
+
93
+ mockGetDimensions(dimensions);
94
+
95
+ expect(minHeight(320, mockStyle)).toEqual(null);
96
+ });
97
+ });
98
+
99
+ describe("minWidth", () => {
100
+ it("should return null when device width less than passed", () => {
101
+ const dimensions = { width: 319 };
102
+
103
+ mockGetDimensions(dimensions);
104
+
105
+ expect(minWidth(320, mockStyle)).toEqual(null);
106
+ });
107
+
108
+ it("should return style when device width more than passed value", () => {
109
+ const dimensions = { width: 321 };
110
+
111
+ mockGetDimensions(dimensions);
112
+
113
+ expect(minWidth(320, mockStyle)).toEqual(mockStyle);
114
+ });
115
+
116
+ it("should return null when device width equal a passed value", () => {
117
+ const dimensions = { width: 319 };
118
+
119
+ mockGetDimensions(dimensions);
120
+
121
+ expect(minWidth(320, mockStyle)).toEqual(null);
122
+ });
123
+ });
124
+ });
125
+
126
+ describe("aspectRatio", () => {
127
+ describe("equal", () => {
128
+ it("should return null when device ratio less then passed", () => {
129
+ const dimensions = { width: 100, height: 201 };
130
+
131
+ mockGetDimensions(dimensions);
132
+
133
+ expect(aspectRatio(1 / 2, mockStyle)).toEqual(null);
134
+ });
135
+
136
+ it("should return style when ratio equal", () => {
137
+ const dimensions = { width: 100, height: 200 };
138
+
139
+ mockGetDimensions(dimensions);
140
+
141
+ expect(aspectRatio(1 / 2, mockStyle)).toEqual(mockStyle);
142
+ });
143
+
144
+ it("should return null when device ratio greater than passed", () => {
145
+ const dimensions = { width: 100, height: 199 };
146
+
147
+ mockGetDimensions(dimensions);
148
+
149
+ expect(aspectRatio(1 / 2, mockStyle)).toEqual(null);
150
+ });
151
+ });
152
+
153
+ describe("min", () => {
154
+ it("should return null when device ratio less then passed", () => {
155
+ const dimensions = { width: 100, height: 201 };
156
+
157
+ mockGetDimensions(dimensions);
158
+
159
+ expect(minAspectRatio(1 / 2, mockStyle)).toEqual(null);
160
+ });
161
+
162
+ it("should return style when ratio equal passed", () => {
163
+ const dimensions = { width: 100, height: 200 };
164
+
165
+ mockGetDimensions(dimensions);
166
+
167
+ expect(minAspectRatio(1 / 2, mockStyle)).toEqual(mockStyle);
168
+ });
169
+
170
+ it("should return style when ratio greater than passed", () => {
171
+ const dimensions = { width: 100, height: 199 };
172
+
173
+ mockGetDimensions(dimensions);
174
+
175
+ expect(minAspectRatio(1 / 2, mockStyle)).toEqual(mockStyle);
176
+ });
177
+ });
178
+
179
+ describe("max", () => {
180
+ it("should return style when device ratio less then passed", () => {
181
+ const dimensions = { width: 100, height: 201 };
182
+
183
+ mockGetDimensions(dimensions);
184
+
185
+ expect(maxAspectRatio(1 / 2, mockStyle)).toEqual(mockStyle);
186
+ });
187
+
188
+ it("should return style when ratio equal passed", () => {
189
+ const dimensions = { width: 100, height: 200 };
190
+
191
+ mockGetDimensions(dimensions);
192
+
193
+ expect(maxAspectRatio(1 / 2, mockStyle)).toEqual(mockStyle);
194
+ });
195
+
196
+ it("should return null when ratio greater than passed", () => {
197
+ const dimensions = { width: 100, height: 199 };
198
+
199
+ mockGetDimensions(dimensions);
200
+
201
+ expect(maxAspectRatio(1 / 2, mockStyle)).toEqual(null);
202
+ });
203
+ });
204
+ });
@@ -0,0 +1,61 @@
1
+ import { landscapeOrientation, portraitOrientation } from "../orientation";
2
+
3
+ import { Dimensions } from "react-native";
4
+
5
+ const mockStyle = { color: "black" };
6
+
7
+ const mockGetDimensions = (dimensions: any) => {
8
+ Dimensions.get = () => dimensions;
9
+ };
10
+
11
+ describe("landscapeOrientation", () => {
12
+ it("should return style when screen height less than width", () => {
13
+ const dimensions = { height: 1, width: 2 };
14
+
15
+ mockGetDimensions(dimensions);
16
+
17
+ expect(landscapeOrientation(mockStyle)).toEqual(mockStyle);
18
+ });
19
+
20
+ it("should return 'undefined' when screen height equal width", () => {
21
+ const dimensions = { height: 1, width: 1 };
22
+
23
+ mockGetDimensions(dimensions);
24
+
25
+ expect(landscapeOrientation(mockStyle)).toBeUndefined();
26
+ });
27
+
28
+ it("should return 'null' when screen height more than width", () => {
29
+ const dimensions = { height: 2, width: 1 };
30
+
31
+ mockGetDimensions(dimensions);
32
+
33
+ expect(landscapeOrientation(mockStyle)).toBeUndefined();
34
+ });
35
+ });
36
+
37
+ describe("portraitOrientation", () => {
38
+ it("should return 'undefined' when screen height less than width", () => {
39
+ const dimensions = { height: 1, width: 2 };
40
+
41
+ mockGetDimensions(dimensions);
42
+
43
+ expect(portraitOrientation(mockStyle)).toBeUndefined();
44
+ });
45
+
46
+ it("should return 'mockStyle' when screen height equal width", () => {
47
+ const dimensions = { height: 1, width: 1 };
48
+
49
+ mockGetDimensions(dimensions);
50
+
51
+ expect(portraitOrientation(mockStyle)).toEqual(mockStyle);
52
+ });
53
+
54
+ it("should return 'mockStyle' when screen height more than width", () => {
55
+ const dimensions = { height: 2, width: 1 };
56
+
57
+ mockGetDimensions(dimensions);
58
+
59
+ expect(portraitOrientation(mockStyle)).toEqual(mockStyle);
60
+ });
61
+ });
@@ -0,0 +1,26 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import { useTheme } from "../useTheme";
3
+ import { ThemeProvider } from "../context";
4
+
5
+ it("should throw an error when context value wasn't passed", () => {
6
+ expect(() => {
7
+ const { result } = renderHook(() => useTheme());
8
+
9
+ // trigger invoking passed hook
10
+ result.current;
11
+ }).toThrow();
12
+ });
13
+
14
+ it("should not throw an error", () => {
15
+ const theme = { test: "theme" };
16
+
17
+ expect(() => {
18
+ const { result } = renderHook(() => useTheme(), {
19
+ wrapper: ThemeProvider,
20
+ initialProps: { value: theme },
21
+ });
22
+
23
+ // trigger invoking passed hook
24
+ result.current;
25
+ }).not.toThrow();
26
+ });
@@ -0,0 +1,173 @@
1
+ import React, { FC, forwardRef } from "react";
2
+ import { TextInput } from "react-native";
3
+ import { InferInjectedStyledProps, withStyles } from "../withStyles";
4
+ import { makeUseStyles } from "../makeUseStyles";
5
+
6
+ {
7
+ const useStyles = makeUseStyles(() => ({
8
+ root: {
9
+ color: "red",
10
+ },
11
+ }));
12
+
13
+ interface Props extends InferInjectedStyledProps<typeof useStyles> {
14
+ a: boolean;
15
+ }
16
+
17
+ function Component(props: Props): JSX.Element | null {
18
+ props.styles.root.color = "";
19
+
20
+ // @ts-expect-error 'a' not exists
21
+ props.styles.a = {};
22
+
23
+ return null;
24
+ }
25
+
26
+ const ComponentWithStyles = withStyles(useStyles)(Component);
27
+
28
+ <ComponentWithStyles a={false} />;
29
+ <ComponentWithStyles
30
+ // @ts-expect-error: string is not a boolean
31
+ a={"not bool"}
32
+ />;
33
+
34
+ <ComponentWithStyles
35
+ a
36
+ // @ts-expect-error: already ibjected by hook
37
+ styles={{
38
+ root: {},
39
+ }}
40
+ />;
41
+ }
42
+
43
+ {
44
+ interface MyTheme {
45
+ colors: {
46
+ red: string;
47
+ };
48
+ }
49
+
50
+ const useStyles = makeUseStyles((theme: MyTheme) => ({
51
+ root: {
52
+ color: theme.colors.red,
53
+ // @ts-expect-error: 'gold' not exists
54
+ borderColor: theme.colors.gold,
55
+ },
56
+ }));
57
+
58
+ interface Props extends InferInjectedStyledProps<typeof useStyles> {
59
+ a: boolean;
60
+ }
61
+
62
+ const WithRef = forwardRef<TextInput, Props>(function Component(
63
+ props,
64
+ ref
65
+ ): JSX.Element | null {
66
+ props.styles.root.color = "";
67
+
68
+ // @ts-expect-error 'a' not exists
69
+ props.styles.a = {};
70
+
71
+ return <TextInput ref={ref} />;
72
+ });
73
+
74
+ const ComponentWithStylesAndRef = withStyles(useStyles)(WithRef);
75
+
76
+ <ComponentWithStylesAndRef a={false} />;
77
+
78
+ <ComponentWithStylesAndRef
79
+ a={false}
80
+ ref={(instance) => {
81
+ instance?.focus();
82
+
83
+ // @ts-expect-error 'abc' not exists
84
+ instance?.abc();
85
+ }}
86
+ />;
87
+ <ComponentWithStylesAndRef
88
+ // @ts-expect-error: string is not a boolean
89
+ a={"not bool"}
90
+ />;
91
+
92
+ <ComponentWithStylesAndRef
93
+ a
94
+ // @ts-expect-error: already ibjected by hook
95
+ styles={{
96
+ root: {},
97
+ }}
98
+ />;
99
+ }
100
+
101
+ {
102
+ interface MyTheme {
103
+ colors: {
104
+ red: string;
105
+ };
106
+ }
107
+
108
+ const useStyles = makeUseStyles((theme: MyTheme) => ({
109
+ root: {
110
+ color: theme.colors.red,
111
+ // @ts-expect-error: 'gold' not exists
112
+ borderColor: theme.colors.gold,
113
+ },
114
+ }));
115
+
116
+ interface Props extends InferInjectedStyledProps<typeof useStyles> {
117
+ a: boolean;
118
+ }
119
+
120
+ class MyCls extends React.Component<Props> {
121
+ focus() {}
122
+ render() {
123
+ return null;
124
+ }
125
+ }
126
+
127
+ const ComponentWithStylesAndRef = withStyles(useStyles)(MyCls);
128
+
129
+ <ComponentWithStylesAndRef a={false} />;
130
+
131
+ <ComponentWithStylesAndRef
132
+ a={false}
133
+ ref={(instance) => {
134
+ instance?.focus();
135
+
136
+ // @ts-expect-error 'abc' not exists
137
+ instance?.abc();
138
+ }}
139
+ />;
140
+ <ComponentWithStylesAndRef
141
+ // @ts-expect-error: string is not a boolean
142
+ a={"not bool"}
143
+ />;
144
+
145
+ <ComponentWithStylesAndRef
146
+ a
147
+ // @ts-expect-error: already ibjected by hook
148
+ styles={{
149
+ root: {},
150
+ }}
151
+ />;
152
+ }
153
+
154
+ {
155
+ const useStyles = makeUseStyles(() => ({
156
+ root: {
157
+ color: "red",
158
+ },
159
+ }));
160
+
161
+ interface Props extends InferInjectedStyledProps<typeof useStyles> {}
162
+
163
+ const MyCmp: FC<Props> = () => null;
164
+
165
+ const StyledMyCmp = withStyles(useStyles)(MyCmp);
166
+
167
+ <StyledMyCmp
168
+ ref={(e) => {
169
+ // @ts-expect-error: StyledMyCmp doesn't have 'ref' type
170
+ e?.aa();
171
+ }}
172
+ />;
173
+ }
@@ -0,0 +1 @@
1
+ export const UI_MODE_DEPENDENCY_KEY = "react-native-appearance";
@@ -0,0 +1,37 @@
1
+ import "./init";
2
+
3
+ import { Appearance } from "react-native";
4
+
5
+ import { onUse } from "../dependencyUsage";
6
+
7
+ import { UI_MODE_DEPENDENCY_KEY } from "./consts";
8
+
9
+ export type ColorSchemeName = "light" | "dark" | "default";
10
+
11
+ export function appearance<T>({
12
+ dark,
13
+ light,
14
+ default: defaultScheme,
15
+ }: { [mode in ColorSchemeName]?: T }): T | undefined {
16
+ onUse(UI_MODE_DEPENDENCY_KEY);
17
+
18
+ // Note: getColorScheme() will always return light when debugging with Chrome.
19
+ if (Appearance.getColorScheme() === "light") {
20
+ return light;
21
+ }
22
+
23
+ if (Appearance.getColorScheme() === "dark") {
24
+ return dark;
25
+ }
26
+
27
+ return defaultScheme;
28
+ }
29
+
30
+ export const darkAppearance = <T>(dark: T): T | undefined =>
31
+ appearance<T>({ dark });
32
+
33
+ export const lightAppearance = <T>(light: T): T | undefined =>
34
+ appearance<T>({ light });
35
+
36
+ export const noPreferenceAppearance = <T>(defaultScheme: T): T | undefined =>
37
+ appearance<T>({ default: defaultScheme });
@@ -0,0 +1,12 @@
1
+ import { Appearance } from "react-native";
2
+
3
+ import { addDependency } from "../dependencyRegistry";
4
+ import { createEventEmitter } from "../createEventEmitter";
5
+
6
+ import { UI_MODE_DEPENDENCY_KEY } from "./consts";
7
+
8
+ const { on, emit } = createEventEmitter(UI_MODE_DEPENDENCY_KEY);
9
+
10
+ addDependency(UI_MODE_DEPENDENCY_KEY, (handler: () => void) => on(handler));
11
+
12
+ Appearance.addChangeListener(emit);
package/src/context.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { createContext } from "react";
2
+ import { DefaultTheme } from "./DefaultTheme";
3
+
4
+ export const themeContext = createContext<DefaultTheme | null>(null);
5
+
6
+ export const {
7
+ Provider: ThemeProvider,
8
+ Consumer: ThemeConsumer,
9
+ } = themeContext;
@@ -0,0 +1,26 @@
1
+ type Handler = () => void;
2
+ type UnSubscribe = () => void;
3
+
4
+ interface EventsRegistry {
5
+ [eventName: string]: Array<Handler>;
6
+ }
7
+
8
+ interface Result {
9
+ on: (callback: Handler) => UnSubscribe;
10
+ emit: () => void;
11
+ }
12
+
13
+ const events: EventsRegistry = {};
14
+
15
+ export function createEventEmitter(event: string): Result {
16
+ const emit = (): void => (events[event] || []).forEach((fn) => fn());
17
+ const on = (cb: Handler): UnSubscribe => {
18
+ (events[event] = events[event] || []).push(cb);
19
+
20
+ return () => {
21
+ events[event] = events[event]!.filter((fn) => fn !== cb);
22
+ };
23
+ };
24
+
25
+ return { on, emit };
26
+ }
@@ -0,0 +1 @@
1
+ export const UI_MODE_DEPENDENCY_KEY = "react-native-dark-mode";
@@ -0,0 +1,29 @@
1
+ import "./init";
2
+
3
+ import { onUse } from "../dependencyUsage";
4
+
5
+ import { UI_MODE_DEPENDENCY_KEY } from "./consts";
6
+ import { state } from "./state";
7
+
8
+ export type UiType = "dark" | "light";
9
+
10
+ export function uiMode<T>({
11
+ dark,
12
+ light,
13
+ }: { [mode in UiType]?: T }): T | undefined {
14
+ onUse(UI_MODE_DEPENDENCY_KEY);
15
+
16
+ if (state.mode === "dark") {
17
+ return dark;
18
+ }
19
+
20
+ if (state.mode === "light") {
21
+ return light;
22
+ }
23
+
24
+ return undefined;
25
+ }
26
+
27
+ export const darkUiMode = <T>(dark: T): T | undefined => uiMode<T>({ dark });
28
+
29
+ export const lightUiMode = <T>(light: T): T | undefined => uiMode<T>({ light });
@@ -0,0 +1,19 @@
1
+ import { eventEmitter, Mode } from "react-native-dark-mode";
2
+
3
+ import { addDependency } from "../dependencyRegistry";
4
+ import { createEventEmitter } from "../createEventEmitter";
5
+
6
+ import { UI_MODE_DEPENDENCY_KEY } from "./consts";
7
+ import { state } from "./state";
8
+
9
+ const { on, emit } = createEventEmitter(UI_MODE_DEPENDENCY_KEY);
10
+
11
+ addDependency(UI_MODE_DEPENDENCY_KEY, (handler: () => void) => on(handler));
12
+
13
+ eventEmitter.on("currentModeChanged", (newMode: Mode) => {
14
+ if (state.mode !== newMode) {
15
+ state.mode = newMode;
16
+
17
+ emit();
18
+ }
19
+ });
@@ -0,0 +1,5 @@
1
+ import { initialMode } from "react-native-dark-mode";
2
+
3
+ export const state = {
4
+ mode: initialMode,
5
+ };